From afecf29356e0b9df0dd80bb389140e6dbab9f98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Hatcher?= Date: Wed, 13 Mar 2024 07:49:05 +0100 Subject: [PATCH] UPDATE: Adds bincode to serializer/deserializer --- Cargo.lock | 27 ++++++++++++++++ base/Cargo.toml | 1 + base/src/expressions/token.rs | 3 +- base/src/model.rs | 7 +++++ base/src/types.rs | 59 ++++++++++++++++++----------------- xlsx/Cargo.toml | 6 ++++ xlsx/src/bin/bench.rs | 59 +++++++++++++++++++++++++++++++++++ 7 files changed, 132 insertions(+), 30 deletions(-) create mode 100644 xlsx/src/bin/bench.rs diff --git a/Cargo.lock b/Cargo.lock index 850add1..a840f01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,25 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bincode" +version = "2.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" +dependencies = [ + "bincode_derive", + "serde", +] + +[[package]] +name = "bincode_derive" +version = "2.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c" +dependencies = [ + "virtue", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -191,6 +210,7 @@ dependencies = [ name = "ironcalc" version = "0.1.3" dependencies = [ + "bincode", "chrono", "ironcalc_base", "itertools", @@ -206,6 +226,7 @@ dependencies = [ name = "ironcalc_base" version = "0.1.2" dependencies = [ + "bincode", "chrono", "chrono-tz", "js-sys", @@ -541,6 +562,12 @@ dependencies = [ "serde", ] +[[package]] +name = "virtue" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" diff --git a/base/Cargo.toml b/base/Cargo.toml index 27c11c4..a76438a 100644 --- a/base/Cargo.toml +++ b/base/Cargo.toml @@ -19,6 +19,7 @@ chrono = "0.4" chrono-tz = "0.7.0" regex = "1.0" once_cell = "1.16.0" +bincode = "=2.0.0-rc.3" [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = { version = "0.3.60" } diff --git a/base/src/expressions/token.rs b/base/src/expressions/token.rs index ef6abe6..02af88b 100644 --- a/base/src/expressions/token.rs +++ b/base/src/expressions/token.rs @@ -1,5 +1,6 @@ use std::fmt; +use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -80,7 +81,7 @@ impl fmt::Display for OpProduct { /// * "#ERROR!" means there was an error processing the formula (for instance "=A1+") /// * "#N/IMPL!" means the formula or feature in Excel but has not been implemented in IronCalc /// Note that they are serialized/deserialized by index -#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize_repr, Deserialize_repr, Decode, Encode, Debug, PartialEq, Eq, Clone)] #[repr(u8)] pub enum Error { REF, diff --git a/base/src/model.rs b/base/src/model.rs index c04872c..82db308 100644 --- a/base/src/model.rs +++ b/base/src/model.rs @@ -7,6 +7,7 @@ //! IronCalc is row first. A cell is referenced by (`sheet`, `row`, `column`) //! +use bincode::config; use serde_json::json; use std::collections::HashMap; @@ -1718,6 +1719,12 @@ impl Model { } } + /// bin + pub fn to_binary_str(&self) -> Vec { + let config = config::standard(); + bincode::encode_to_vec(&self.workbook, config).expect("") + } + /// Returns markup representation of the given `sheet`. pub fn sheet_markup(&self, sheet: u32) -> Result { let worksheet = self.workbook.worksheet(sheet)?; diff --git a/base/src/types.rs b/base/src/types.rs index 914351b..904cd35 100644 --- a/base/src/types.rs +++ b/base/src/types.rs @@ -1,3 +1,4 @@ +use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt::Display}; @@ -33,7 +34,7 @@ fn hashmap_is_empty(h: &HashMap) -> bool { h.values().len() == 0 } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct Metadata { pub application: String, pub app_version: String, @@ -43,13 +44,13 @@ pub struct Metadata { pub last_modified: String, //"2020-11-20T16:24:35" } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct WorkbookSettings { pub tz: String, pub locale: String, } /// An internal representation of an IronCalc Workbook -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Clone)] #[serde(deny_unknown_fields)] pub struct Workbook { pub shared_strings: Vec, @@ -65,7 +66,7 @@ pub struct Workbook { } /// A defined name. The `sheet_id` is the sheet index in case the name is local -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct DefinedName { pub name: String, pub formula: String, @@ -79,7 +80,7 @@ pub struct DefinedName { /// * state: /// 18.18.68 ST_SheetState (Sheet Visibility Types) /// hidden, veryHidden, visible -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "lowercase")] pub enum SheetState { Visible, @@ -98,7 +99,7 @@ impl Display for SheetState { } /// Internal representation of a worksheet Excel object -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Clone)] pub struct Worksheet { pub dimension: String, pub cols: Vec, @@ -125,7 +126,7 @@ pub struct Worksheet { pub type SheetData = HashMap>; // ECMA-376-1:2016 section 18.3.1.73 -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Clone)] pub struct Row { /// Row index pub r: i32, @@ -139,7 +140,7 @@ pub struct Row { } // ECMA-376-1:2016 section 18.3.1.13 -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Clone)] pub struct Col { // Column definitions are defined on ranges, unlike rows which store unique, per-row entries. /// First column affected by this record. Settings apply to column in \[min, max\] range. @@ -164,7 +165,7 @@ pub enum CellType { CompoundData = 128, } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, Clone, PartialEq)] #[serde(tag = "t", deny_unknown_fields)] pub enum Cell { #[serde(rename = "empty")] @@ -208,7 +209,7 @@ impl Default for Cell { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct Comment { pub text: String, pub author_name: String, @@ -218,7 +219,7 @@ pub struct Comment { } // ECMA-376-1:2016 section 18.5.1.2 -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct Table { pub name: String, pub display_name: String, @@ -241,7 +242,7 @@ pub struct Table { // totals_row_label vs totals_row_function might be mutually exclusive. Use an enum? // the totals_row_function is an enum not String methinks -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct TableColumn { pub id: u32, pub name: String, @@ -271,7 +272,7 @@ impl Default for TableColumn { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone, Default)] pub struct TableStyleInfo { #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, @@ -289,7 +290,7 @@ pub struct TableStyleInfo { pub show_column_stripes: bool, } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct Styles { pub num_fmts: Vec, pub fonts: Vec, @@ -314,7 +315,7 @@ impl Default for Styles { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct Style { pub alignment: Option, pub num_fmt: String, @@ -324,7 +325,7 @@ pub struct Style { pub quote_prefix: bool, } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct NumFmt { pub num_fmt_id: i32, pub format_code: String, @@ -342,7 +343,7 @@ impl Default for NumFmt { // ST_FontScheme simple type (ยง18.18.33). // Usually major fonts are used for styles like headings, // and minor fonts are used for body and paragraph text. -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "lowercase")] #[derive(Default)] pub enum FontScheme { @@ -362,7 +363,7 @@ impl Display for FontScheme { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct Font { #[serde(default = "default_as_false")] #[serde(skip_serializing_if = "is_false")] @@ -405,7 +406,7 @@ impl Default for Font { } // TODO: Maybe use an enum for the pattern_type values here? -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct Fill { pub pattern_type: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -424,7 +425,7 @@ impl Default for Fill { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "lowercase")] pub enum HorizontalAlignment { Center, @@ -466,7 +467,7 @@ impl Display for HorizontalAlignment { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "lowercase")] pub enum VerticalAlignment { Bottom, @@ -501,7 +502,7 @@ impl Display for VerticalAlignment { } // 1762 -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone, Default)] pub struct Alignment { #[serde(default)] #[serde(skip_serializing_if = "HorizontalAlignment::is_default")] @@ -514,7 +515,7 @@ pub struct Alignment { pub wrap_text: bool, } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct CellStyleXfs { pub num_fmt_id: i32, pub font_id: i32, @@ -557,7 +558,7 @@ impl Default for CellStyleXfs { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone, Default)] pub struct CellXfs { pub xf_id: i32, pub num_fmt_id: i32, @@ -589,7 +590,7 @@ pub struct CellXfs { pub alignment: Option, } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct CellStyles { pub name: String, pub xf_id: i32, @@ -606,7 +607,7 @@ impl Default for CellStyles { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] #[serde(rename_all = "lowercase")] pub enum BorderStyle { Thin, @@ -636,13 +637,13 @@ impl Display for BorderStyle { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone)] pub struct BorderItem { pub style: BorderStyle, pub color: Option, } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq, Eq, Clone, Default)] pub struct Border { #[serde(default = "default_as_false")] #[serde(skip_serializing_if = "is_false")] @@ -664,7 +665,7 @@ pub struct Border { /// Information need to show a sheet tab in the UI /// The color is serialized only if it is not Color::None -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Serialize, Deserialize, Decode, Encode, Debug, PartialEq)] pub struct SheetInfo { pub name: String, pub state: String, diff --git a/xlsx/Cargo.toml b/xlsx/Cargo.toml index 5a832c0..b1e9547 100644 --- a/xlsx/Cargo.toml +++ b/xlsx/Cargo.toml @@ -23,6 +23,7 @@ thiserror = "1.0" ironcalc_base = { path = "../base", version = "0.1.0" } itertools = "0.10.5" chrono = "0.4" +bincode = "=2.0.0-rc.3" [dev-dependencies] uuid = { version = "1.2.2", features = ["serde", "v4"] } @@ -34,3 +35,8 @@ path = "src/lib.rs" [[bin]] name = "test" path = "src/bin/test.rs" + +[[bin]] +name = "bench" +path = "src/bin/bench.rs" + diff --git a/xlsx/src/bin/bench.rs b/xlsx/src/bin/bench.rs new file mode 100644 index 0000000..e37ceba --- /dev/null +++ b/xlsx/src/bin/bench.rs @@ -0,0 +1,59 @@ +use std::{fs, io::Write, time::Instant}; + +use bincode::config; +use ironcalc::import::load_model_from_xlsx; +use ironcalc_base::{types::Workbook, model::Model}; + + +fn main() { + let args: Vec<_> = std::env::args().collect(); + if args.len() != 2 { + panic!("Usage: {} ", args[0]); + } + let file_name = &args[1]; + let now = Instant::now(); + let mut model = load_model_from_xlsx(file_name, "en", "UTC").unwrap(); + let elapsed_time = now.elapsed(); + println!("Loaded model from xlsx: {:?}", elapsed_time); + model.evaluate(); + let now = Instant::now(); + let s = model.to_json_str(); + let elapsed_time = now.elapsed(); + println!("Stringify json: {:?}", elapsed_time); + { + let now = Instant::now(); + let decoded : Workbook = serde_json::from_str(&s).unwrap(); + let elapsed_time = now.elapsed(); + println!("Parse from json: {:?} and name {}", elapsed_time, decoded.name); + } + let file_name_json = format!("{}.json", file_name); + let file_path = std::path::Path::new(&file_name_json); + let mut file = fs::File::create(file_path).unwrap(); + file.write_all(s.as_bytes()).unwrap(); + let now = Instant::now(); + let s = model.to_binary_str(); + let elapsed_time = now.elapsed(); + println!("stringify to binary: {:?}", elapsed_time); + { + let config = config::standard(); + let now = Instant::now(); + let (decoded, _): (Workbook, usize) = bincode::decode_from_slice(&s[..], config).unwrap(); + let elapsed_time = now.elapsed(); + println!("Parse from binary: {:?} and {}", elapsed_time, decoded.name); + } + let file_name_binary = format!("{}.binary", file_name); + let file_path = std::path::Path::new(&file_name_binary); + let mut file = fs::File::create(file_path).unwrap(); + file.write_all(&s).unwrap(); + { + let config = config::standard(); + let now = Instant::now(); + let s = &fs::read(file_name_binary).unwrap(); + let (decoded, _): (Workbook, usize) = bincode::decode_from_slice(&s[..], config).unwrap(); + let model = Model::from_workbook(decoded).unwrap(); + let elapsed_time = now.elapsed(); + println!("Loaded from binary file: {:?} and {}", elapsed_time, model.workbook.name); + + } +} +