diff --git a/Cargo.lock b/Cargo.lock index 13764bdd1c..e3fa899dac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1465,6 +1465,26 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "paste" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste-impl" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "percent-encoding" version = "1.0.1" @@ -1508,6 +1528,16 @@ dependencies = [ "treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.4.30" @@ -1900,6 +1930,7 @@ dependencies = [ "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "leb128 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2754,6 +2785,8 @@ dependencies = [ "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" +"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" @@ -2761,6 +2794,7 @@ dependencies = [ "checksum predicates 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53e09015b0d3f5a0ec2d4428f7559bb7b3fff341b4e159fedd1d57fac8b939ff" "checksum predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" "checksum predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" +"checksum proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc" "checksum publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9bf259a81de2b2eb9850ec990ec78e6a25319715584fd7652b9b26f96fcb1510" diff --git a/lib/Cargo.lock b/lib/Cargo.lock new file mode 100644 index 0000000000..20ed84d43c --- /dev/null +++ b/lib/Cargo.lock @@ -0,0 +1,250 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "assert_cmd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "escargot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dfx_derive" +version = "0.1.0" +dependencies = [ + "proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dfx_info" +version = "0.0.0" +dependencies = [ + "dfx_derive 0.1.0", +] + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "escargot" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "itoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "leb128" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste-impl" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "predicates" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "predicates-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "predicates-tree" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_bytes" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_idl" +version = "0.1.0" +dependencies = [ + "assert_cmd 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dfx_info 0.0.0", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "leb128 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "treeline" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum assert_cmd 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2dc477793bd82ec39799b6f6b3df64938532fdf2ab0d49ef817eac65856a5a1e" +"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +"checksum escargot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ceb9adbf9874d5d028b5e4c5739d22b71988252b25c9c98fe7cf9738bee84597" +"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum leb128 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" +"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" +"checksum predicates 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53e09015b0d3f5a0ec2d4428f7559bb7b3fff341b4e159fedd1d57fac8b939ff" +"checksum predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" +"checksum predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" +"checksum proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8" +"checksum proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "175a40b9cf564ce9bf050654633dbf339978706b8ead1a907bb970b63185dd95" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +"checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f" +"checksum serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "45af0182ff64abaeea290235eb67da3825a576c5d53e642c4d5b652e12e6effc" +"checksum serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425" +"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/lib/README.adoc b/lib/README.adoc index 90ee193392..5ab0060340 100644 --- a/lib/README.adoc +++ b/lib/README.adoc @@ -1,12 +1,14 @@ = IDL Serialization library in Rust == Using the library -* Import +* Serialization [source,rust] #[macro_use] extern crate serde_idl; -extern crate dfx_info; -use dfx_info::{IDLType}; +IDL!(&42, &Some(42), &[1,2,3]) +// IDL! macro expands to: +IDLBuilder::new().arg(&42).arg(&Some(42)).arg(&[1,2,3]).to_vec().unwrap(), + * Derive serialization trait and inspect IDL type [source,rust] @@ -19,19 +21,9 @@ assert_eq!(serde_idl::get_type(&list), field("tail", Type::Opt(Box::new( Type::Knot(TypeId::of::()))))]) ); - -* Serialize single argument -[source,rust] -assert_eq!(serde_idl::to_vec(list).unwrap(), +assert_eq!(IDL!(&list), hex::decode("4449444c026c02a0d2aca8047c90eddae704016e00002a00").unwrap()); -* Serialize multiple arguments -[source,rust] -assert_eq!( - IDLBuilder::new().arg(&42).arg(&Some(42)).arg(&[1,2,3]).to_vec().unwrap(), - IDL!(&42, &Some(42), &[1,2,3]) -); - == Why not using serde We end up not using `serde`, since `serde` cannot derive type information, and we cannot easily sort the field index in `struct` and `enum` by the `idl_hash`. diff --git a/lib/serde_idl/Cargo.toml b/lib/serde_idl/Cargo.toml index 1f1d6bbb42..0906bca80c 100644 --- a/lib/serde_idl/Cargo.toml +++ b/lib/serde_idl/Cargo.toml @@ -9,6 +9,7 @@ leb128 = "0.2.4" log = "0.4" serde = "1.0" serde_bytes = "0.11" +paste = "0.1" dfx_info = { path = "../dfx_info" } [dev-dependencies] diff --git a/lib/serde_idl/src/de.rs b/lib/serde_idl/src/de.rs new file mode 100644 index 0000000000..9fa58a6404 --- /dev/null +++ b/lib/serde_idl/src/de.rs @@ -0,0 +1,484 @@ +extern crate paste; + +use super::error::{Error, Result}; +use super::idl_hash; +use serde::de::{self, DeserializeOwned, Visitor}; +use std::collections::{BTreeMap, VecDeque}; +use std::io::Read; + +use leb128::read::{signed as sleb128_decode, unsigned as leb128_decode}; + +pub fn from_bytes(bytes: &[u8]) -> Result +where + T: DeserializeOwned, +{ + let mut deserializer = Deserializer::from_bytes(bytes); + deserializer.parse_table()?; + let t = T::deserialize(&mut deserializer)?; + if deserializer.input.is_empty() + && deserializer.current_type.is_empty() + && deserializer.field_index.is_none() + { + Ok(t) + } else { + Err(Error::Message(format!( + "Trailing bytes: {:x?}, types: {:?}", + deserializer.input, deserializer.current_type + ))) + } +} + +#[derive(Clone, Debug)] +enum RawValue { + I(i64), + U(u64), +} +impl RawValue { + fn get_i64(&self) -> Result { + match *self { + RawValue::I(i) => Ok(i), + _ => Err(Error::Message("get_i64 fail".to_string())), + } + } + fn get_u64(&self) -> Result { + match *self { + RawValue::U(u) => Ok(u), + _ => Err(Error::Message("get_u64 fail".to_string())), + } + } +} + +pub struct Deserializer<'de> { + input: &'de [u8], + table: Vec>, + types: Vec, + current_type: VecDeque, + field_index: Option<&'static str>, +} + +impl<'de> Deserializer<'de> { + pub fn from_bytes(input: &'de [u8]) -> Self { + Deserializer { + input, + table: Vec::new(), + types: Vec::new(), + // TODO consider borrowing + current_type: VecDeque::new(), + field_index: None, + } + } + fn leb128_read(&mut self) -> Result { + Ok(leb128_decode(&mut self.input).expect("Should read unsigned number")) + } + fn sleb128_read(&mut self) -> Result { + Ok(sleb128_decode(&mut self.input).expect("Should read signed number")) + } + fn parse_string(&mut self, len: usize) -> Result { + let mut buf = Vec::new(); + buf.resize(len, 0); + self.input.read_exact(&mut buf)?; + Ok(String::from_utf8(buf).unwrap()) + } + fn parse_char(&mut self) -> Result { + let mut buf = [0u8; 1]; + self.input.read_exact(&mut buf)?; + Ok(buf[0]) + } + fn parse_magic(&mut self) -> Result<()> { + let magic = self.parse_string(4)?; + if magic == "DIDL" { + Ok(()) + } else { + Err(Error::Message(format!("wrong magic number {}", magic))) + } + } + + fn parse_table(&mut self) -> Result<()> { + self.parse_magic()?; + let len = self.leb128_read()?; + for _i in 0..len { + let mut buf = Vec::new(); + let ty = self.sleb128_read()?; + buf.push(RawValue::I(ty)); + match ty { + -18 | -19 => { + // opt, vec + buf.push(RawValue::I(self.sleb128_read()?)); + } + -20 | -21 => { + //record, variant + let obj_len = self.leb128_read()?; + buf.push(RawValue::U(obj_len)); + for _ in 0..obj_len { + buf.push(RawValue::U(self.leb128_read()?)); + buf.push(RawValue::I(self.sleb128_read()?)); + } + } + _ => return Err(Error::Message(format!("Unknown op_code {}", ty))), + }; + self.table.push(buf); + } + println!("{:?}", self.table); + let len = self.leb128_read()?; + for _i in 0..len { + let ty = self.sleb128_read()?; + self.types.push(RawValue::I(ty)); + } + self.current_type.push_back(self.types[0].clone()); + println!("{:?}", self.types); + Ok(()) + } + + fn parse_type(&mut self) -> Result { + let op = self.current_type.pop_front().unwrap().get_i64()?; + if op >= 0 { + self.current_type.pop_front(); + let ty = &self.table[op as usize]; + for x in ty.iter().rev() { + self.current_type.push_front(x.clone()); + } + self.parse_type() + } else { + Ok(op) + } + } +} + +macro_rules! primitive_impl { + ($ty:ident, $opcode:literal, $method:ident $($cast:tt)*) => { + paste::item! { + fn [](self, visitor: V) -> Result + where V: Visitor<'de> { + assert_eq!(self.parse_type().unwrap(), $opcode); + visitor.[](self.$method()? $($cast)*) + } + } + }; +} + +impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { + type Error = Error; + fn deserialize_any(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + unimplemented!() + } + + primitive_impl!(i8, -4, sleb128_read as i8); + primitive_impl!(i16, -4, sleb128_read as i16); + primitive_impl!(i32, -4, sleb128_read as i32); + primitive_impl!(i64, -4, sleb128_read); + primitive_impl!(u8, -3, leb128_read as u8); + primitive_impl!(u16, -3, leb128_read as u16); + primitive_impl!(u32, -3, leb128_read as u32); + primitive_impl!(u64, -3, leb128_read); + primitive_impl!(bool, -2, parse_char == 1u8); + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + assert_eq!(self.parse_type().unwrap(), -15); + let len = self.leb128_read()? as usize; + let value = self.parse_string(len)?; + visitor.visit_string(value) + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + assert_eq!(self.parse_type().unwrap(), -15); + let len = self.leb128_read()? as usize; + let value = std::str::from_utf8(&self.input[0..len]).unwrap(); + self.input = &self.input[len..]; + visitor.visit_str(value) + } + + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + assert_eq!(self.parse_type().unwrap(), -18); + let bit = self.parse_char()?; + if bit == 0u8 { + //self.parse_type() cannot be used as it will expand the type, which has no value + self.current_type.pop_front(); + visitor.visit_none() + } else { + visitor.visit_some(self) + } + } + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + assert_eq!(self.parse_type().unwrap(), -1); + visitor.visit_unit() + } + fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_unit(visitor) + } + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + fn deserialize_seq(mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.parse_type().unwrap() { + -19 => { + let len = self.leb128_read()? as u32; + let value = visitor.visit_seq(Compound::new(&mut self, Style::Vector { len })); + self.current_type.pop_front(); + value + } + -20 => { + let len = self.current_type.pop_front().unwrap().get_u64()? as u32; + visitor.visit_seq(Compound::new(&mut self, Style::Tuple { len, index: 0 })) + } + _ => Err(Error::Message("seq only takes vector or tuple".to_string())), + } + } + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + fn deserialize_struct( + mut self, + _name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + assert_eq!(self.parse_type().unwrap(), -20); + let len = self.current_type.pop_front().unwrap().get_u64()? as u32; + let mut fs = BTreeMap::new(); + for s in fields.iter() { + assert_eq!(fs.insert(idl_hash(s), *s), None); + } + let value = visitor.visit_map(Compound::new(&mut self, Style::Struct { len, fs }))?; + Ok(value) + } + + fn deserialize_enum( + mut self, + _name: &'static str, + variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + assert_eq!(self.parse_type().unwrap(), -21); + let len = self.current_type.pop_front().unwrap().get_u64()? as u32; + let mut fs = BTreeMap::new(); + for s in variants.iter() { + assert_eq!(fs.insert(idl_hash(s), *s), None); + } + let value = visitor.visit_enum(Compound::new(&mut self, Style::Enum { len, fs }))?; + Ok(value) + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if self.field_index.is_none() { + return Err(Error::Message("empty field_name".to_string())); + } + let v = visitor.visit_str(self.field_index.unwrap()); + self.field_index = None; + v + } + + serde::forward_to_deserialize_any! { + char bytes byte_buf ignored_any f32 f64 map + } +} + +#[derive(Debug)] +enum Style { + Tuple { + len: u32, + index: u32, + }, + Vector { + len: u32, + }, + Struct { + len: u32, + fs: BTreeMap, + }, + Enum { + len: u32, + fs: BTreeMap, + }, +} + +struct Compound<'a, 'de> { + de: &'a mut Deserializer<'de>, + style: Style, +} + +impl<'a, 'de> Compound<'a, 'de> { + fn new(de: &'a mut Deserializer<'de>, style: Style) -> Self { + Compound { de, style } + } +} + +impl<'de, 'a> de::SeqAccess<'de> for Compound<'a, 'de> { + type Error = Error; + + fn next_element_seed(&mut self, seed: T) -> Result> + where + T: de::DeserializeSeed<'de>, + { + match self.style { + Style::Tuple { + ref len, + ref mut index, + } => { + if *index == *len { + return Ok(None); + } + let t_idx = self.de.current_type.pop_front().unwrap().get_u64()? as u32; + assert_eq!(t_idx, *index); + *index += 1; + seed.deserialize(&mut *self.de).map(Some) + } + Style::Vector { ref mut len } => { + if *len == 0 { + return Ok(None); + } + let ty = self.de.current_type.front().unwrap().clone(); + self.de.current_type.push_back(ty); + *len -= 1; + seed.deserialize(&mut *self.de).map(Some) + } + _ => Err(Error::Message("expect tuple".to_string())), + } + } +} + +impl<'de, 'a> de::MapAccess<'de> for Compound<'a, 'de> { + type Error = Error; + fn next_key_seed(&mut self, seed: K) -> Result> + where + K: de::DeserializeSeed<'de>, + { + match self.style { + Style::Struct { + ref mut len, + ref fs, + } => { + if *len == 0 { + return Ok(None); + } + *len -= 1; + let hash = self.de.current_type.pop_front().unwrap().get_u64()? as u32; + if self.de.field_index.is_some() { + return Err(Error::Message("field_name already taken".to_string())); + } + self.de.field_index = Some(fs[&hash]); + seed.deserialize(&mut *self.de).map(Some) + } + _ => Err(Error::Message("expect struct".to_string())), + } + } + fn next_value_seed(&mut self, seed: V) -> Result + where + V: de::DeserializeSeed<'de>, + { + seed.deserialize(&mut *self.de) + } +} + +impl<'de, 'a> de::EnumAccess<'de> for Compound<'a, 'de> { + type Error = Error; + type Variant = Self; + + fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> + where + V: de::DeserializeSeed<'de>, + { + match self.style { + Style::Enum { len, ref fs } => { + let index = self.de.leb128_read()? as u32; + if index >= len { + return Err(Error::Message(format!( + "variant index {} larger than length {}", + index, len + ))); + } + for i in 0..len { + let hash = self.de.current_type.pop_front().unwrap().get_u64()? as u32; + let ty = self.de.current_type.pop_front().unwrap(); + if i == index { + if self.de.field_index.is_some() { + return Err(Error::Message("field_index already taken".to_string())); + } + self.de.field_index = Some(fs[&hash]); + // After we skip all the fields, ty will be the only thing left + self.de.current_type.push_back(ty); + } + } + let val = seed.deserialize(&mut *self.de)?; + Ok((val, self)) + } + _ => Err(Error::Message("expect enum".to_string())), + } + } +} + +impl<'de, 'a> de::VariantAccess<'de> for Compound<'a, 'de> { + type Error = Error; + + fn unit_variant(self) -> Result<()> { + assert_eq!(self.de.parse_type()?, -1); + Ok(()) + } + + fn newtype_variant_seed(self, seed: T) -> Result + where + T: de::DeserializeSeed<'de>, + { + seed.deserialize(self.de) + } + + fn tuple_variant(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + de::Deserializer::deserialize_seq(self.de, visitor) + } + + fn struct_variant(self, fields: &'static [&'static str], visitor: V) -> Result + where + V: Visitor<'de>, + { + de::Deserializer::deserialize_struct(self.de, "_", fields, visitor) + } +} diff --git a/lib/serde_idl/src/error.rs b/lib/serde_idl/src/error.rs index 87510cde19..0d464fb125 100644 --- a/lib/serde_idl/src/error.rs +++ b/lib/serde_idl/src/error.rs @@ -1,86 +1,47 @@ -use std::fmt; -use std::result; -//use serde::de; -use serde::ser; -use std::error; +use serde::{de, ser}; + +use std::fmt::{self, Display}; use std::io; -/// This type represents all possible errors that can occur when serializing or deserializing CBOR -/// data. -pub struct Error(ErrorImpl); +pub type Result = std::result::Result; -/// Alias for a `Result` with the error type `serde_cbor::Error`. -pub type Result = result::Result; +#[derive(Debug)] +pub enum Error { + Message(String), + Eof, + TrailingCharacters, +} -impl Error { - pub(crate) fn io(error: io::Error) -> Error { - Error(ErrorImpl { - code: ErrorCode::Io(error), - offset: 0, - }) - } - pub(crate) fn message(_msg: T) -> Error { - Error(ErrorImpl { - code: ErrorCode::Message(_msg.to_string()), - offset: 0, - }) +impl ser::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) } } -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.0.code { - ErrorCode::Io(ref err) => Some(err), - _ => None, - } +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) } } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.0.offset == 0 { - fmt::Display::fmt(&self.0.code, f) - } else { - write!(f, "{} at offset {}", self.0.code, self.0.offset) - } +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(std::error::Error::description(self)) } } -impl fmt::Debug for Error { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, fmt) +impl std::error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::Message(ref msg) => msg, + Error::Eof => "unexpected end of input", + Error::TrailingCharacters => "trailing characters", + } } } impl From for Error { fn from(e: io::Error) -> Error { - Error::io(e) - } -} - -impl ser::Error for Error { - fn custom(msg: T) -> Error { - Error::message(msg) - } -} - -#[derive(Debug)] -struct ErrorImpl { - code: ErrorCode, - offset: u64, -} - -#[derive(Debug)] -pub(crate) enum ErrorCode { - Message(String), - Io(io::Error), -} - -impl fmt::Display for ErrorCode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ErrorCode::Message(ref msg) => f.write_str(msg), - ErrorCode::Io(ref err) => fmt::Display::fmt(err, f), - } + Error::Message(format!("io error {}", e)) } } diff --git a/lib/serde_idl/src/lib.rs b/lib/serde_idl/src/lib.rs index d272e4069b..2f58c7a63d 100644 --- a/lib/serde_idl/src/lib.rs +++ b/lib/serde_idl/src/lib.rs @@ -6,13 +6,10 @@ extern crate serde; // Re-export the [items recommended by serde](https://serde.rs/conventions.html). //#[doc(inline)] -//pub use crate::de::{from_str, Deserializer}; -#[doc(inline)] +pub use crate::de::{from_bytes, Deserializer}; pub use crate::error::{Error, Result}; -#[doc(inline)] -pub use crate::ser::to_vec; -//pub mod de; +pub mod de; pub mod error; #[macro_use] pub mod ser; @@ -25,3 +22,11 @@ macro_rules! IDL { idl.to_vec().unwrap() }} } + +pub fn idl_hash(id: &str) -> u32 { + let mut s: u32 = 0; + for c in id.chars() { + s = s.wrapping_mul(223).wrapping_add(c as u32); + } + s +} diff --git a/lib/serde_idl/src/ser.rs b/lib/serde_idl/src/ser.rs index 586dafb59d..cd18946741 100644 --- a/lib/serde_idl/src/ser.rs +++ b/lib/serde_idl/src/ser.rs @@ -45,14 +45,6 @@ impl IDLBuilder { } } -/// Serializes a value to a vector. -pub fn to_vec(value: &T) -> Result> -where - T: dfx_info::IDLType, -{ - IDLBuilder::new().arg(value).serialize_to_vec() -} - /// A structure for serializing Rust values to IDL. #[derive(Clone, Debug, Default)] pub struct ValueSerializer { diff --git a/lib/serde_idl/tests/both.rs b/lib/serde_idl/tests/both.rs new file mode 100644 index 0000000000..a25d61281b --- /dev/null +++ b/lib/serde_idl/tests/both.rs @@ -0,0 +1,30 @@ +#[macro_use] +extern crate serde_idl; +extern crate serde; +extern crate dfx_info; + +//use dfx_info::types::{get_type, Type}; +use dfx_info::IDLType; +//use serde::Deserialize; +use serde_idl::{from_bytes}; + +fn test_check(x:&T) +where T: Eq + IDLType + serde::de::DeserializeOwned + std::fmt::Debug, +{ + // serialize via the IDL's wire format: + let y = IDL!(x); + // deserialize from the IDL's wire format: + let z1 : Result = from_bytes(&y); + let z2 : T = z1.unwrap(); + assert_eq!(x, &z2); +} + +#[test] +fn test_simple_values() { + test_check(&true); + test_check(&false); + test_check(&(true, true)); + test_check(&(1, true)); + test_check(&(false, 2)); + test_check(&(false, 2, 3)); +} diff --git a/lib/serde_idl/tests/ser.rs b/lib/serde_idl/tests/ser.rs index 4933beaf97..2f944575b2 100644 --- a/lib/serde_idl/tests/ser.rs +++ b/lib/serde_idl/tests/ser.rs @@ -1,44 +1,47 @@ #[macro_use] extern crate serde_idl; extern crate dfx_info; +extern crate serde; use dfx_info::types::{get_type, Type}; use dfx_info::IDLType; -use serde_idl::to_vec; +use serde::Deserialize; +use serde_idl::{from_bytes, idl_hash}; #[test] fn test_bool() { - check(true, "4449444c00017e01"); - check(false, "4449444c00017e00"); + all_check(true, "4449444c00017e01"); + all_check(false, "4449444c00017e00"); assert_eq!(get_type(&true), Type::Bool); } #[test] fn test_integer() { - check(42, "4449444c00017c2a"); - check(1_234_567_890, "4449444c00017cd285d8cc04"); - check(-1_234_567_890, "4449444c00017caefaa7b37b"); - check(Box::new(42), "4449444c00017c2a"); + all_check(42, "4449444c00017c2a"); + all_check(1_234_567_890, "4449444c00017cd285d8cc04"); + all_check(-1_234_567_890, "4449444c00017caefaa7b37b"); + all_check(Box::new(42), "4449444c00017c2a"); assert_eq!(get_type(&42), Type::Int); } #[test] fn test_text() { + all_check("Hi ☃\n".to_string(), "4449444c00017107486920e298830a"); check("Hi ☃\n", "4449444c00017107486920e298830a"); } #[test] fn test_option() { - check(Some(42), "4449444c016e7c0100012a"); - check(Some(Some(42)), "4449444c026e016e7c010001012a"); + all_check(Some(42), "4449444c016e7c0100012a"); + all_check(Some(Some(42)), "4449444c026e016e7c010001012a"); let opt: Option = None; assert_eq!(get_type(&opt), Type::Opt(Box::new(Type::Int))); - check(opt, "4449444c016e7c010000"); + all_check(opt, "4449444c016e7c010000"); } #[test] fn test_struct() { - #[derive(Debug, IDLType)] + #[derive(Debug, Deserialize, IDLType)] struct A { foo: i32, bar: bool, @@ -49,13 +52,13 @@ fn test_struct() { get_type(&record), Type::Record(vec![field("bar", Type::Bool), field("foo", Type::Int),]) ); - check(record, "4449444c016c02d3e3aa027e868eb7027c0100012a"); + all_check(record, "4449444c016c02d3e3aa027e868eb7027c0100012a"); - #[derive(Debug, IDLType)] + #[derive(Debug, Deserialize, IDLType)] struct B(bool, i32); - check(B(true, 42), "4449444c016c02007e017c0100012a"); + all_check(B(true, 42), "4449444c016c02007e017c0100012a"); - #[derive(Debug, IDLType)] + #[derive(Debug, Deserialize, IDLType)] struct List { head: i32, tail: Option>, @@ -75,68 +78,78 @@ fn test_struct() { ) ]) ); - check(list, "4449444c026c02a0d2aca8047c90eddae704016e0001002a00"); + all_check(list, "4449444c026c02a0d2aca8047c90eddae704016e0001002a00"); let list: Option = None; // without memoization on the unrolled type, type table will have 3 entries. - check(list, "4449444c026e016c02a0d2aca8047c90eddae70400010000"); + all_check(list, "4449444c026e016c02a0d2aca8047c90eddae70400010000"); } #[test] fn test_mutual_recursion() { type List = Option; - #[derive(Debug, IDLType)] + #[derive(Debug, Deserialize, IDLType)] struct ListA { head: i32, tail: Box, }; let list: List = None; - check(list, "4449444c026e016c02a0d2aca8047c90eddae70400010000"); + all_check(list, "4449444c026e016c02a0d2aca8047c90eddae70400010000"); } #[test] fn test_vector() { - check(vec![0, 1, 2, 3], "4449444c016d7c01000400010203"); - check([0, 1, 2, 3], "4449444c016d7c01000400010203"); + all_check(vec![0, 1, 2, 3], "4449444c016d7c01000400010203"); + all_check([0, 1, 2, 3], "4449444c016d7c01000400010203"); let boxed_array: Box<[i32]> = Box::new([0, 1, 2, 3]); - check(boxed_array, "4449444c016d7c01000400010203"); - check( - [(42, "text")], + all_check(boxed_array, "4449444c016d7c01000400010203"); + all_check( + [(42, "text".to_string())], "4449444c026d016c02007c01710100012a0474657874", ); - check([[[[()]]]], "4449444c046d016d026d036d7f010001010101"); + all_check([[[[()]]]], "4449444c046d016d026d036d7f010001010101"); } #[test] fn test_tuple() { - check((42, "💩"), "4449444c016c02007c017101002a04f09f92a9"); + all_check( + (42, "💩".to_string()), + "4449444c016c02007c017101002a04f09f92a9", + ); } #[test] fn test_variant() { - #[derive(Debug, IDLType)] + #[derive(Debug, Deserialize, IDLType)] enum Unit { Foo, + Bar, } - check(Unit::Foo, "4449444c016b01e6fdd5017f010000"); + all_check(Unit::Bar, "4449444c016b02b3d3c9017fe6fdd5017f010000"); - let res: Result<&str, &str> = Ok("good"); - check(res, "4449444c016b02bc8a0171c5fed2017101000004676f6f64"); + let res: Result = Ok("good".to_string()); + all_check(res, "4449444c016b02bc8a0171c5fed2017101000004676f6f64"); #[allow(dead_code)] - #[derive(Debug, IDLType)] + #[derive(Debug, Deserialize, IDLType)] enum E { Foo, - Bar(bool), + Bar(bool, i32), Baz { a: i32, b: u32 }, } - let v = E::Foo; + let v = E::Bar(true, 42); assert_eq!( get_type(&v), Type::Variant(vec![ - field("Bar", Type::Record(vec![unnamed_field(0, Type::Bool)])), + field( + "Bar", + Type::Record(vec![ + unnamed_field(0, Type::Bool), + unnamed_field(1, Type::Int) + ]) + ), field( "Baz", Type::Record(vec![field("a", Type::Int), field("b", Type::Nat)]) @@ -144,15 +157,15 @@ fn test_variant() { field("Foo", Type::Null), ]) ); - check( + all_check( v, - "4449444c036b03b3d3c90101bbd3c90102e6fdd5017f6c01007e6c02617c627d010002", + "4449444c036b03b3d3c90101bbd3c90102e6fdd5017f6c02007e017c6c02617c627d010000012a", ); } #[test] fn test_generics() { - #[derive(Debug, IDLType)] + #[derive(Debug, Deserialize, IDLType)] struct G { g1: T, g2: E, @@ -163,11 +176,11 @@ fn test_generics() { get_type(&res), Type::Record(vec![field("g1", Type::Int), field("g2", Type::Bool)]) ); - check(res, "4449444c016c02eab3017cebb3017e01002a01") + all_check(res, "4449444c016c02eab3017cebb3017e01002a01") } #[test] -fn test_builder() { +fn test_multiargs() { checks( IDL!(&42, &Some(42), &Some(1), &Some(2)), "4449444c016e7c047c0000002a012a01010102", @@ -182,12 +195,22 @@ fn check(value: T, expected: &str) where T: IDLType, { - let encoded = to_vec(&value).unwrap(); + let encoded = IDL!(&value); + checks(encoded, expected); +} + +fn all_check(value: T, expected: &str) +where + T: IDLType + serde::de::DeserializeOwned, +{ let expected = hex::decode(expected).unwrap(); + let decoded: T = from_bytes(&expected).unwrap(); + let encoded_from_value = IDL!(&value); + let encoded_from_decoded = IDL!(&decoded); assert_eq!( - encoded, expected, - "\nExpected\n{:x?}\nActual\n{:x?}\n", - expected, encoded + encoded_from_value, encoded_from_decoded, + "\nValue\n{:x?}\nDecoded\n{:x?}\n", + encoded_from_value, encoded_from_decoded ); } @@ -215,11 +238,3 @@ fn unnamed_field(id: u32, ty: Type) -> dfx_info::types::Field { ty, } } - -fn idl_hash(id: &str) -> u32 { - let mut s: u32 = 0; - for c in id.chars() { - s = s.wrapping_mul(223).wrapping_add(c as u32); - } - s -}