| -rw-r--r-- | Cargo.lock | 341 | ||||
| -rw-r--r-- | Cargo.toml | 10 | ||||
| -rw-r--r-- | proptest-regressions/symbol.txt | 14 | ||||
| -rw-r--r-- | src/build.rs | 2 | ||||
| -rw-r--r-- | src/lib.rs | 91 | ||||
| -rw-r--r-- | src/protocol.rs | 8 | ||||
| -rw-r--r-- | src/symbol.rs | 759 | ||||
| -rw-r--r-- | tests/demo2.rs | 14 |
8 files changed, 1232 insertions, 7 deletions
@@ -3,6 +3,140 @@ version = 3 [[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "macro_rules_attribute" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a82271f7bc033d84bbca59a3ce3e4159938cb08a9c3aebbe54d215131518a13" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dd856d451cc0da70e2ef2ce95a18e39a93b7558bedf10201ad28503f918568" + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] name = "proc-macro2" version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -12,6 +146,32 @@ dependencies = [ ] [[package]] +name = "proptest" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.4.2", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -21,6 +181,85 @@ dependencies = [ ] [[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustix" +version = "0.38.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] name = "serde" version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -52,6 +291,25 @@ dependencies = [ ] [[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -61,5 +319,88 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" name = "uniserde" version = "0.1.0" dependencies = [ + "macro_rules_attribute", + "proptest", "serde", ] + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" @@ -11,3 +11,13 @@ default = [] std = ["alloc", "serde?/std"] alloc = ["serde?/alloc"] serde = ["dep:serde"] + +[dev-dependencies] +macro_rules_attribute = "0.2.0" +proptest = "1.4.0" + +[profile.test.package.proptest] +opt-level = 3 + +[profile.test.package.rand_chacha] +opt-level = 3 diff --git a/proptest-regressions/symbol.txt b/proptest-regressions/symbol.txt new file mode 100644 index 0000000..1634c70 --- /dev/null +++ b/proptest-regressions/symbol.txt @@ -0,0 +1,14 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 1725c26548f1c5297668210ab4dda12dc45754627a4019359763c1c9fd6913a3 # shrinks to x = 4 +cc 3c8175ce49a080bb929b2781b090073cf0700415a08db6b7c39f32b59592661b # shrinks to s = "XA0A0a_" +cc 3569a9f258aeb7d1dd27d9261dd0d511b4394c0fd2d4a386d8445d9c98756f30 # shrinks to str = " -DA5D_" +cc 779296ede6c0180afb57a35b1c128ade1435d02a6db2ed68e18efb5c7785ef2f # shrinks to str = "0E-0- " +cc 77c97f17e9195785d45fe2a040ccdb4932501ab8b1855ba19870f43454a337b4 # shrinks to str = "708QE" +cc dcf95b6e6c0f189109f4e22b84831c4881dd0ea10c6a87da3ca8ec984de427fa # shrinks to str = "" +cc 7f0b586af42923f396d792161f736dc475e8eb1afd1ab991a13fdc6b455b54ae # shrinks to str = "0A-A 00" +cc e4c41856713df30321a99e3e647f5541db9789f13cf2f2416380a0931be4a677 # shrinks to str = "-Xj0Ab" diff --git a/src/build.rs b/src/build.rs index 0322c6a..d66bf84 100644 --- a/src/build.rs +++ b/src/build.rs @@ -35,7 +35,7 @@ pub trait Builder<'ctx>: Default { /// Finish the value. /// - /// If an error happened with the builder during the walk + /// If an error happened with the builder during the walk /// it will be reported here. fn build(self) -> Result<Self::Value, Self::Error>; } @@ -1,14 +1,15 @@ //! # Design //! -#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(not(test), not(feature = "std")), no_std)] #[cfg(feature = "alloc")] extern crate alloc; +mod build; pub mod protocol; -pub mod build; -pub mod walk; +pub mod symbol; +mod walk; // pub mod impls; // pub mod transform; @@ -18,3 +19,87 @@ pub mod walk; // // pub use walk::Walk; // pub use walk::Walker; + +pub use build::*; +pub use walk::*; + +#[macro_export] +macro_rules! Build { + { + $(#[$($attr:tt)*])* + $vis:vis struct $name:ident {$( + $field:ident: $type:ty + ),* $(,)?} + } => { + const _: () = { + impl<'ctx> $crate::Build<'ctx> for $name { + type Builder = StructBuilder; + } + + #[derive(Default)] + $vis struct StructBuilder {$( + $field: Option<$type> + ),*} + + impl<'ctx> $crate::Builder<'ctx> for StructBuilder { + type Error = (); + + type Value = $name; + + fn as_visitor(&mut self) -> &mut dyn $crate::protocol::Implementer<'ctx> { + self + } + + fn build(self) -> Result<Self::Value, Self::Error> { + if let StructBuilder {$($field: Some($field)),*} = self { + Ok($name {$($field),*}) + } else { + Err(()) + } + } + } + + $crate::protocol::implementer! { + impl['ctx] StructBuilder = [ + + ]; + } + + impl<'ctx> $crate::Walk<'ctx> for $name { + type Walker = StructWalker; + } + + $vis struct StructWalker { + value: Option<$name>, + hint_given: bool, + } + + impl From<$name> for StructWalker { + fn from(value: $name) -> Self { + Self { + value: Some(value), + hint_given: false + } + } + } + + impl<'ctx> $crate::Walker<'ctx> for StructWalker { + type Error = (); + + type Output = (); + + fn walk(&mut self, visitor: &mut dyn $crate::protocol::Implementer<'ctx>) -> Result<Self::Output, Self::Error> { + use $crate::protocol::ImplementerExt; + // Want kinds for tags. + if let Some(interface) = visitor.interface_for::<$crate::builtins::visitor::request_hint>() { + interface.as_object().visit(&mut self); + } + $( + <$type as $crate::Walk>::Walker::from(self.0.$field).walk(visitor); + )* + todo!() + } + } + }; + }; +} diff --git a/src/protocol.rs b/src/protocol.rs index acd22b8..238dd5e 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,11 +1,13 @@ -//! # Design +//! Interface for interfaces. +//! +//! ## Design //! The design of protocols is based on an idea found in the [`gdbstub`](https://docs.rs/gdbstub/latest/gdbstub/target/ext/index.html) crate. //! This idea is of so called inlinable dyn extension traits. //! However, in the form given in `gdbstub` they can't be used for arbitrary interfaces. //! The main trait still needs to know about all the possible protocols. //! That is where this module comes in. //! -//! This module implements a technique we name dynamic inlinable dyn extension traits (DIDETs). +//! This module implements a technique we name dynamic inlinable dyn extension traits (DIDETs). //! DIDETs adds one more layer to IDETs. Instead of a trait that knows all the possible protocols, //! we have a single trait [`Implementer`] that allows looking up an extension trait //! using a type ID. This may seem like it defeats the purpose of IDETs, that being to @@ -70,7 +72,7 @@ pub trait Implementer<'ctx> { /// The returned [`AnyImpl`] could be for a different protocol; This is considered /// a bug in an implementation and can be resolved via a panic. This is how /// [`ImplementerExt::interface_for`] behaves. - /// + /// /// If `self` doesn't implement the given protocol, then a `None` is returned. fn interface(&mut self, id: ProtocolId) -> Option<AnyImpl<'_, 'ctx>>; } diff --git a/src/symbol.rs b/src/symbol.rs new file mode 100644 index 0000000..a3c8b4d --- /dev/null +++ b/src/symbol.rs @@ -0,0 +1,759 @@ +use range::*; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct Symbol(u64); + +impl Symbol { + #[track_caller] + pub const fn new(tag: &str) -> Self { + match Self::try_new(tag) { + Ok(s) => s, + Err(EncodeError::TooComplex { .. }) => { + panic!("Symbol is too complex to encode. Try making it shorter.") + } + Err(EncodeError::UnknownChar(_)) => { + panic!( + "Unknown character, supported characters are: a-z, A-Z, 0-9, _, -, and space." + ); + } + } + } + + pub const fn try_new(tag: &str) -> Result<Self, EncodeError> { + match encode(tag) { + Ok(tag) => Ok(Self(tag)), + Err(err) => Err(err), + } + } + + pub const fn to_int(self) -> u64 { + self.0 + } + + pub const fn from_int(value: u64) -> Self { + Self(value) + } +} + +impl core::fmt::Debug for Symbol { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("Symbol").field(&DisplayFmt(self.0)).finish() + } +} + +struct DisplayFmt(u64); + +impl core::fmt::Debug for DisplayFmt { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + decode(self.0, f) + } +} + +impl core::fmt::Display for Symbol { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + decode(self.0, f) + } +} + +#[derive(Debug)] +pub enum EncodeError<'a> { + TooComplex { encoded: &'a str }, + UnknownChar(char), +} + +impl<'a> EncodeError<'a> { + const fn too_complex(input: &'a str, length: usize) -> Self { + if length >= input.len() { + return Self::TooComplex { encoded: input }; + } + + let buffer = input.as_bytes(); + let slice = unsafe { core::slice::from_raw_parts(buffer.as_ptr(), length) }; + let s = unsafe { core::str::from_utf8_unchecked(slice) }; + + Self::TooComplex { encoded: s } + } +} + +fn decode(input: u64, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let (interval, mut range) = Interval::new(); + let mut precision = 31; // Matches the interval. + let mut input_buffer = 0; + let mut offset = 0; + + for _ in 0..precision { + input_buffer = (input_buffer << 1) + | match bit(&mut precision, &mut offset, input) { + Some(x) => x, + None => { + write!(f, "<EOF>")?; + return Ok(()); + } + }; + } + + let mut len = 0; + loop { + let symbol: usize; + let mut low_high: Range; + let mut sym_idx_low_high = (0, ALPHABET.len()); + + // Binary search for the symbol that covers the input. + loop { + let sym_idx_mid = (sym_idx_low_high.0 + sym_idx_low_high.1) / 2; + low_high = calculate_range(range, sym_idx_mid, len); + if low_high.low <= input_buffer && input_buffer < low_high.high { + symbol = sym_idx_mid; + break; + } else if input_buffer >= low_high.high { + sym_idx_low_high.0 = sym_idx_mid + 1; + } else { + sym_idx_low_high.1 = sym_idx_mid - 1; + } + } + + // When we see a null byte we know the string is done. + if symbol == ALPHABET.len() { + break; + } + + range = low_high; + + while interval.in_bottom_half(range) || interval.in_upper_half(range) { + if interval.in_bottom_half(range) { + range = interval.scale_bottom_half(range); + input_buffer = (2 * input_buffer) + | match bit(&mut precision, &mut offset, input) { + Some(x) => x, + None => { + write!(f, "<EOF>")?; + return Ok(()); + } + }; + } else if interval.in_upper_half(range) { + range = interval.scale_upper_half(range); + input_buffer = (2 * (input_buffer - interval.half())) + | match bit(&mut precision, &mut offset, input) { + Some(x) => x, + None => { + write!(f, "<EOF>")?; + return Ok(()); + } + }; + } + } + + while interval.in_middle_half(range) { + range = interval.scale_middle_half(range); + input_buffer = (2 * (input_buffer - interval.quarter())) + | match bit(&mut precision, &mut offset, input) { + Some(x) => x, + None => { + write!(f, "<EOF>")?; + return Ok(()); + } + }; + } + + core::fmt::Display::fmt(&(ALPHABET[symbol].0 as char), f)?; + len += 1; + } + + Ok(()) +} + +fn bit(precision: &mut u64, offset: &mut u64, input: u64) -> Option<u64> { + if *offset < 64 { + let bit = (input & (1u64 << *offset)) != 0; + *offset += 1; + Some(bit as _) + } else { + if *precision == 0 { + return None; + } + *precision -= 1; + Some(0) + } +} + +const fn encode(input: &str) -> Result<u64, EncodeError> { + // Interval and current range in that interval. + let (interval, mut range) = Interval::new(); + + // Output buffer. + let mut output = 0; + + // Offset into the output buffer to write to. + let mut offset = 0; + + // Index into the input string. + let mut index = 0; + + // Bits pending to write. + let mut pending_bit_count = 0; + + // For each char in the string and the terminator. + while index < input.len() + 1 { + // Get the char. + let symbol = if index == input.len() { + // Terminate with the null byte so we can recover the length in the + // decode. + b'\0' + } else { + input.as_bytes()[index] + }; + + // Find the symbol in the alphabet. + let Some(symbol_index) = find_symbol(symbol) else { + return Err(EncodeError::UnknownChar(symbol as char)); + }; + + // Update the range to be for the symbol. + range = calculate_range(range, symbol_index, index); + + // While the range is in the bottom or top half we can emit a bit + // and scale the range back to not loose precision. + while interval.in_bottom_half(range) || interval.in_upper_half(range) { + if interval.in_bottom_half(range) { + // Rescale range to the full interval. + range = interval.scale_bottom_half(range); + + // Emit a 0 as the range was in the lower half of the interval. + (output, offset, pending_bit_count) = + match emit(output, offset, pending_bit_count, false) { + Some(x) => x, + None => return Err(EncodeError::too_complex(input, index)), + }; + } else if interval.in_upper_half(range) { + // Rescale range to the full interval. + range = interval.scale_upper_half(range); + + // Emit a 1 as the range was in the lower half of the interval. + (output, offset, pending_bit_count) = + match emit(output, offset, pending_bit_count, true) { + Some(x) => x, + None => return Err(EncodeError::too_complex(input, index)), + }; + } + } + + // If the range is in the middle then the above couldn't scale it up. + // So we do it here from the middle. + // Because we don't know which side it will eventually be we add pending bits. + while interval.in_middle_half(range) { + // Rescale range to the full interval. + range = interval.scale_middle_half(range); + + pending_bit_count += 1; + } + + // Move to the next char. + index += 1; + } + + pending_bit_count += 1; + + if interval.in_bottom_quarter(range) { + (output, _, _) = match emit(output, offset, pending_bit_count, false) { + Some(x) => x, + None => return Err(EncodeError::too_complex(input, index)), + }; + } else { + (output, _, _) = match emit(output, offset, pending_bit_count, true) { + Some(x) => x, + None => return Err(EncodeError::too_complex(input, index)), + }; + } + + Ok(output) +} + +const fn emit( + mut output: u64, + mut offset: u64, + mut pending_bit_count: usize, + bit: bool, +) -> Option<(u64, u64, usize)> { + // Emit the bit. + if offset + pending_bit_count as u64 >= 64 { + return None; + } + output |= (bit as u64) << offset; + offset += 1; + + // For each middle half scale emit the inverse bit. + while pending_bit_count > 0 { + if offset + pending_bit_count as u64 >= 64 { + return None; + } + output |= (!bit as u64) << offset; + offset += 1; + pending_bit_count -= 1; + } + + // Return updated output state. + Some((output, offset, pending_bit_count)) +} + +const fn calculate_range(range: Range, symbol_index: usize, len: usize) -> Range { + // The symbol's range needs to be scaled down by the current width. + let new_width = range.high - range.low; + + // Get the probability range of the symbol, this is from 0 to 65536. + let Range { low, high } = probability(symbol_index, len); + + // Scale the probability range to be within the given range. + Range { + low: range.low + new_width * low / 65536, + high: range.low + new_width * high / 65536, + } +} + +const fn probability(symbol_index: usize, len: usize) -> Range { + if symbol_index == ALPHABET.len() { + let (_, table) = ALPHABET[ALPHABET.len() - 1]; + + let t = table[len].0; + let f = table[len].1; + + Range { + low: t + f, + high: 65536, + } + } else { + let (_, table) = ALPHABET[symbol_index]; + + let t = table[len].0; + let f = table[len].1; + + Range { + low: t, + high: t + f, + } + } +} + +const fn find_symbol(symbol: u8) -> Option<usize> { + if symbol == b'\0' { + return Some(ALPHABET.len()) + } + + let mut i = 0; + while i < ALPHABET.len() { + if ALPHABET[i].0 == symbol { + return Some(i); + } + i += 1; + } + None +} + +mod range { + #[derive(Debug, Copy, Clone)] + pub struct Range { + pub low: u64, + pub high: u64, + } + + #[derive(Debug, Copy, Clone)] + pub struct Interval { + one_quarter: u64, + half: u64, + three_quarter: u64, + } + + impl Interval { + pub const fn new() -> (Self, Range) { + // A precision of 31 bits is hardcoded to not overflow the u64 multiplication + // and because the alphabet uses a 2^16 divisor. + let precision = 31; + + let high: u64 = 1 << precision; + ( + Self { + one_quarter: high / 4, + half: high / 2, + three_quarter: (high / 4) * 3, + }, + Range { low: 0, high }, + ) + } + + /// Check if the range is entirely in the bottom half of the interval. + pub const fn in_bottom_half(&self, range: Range) -> bool { + range.high < self.half + } + + /// Check if the range is entirely in the upper half of the interval. + pub const fn in_upper_half(&self, range: Range) -> bool { + range.low > self.half + } + + /// Check if the range is entirely in the middle half of the interval. + pub const fn in_middle_half(&self, range: Range) -> bool { + range.low > self.one_quarter && range.high < self.three_quarter + } + + pub const fn in_bottom_quarter(&self, range: Range) -> bool { + range.low <= self.one_quarter + } + + /// Take a range in the upper half of the interval and scale it + /// to the whole interval. + pub const fn scale_upper_half(&self, mut range: Range) -> Range { + range.low = (range.low - self.half) << 1; + range.high = (range.high - self.half) << 1; + range + } + + /// Take a range in the middle of the interval and scale it + /// to the whole interval. + pub const fn scale_middle_half(&self, mut range: Range) -> Range { + range.low = (range.low - self.one_quarter) << 1; + range.high = (range.high - self.one_quarter) << 1; + range + } + + /// Take a range in the bottom half of the interval and scale it + /// to the whole interval. + pub const fn scale_bottom_half(&self, mut range: Range) -> Range { + range.low <<= 1; + range.high <<= 1; + range + } + + pub const fn half(&self) -> u64 { + self.half + } + + pub const fn quarter(&self) -> u64 { + self.one_quarter + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use proptest::proptest; + use std::collections::HashMap; + + proptest! { + #[allow(unreachable_code)] + #[test] + fn encode_decode(str in "(?:[a-z]|[A-Z]|[0-9]|[ _-]){0,18}") { + // Generate a symbol from the string. + let s = match Symbol::try_new(&str) { + Ok(s) => s, + Err(EncodeError::TooComplex { .. }) => { + // All too complex should be over 4 chars. + assert!(str.len() > 3); + return Ok(()); + } + Err(err) => panic!("{:?}", err), + }; + + // Format symbol as string to decode it. + let x = format!("{}", s); + + // The symbol as a string should match the original string. + assert_eq!(x, str); + } + } + + #[test] + #[should_panic(expected = "Symbol is too complex to encode. Try making it shorter.")] + fn too_complex_panic() { + Symbol::new("0123456789"); + } + + #[test] + #[should_panic( + expected = "Unknown character, supported characters are: a-z, A-Z, 0-9, _, -, and space." + )] + fn unknown_char_panic() { + Symbol::new("Hello, world"); + } + + #[test] + fn in_match() { + mod s { + use super::*; + + pub const A: Symbol = Symbol::new("A"); + pub const B: Symbol = Symbol::new("B"); + pub const C: Symbol = Symbol::new("C"); + } + + match Symbol::new("B") { + s::A => panic!(), + s::B => {} + s::C => panic!(), + _ => unreachable!(), + } + } + + #[test] + fn in_generic() { + struct X<const N: u64>; + + type Z = X<{ Symbol::new("Hello world").to_int() }>; + let _: Z; + } + + #[test] + fn demo() { + let n = Symbol::new("Hello").to_int().rotate_left(64); + for mut x in 0..1000 { + let mut y = x; + // y |= x << 25; + + + // println!("{:0>64b}", y); + println!("`{}`", Symbol::from_int(y)); + } + todo!(); + } + + #[test] + fn examples() { + const _: Symbol = Symbol::new("eeeeeeeeeeeeeeee"); + const _: Symbol = Symbol::new("XXX"); + const _: Symbol = Symbol::new("Hello world"); + const _: Symbol = Symbol::new("hello world_"); + const _: Symbol = Symbol::new("____________"); + const _: Symbol = Symbol::new(" "); + const _: Symbol = Symbol::new("--------"); + const _: Symbol = Symbol::new("_-_-_-_-_"); + const _: Symbol = Symbol::new("123456"); + const _: Symbol = Symbol::new("1111111"); + const _: Symbol = Symbol::new("99999"); + const _: Symbol = Symbol::new("0000"); + const _: Symbol = Symbol::new("Example "); + const _: Symbol = Symbol::new("ABCDEF"); + const _: Symbol = Symbol::new(""); + + const _: Symbol = Symbol::new("Protocol"); + const _: Symbol = Symbol::new("Attribute"); + const _: Symbol = Symbol::new("TypeName"); + const _: Symbol = Symbol::new("Name"); + const _: Symbol = Symbol::new("Field"); + const _: Symbol = Symbol::new("Variant"); + const _: Symbol = Symbol::new("Struct"); + const _: Symbol = Symbol::new("Module"); + const _: Symbol = Symbol::new("Enum"); + const _: Symbol = Symbol::new("Union"); + const _: Symbol = Symbol::new("Sequence"); + const _: Symbol = Symbol::new("Walker"); + const _: Symbol = Symbol::new("Builder"); + const _: Symbol = Symbol::new("Map"); + const _: Symbol = Symbol::new("Variable"); + + const _: Symbol = Symbol::new("alksjdoiwsd"); + const _: Symbol = Symbol::new("2309ja"); + const _: Symbol = Symbol::new("SAF012"); + } + + #[test] + fn alphabet() { + let lower = HashMap::<char, f64>::from([ + ('e', 12.02), + ('t', 9.10), + ('a', 8.12), + ('o', 7.68), + ('i', 7.31), + ('n', 6.95), + ('s', 6.28), + ('r', 6.02), + ('h', 5.92), + ('d', 4.32), + ('l', 3.98), + ('u', 2.88), + ('c', 2.71), + ('m', 2.71), + ('f', 2.30), + ('y', 2.11), + ('w', 2.09), + ('g', 2.03), + ('p', 1.82), + ('b', 1.49), + ('v', 1.11), + ('k', 0.69), + ('x', 0.17), + ('q', 0.11), + ('j', 0.10), + ('z', 0.07), + ]); + assert_eq!(lower.len(), 26); + assert!((lower.iter().map(|(_, x)| x).sum::<f64>() - 100.0).abs() < 0.1); + + let upper = HashMap::<char, f64>::from([ + ('A', 5.64), + ('B', 6.25), + ('C', 9.44), + ('D', 5.39), + ('E', 3.42), + ('F', 4.26), + ('G', 3.58), + ('H', 4.10), + ('I', 3.22), + ('J', 0.95), + ('K', 1.32), + ('L', 3.71), + ('M', 5.71), + ('N', 2.29), + ('O', 2.70), + ('P', 8.13), + ('Q', 0.53), + ('R', 4.49), + ('S', 11.65), + ('T', 5.52), + ('U', 2.54), + ('V', 1.58), + ('W', 2.82), + ('X', 0.09), + ('Y', 0.38), + ('Z', 0.27), + ]); + assert_eq!(upper.len(), 26); + assert!((upper.iter().map(|(_, x)| x).sum::<f64>() - 100.0).abs() < 0.1); + + let digit = HashMap::<char, f64>::from([ + ('1', 30.1), + ('2', 17.6), + ('3', 12.5), + ('4', 9.7), + ('5', 7.9), + ('6', 6.7), + ('7', 5.8), + ('8', 5.1), + ('9', 3.6), + ('0', 1.0), + ]); + assert_eq!(digit.len(), 10); + assert!((digit.iter().map(|(_, x)| x).sum::<f64>() - 100.0).abs() < 0.1); + + let extra = HashMap::<char, f64>::from([(' ', 30.0), ('_', 60.0), ('-', 10.0)]); + assert_eq!(extra.len(), 3); + assert!((extra.iter().map(|(_, x)| x).sum::<f64>() - 100.0).abs() < 0.1); + + let all: HashMap<char, f64> = lower + .iter() + .map(|(c, f)| (*c, (f / 100.0) * 0.90)) + .chain(upper.iter().map(|(c, f)| (*c, (f / 100.0) * 0.025))) + .chain(digit.iter().map(|(c, f)| (*c, (f / 100.0) * 0.0141))) + .chain(extra.iter().map(|(c, f)| (*c, (f / 100.0) * 0.06))) + // .chain([('\0', (0.07003f64 * 65536.0).round() as u64)]) + .collect(); + let mut all: Vec<_> = all.into_iter().collect(); + all.sort_by(|(a, _), (b, _)| a.cmp(b)); + assert_eq!(all.len(), 26 + 26 + 10 + 3); + let diff = all.iter().map(|(_, x)| x).sum::<f64>() - 1.0; + assert!(diff.abs() < 0.0001, "{}", diff); + + let scales: [f64; 18] = [ + 0.0001, 0.001, 0.01, 0.023, 0.048, 0.074, 0.103, 0.135, 0.169, 0.207, 0.250, 0.298, 0.353, 0.419, + 0.500, 0.603, 0.750, 0.95, + ]; + let mut totals = [0u64; 18]; + let with_len: Vec<(u8, [(u64, u64); 18])> = all + .into_iter() + .map(|(c, f)| { + let mut lens = [(0, 0); 18]; + for j in 0..16 { + lens[j].0 = totals[j]; + let f = ((f * (1.0 - scales[j])) * 65536.0).round() as u64; + lens[j].1 = f; + totals[j] += f; + } + (c as _, lens) + }) + .collect(); + assert_eq!(with_len.len(), 26 + 26 + 10 + 3); + + if ALPHABET != with_len { + eprintln!("const ALPHABET: &[(u8, [(u64, u64); 18])] = &["); + for (c, table) in &with_len { + use std::fmt::Write; + let mut table_s = String::new(); + for (t, f) in table { + write!(table_s, "({},{}),", t, f).unwrap(); + } + + eprintln!(" (b{:?}, [{}]),", *c as char, table_s); + } + eprintln!("];"); + panic!("alphabet is wrong") + } + } +} + +#[rustfmt::skip] +const ALPHABET: &[(u8, [(u64, u64); 18])] = &[ + (b' ', [(0,1180),(0,1178),(0,1168),(0,1153),(0,1123),(0,1092),(0,1058),(0,1020),(0,980),(0,935),(0,885),(0,828),(0,763),(0,685),(0,590),(0,468),(0,0),(0,0),]), + (b'-', [(1180,393),(1178,393),(1168,389),(1153,384),(1123,374),(1092,364),(1058,353),(1020,340),(980,327),(935,312),(885,295),(828,276),(763,254),(685,228),(590,197),(468,156),(0,0),(0,0),]), + (b'0', [(1573,9),(1571,9),(1557,9),(1537,9),(1497,9),(1456,9),(1411,8),(1360,8),(1307,8),(1247,7),(1180,7),(1104,6),(1017,6),(913,5),(787,5),(624,4),(0,0),(0,0),]), + (b'1', [(1582,278),(1580,278),(1566,275),(1546,272),(1506,265),(1465,258),(1419,249),(1368,241),(1315,231),(1254,221),(1187,209),(1110,195),(1023,180),(918,162),(792,139),(628,110),(0,0),(0,0),]), + (b'2', [(1860,163),(1858,162),(1841,161),(1818,159),(1771,155),(1723,151),(1668,146),(1609,141),(1546,135),(1475,129),(1396,122),(1305,114),(1203,105),(1080,94),(931,81),(738,65),(0,0),(0,0),]), + (b'3', [(2023,115),(2020,115),(2002,114),(1977,113),(1926,110),(1874,107),(1814,104),(1750,100),(1681,96),(1604,92),(1518,87),(1419,81),(1308,75),(1174,67),(1012,58),(803,46),(0,0),(0,0),]), + (b'4', [(2138,90),(2135,90),(2116,89),(2090,88),(2036,85),(1981,83),(1918,80),(1850,78),(1777,74),(1696,71),(1605,67),(1500,63),(1383,58),(1241,52),(1070,45),(849,36),(0,0),(0,0),]), + (b'5', [(2228,73),(2225,73),(2205,72),(2178,71),(2121,69),(2064,68),(1998,65),(1928,63),(1851,61),(1767,58),(1672,55),(1563,51),(1441,47),(1293,42),(1115,37),(885,29),(0,0),(0,0),]), + (b'6', [(2301,62),(2298,62),(2277,61),(2249,60),(2190,59),(2132,57),(2063,56),(1991,54),(1912,51),(1825,49),(1727,46),(1614,43),(1488,40),(1335,36),(1152,31),(914,25),(0,0),(0,0),]), + (b'7', [(2363,54),(2360,54),(2338,53),(2309,52),(2249,51),(2189,50),(2119,48),(2045,46),(1963,45),(1874,43),(1773,40),(1657,38),(1528,35),(1371,31),(1183,27),(939,21),(0,0),(0,0),]), + (b'8', [(2417,47),(2414,47),(2391,47),(2361,46),(2300,45),(2239,44),(2167,42),(2091,41),(2008,39),(1917,37),(1813,35),(1695,33),(1563,30),(1402,27),(1210,24),(960,19),(0,0),(0,0),]), + (b'9', [(2464,33),(2461,33),(2438,33),(2407,33),(2345,32),(2283,31),(2209,30),(2132,29),(2047,28),(1954,26),(1848,25),(1728,23),(1593,22),(1429,19),(1234,17),(979,13),(0,0),(0,0),]), + (b'A', [(2497,92),(2494,92),(2471,91),(2440,90),(2377,88),(2314,86),(2239,83),(2161,80),(2075,77),(1980,73),(1873,69),(1751,65),(1615,60),(1448,54),(1251,46),(992,37),(0,0),(0,0),]), + (b'B', [(2589,102),(2586,102),(2562,101),(2530,100),(2465,97),(2400,95),(2322,92),(2241,89),(2152,85),(2053,81),(1942,77),(1816,72),(1675,66),(1502,59),(1297,51),(1029,41),(0,0),(0,0),]), + (b'C', [(2691,155),(2688,155),(2663,153),(2630,151),(2562,147),(2495,143),(2414,139),(2330,134),(2237,129),(2134,123),(2019,116),(1888,109),(1741,100),(1561,90),(1348,77),(1070,61),(0,0),(0,0),]), + (b'D', [(2846,88),(2843,88),(2816,87),(2781,86),(2709,84),(2638,82),(2553,79),(2464,76),(2366,73),(2257,70),(2135,66),(1997,62),(1841,57),(1651,51),(1425,44),(1131,35),(0,0),(0,0),]), + (b'E', [(2934,56),(2931,56),(2903,55),(2867,55),(2793,53),(2720,52),(2632,50),(2540,48),(2439,47),(2327,44),(2201,42),(2059,39),(1898,36),(1702,33),(1469,28),(1166,22),(0,0),(0,0),]), + (b'F', [(2990,70),(2987,70),(2958,69),(2922,68),(2846,66),(2772,65),(2682,63),(2588,60),(2486,58),(2371,55),(2243,52),(2098,49),(1934,45),(1735,41),(1497,35),(1188,28),(0,0),(0,0),]), + (b'G', [(3060,59),(3057,59),(3027,58),(2990,57),(2912,56),(2837,54),(2745,53),(2648,51),(2544,49),(2426,47),(2295,44),(2147,41),(1979,38),(1776,34),(1532,29),(1216,23),(0,0),(0,0),]), + (b'H', [(3119,67),(3116,67),(3085,67),(3047,66),(2968,64),(2891,62),(2798,60),(2699,58),(2593,56),(2473,53),(2339,50),(2188,47),(2017,43),(1810,39),(1561,34),(1239,27),(0,0),(0,0),]), + (b'I', [(3186,53),(3183,53),(3152,52),(3113,52),(3032,50),(2953,49),(2858,47),(2757,46),(2649,44),(2526,42),(2389,40),(2235,37),(2060,34),(1849,31),(1595,26),(1266,21),(0,0),(0,0),]), + (b'J', [(3239,16),(3236,16),(3204,15),(3165,15),(3082,15),(3002,14),(2905,14),(2803,13),(2693,13),(2568,12),(2429,12),(2272,11),(2094,10),(1880,9),(1621,8),(1287,6),(0,0),(0,0),]), + (b'K', [(3255,22),(3252,22),(3219,21),(3180,21),(3097,21),(3016,20),(2919,19),(2816,19),(2706,18),(2580,17),(2441,16),(2283,15),(2104,14),(1889,13),(1629,11),(1293,9),(0,0),(0,0),]), + (b'L', [(3277,61),(3274,61),(3240,60),(3201,59),(3118,58),(3036,56),(2938,55),(2835,53),(2724,51),(2597,48),(2457,46),(2298,43),(2118,39),(1902,35),(1640,30),(1302,24),(0,0),(0,0),]), + (b'M', [(3338,94),(3335,93),(3300,93),(3260,91),(3176,89),(3092,87),(2993,84),(2888,81),(2775,78),(2645,74),(2503,70),(2341,66),(2157,61),(1937,54),(1670,47),(1326,37),(0,0),(0,0),]), + (b'N', [(3432,38),(3428,37),(3393,37),(3351,37),(3265,36),(3179,35),(3077,34),(2969,32),(2853,31),(2719,30),(2573,28),(2407,26),(2218,24),(1991,22),(1717,19),(1363,15),(0,0),(0,0),]), + (b'O', [(3470,44),(3465,44),(3430,44),(3388,43),(3301,42),(3214,41),(3111,40),(3001,38),(2884,37),(2749,35),(2601,33),(2433,31),(2242,29),(2013,26),(1736,22),(1378,18),(0,0),(0,0),]), + (b'P', [(3514,133),(3509,133),(3474,132),(3431,130),(3343,127),(3255,123),(3151,119),(3039,115),(2921,111),(2784,106),(2634,100),(2464,94),(2271,86),(2039,77),(1758,67),(1396,53),(0,0),(0,0),]), + (b'Q', [(3647,9),(3642,9),(3606,9),(3561,8),(3470,8),(3378,8),(3270,8),(3154,8),(3032,7),(2890,7),(2734,7),(2558,6),(2357,6),(2116,5),(1825,4),(1449,3),(0,0),(0,0),]), + (b'R', [(3656,74),(3651,73),(3615,73),(3569,72),(3478,70),(3386,68),(3278,66),(3162,64),(3039,61),(2897,58),(2741,55),(2564,52),(2363,48),(2121,43),(1829,37),(1452,29),(0,0),(0,0),]), + (b'S', [(3730,191),(3724,191),(3688,189),(3641,186),(3548,182),(3454,177),(3344,171),(3226,165),(3100,159),(2955,151),(2796,143),(2616,134),(2411,123),(2164,111),(1866,95),(1481,76),(0,0),(0,0),]), + (b'T', [(3921,90),(3915,90),(3877,90),(3827,88),(3730,86),(3631,84),(3515,81),(3391,78),(3259,75),(3106,72),(2939,68),(2750,63),(2534,59),(2275,53),(1961,45),(1557,36),(0,0),(0,0),]), + (b'U', [(4011,42),(4005,42),(3967,41),(3915,41),(3816,40),(3715,39),(3596,37),(3469,36),(3334,35),(3178,33),(3007,31),(2813,29),(2593,27),(2328,24),(2006,21),(1593,17),(0,0),(0,0),]), + (b'V', [(4053,26),(4047,26),(4008,26),(3956,25),(3856,25),(3754,24),(3633,23),(3505,22),(3369,22),(3211,21),(3038,19),(2842,18),(2620,17),(2352,15),(2027,13),(1610,10),(0,0),(0,0),]), + (b'W', [(4079,46),(4073,46),(4034,46),(3981,45),(3881,44),(3778,43),(3656,41),(3527,40),(3391,38),(3232,37),(3057,35),(2860,32),(2637,30),(2367,27),(2040,23),(1620,18),(0,0),(0,0),]), + (b'X', [(4125,1),(4119,1),(4080,1),(4026,1),(3925,1),(3821,1),(3697,1),(3567,1),(3429,1),(3269,1),(3092,1),(2892,1),(2667,1),(2394,1),(2063,1),(1638,1),(0,0),(0,0),]), + (b'Y', [(4126,6),(4120,6),(4081,6),(4027,6),(3926,6),(3822,6),(3698,6),(3568,5),(3430,5),(3270,5),(3093,5),(2893,4),(2668,4),(2395,4),(2064,3),(1639,2),(0,0),(0,0),]), + (b'Z', [(4132,4),(4126,4),(4087,4),(4033,4),(3932,4),(3828,4),(3704,4),(3573,4),(3435,4),(3275,4),(3098,3),(2897,3),(2672,3),(2399,3),(2067,2),(1641,2),(0,0),(0,0),]), + (b'_', [(4136,2359),(4130,2357),(4091,2336),(4037,2305),(3936,2246),(3832,2185),(3708,2116),(3577,2041),(3439,1961),(3279,1871),(3101,1769),(2900,1656),(2675,1526),(2402,1371),(2069,1180),(1643,937),(0,0),(0,0),]), + (b'a', [(6495,4789),(6487,4785),(6427,4741),(6342,4679),(6182,4559),(6017,4435),(5824,4296),(5618,4143),(5400,3980),(5150,3798),(4870,3592),(4556,3362),(4201,3099),(3773,2783),(3249,2395),(2580,1901),(0,0),(0,0),]), + (b'b', [(11284,879),(11272,878),(11168,870),(11021,859),(10741,837),(10452,814),(10120,788),(9761,760),(9380,730),(8948,697),(8462,659),(7918,617),(7300,569),(6556,511),(5644,439),(4481,349),(0,0),(0,0),]), + (b'c', [(12163,1598),(12150,1597),(12038,1582),(11880,1562),(11578,1522),(11266,1480),(10908,1434),(10521,1383),(10110,1328),(9645,1268),(9121,1199),(8535,1122),(7869,1034),(7067,929),(6083,799),(4830,635),(0,0),(0,0),]), + (b'd', [(13761,2548),(13747,2545),(13620,2523),(13442,2489),(13100,2426),(12746,2359),(12342,2286),(11904,2204),(11438,2117),(10913,2021),(10320,1911),(9657,1789),(8903,1649),(7996,1480),(6882,1274),(5465,1012),(0,0),(0,0),]), + (b'e', [(16309,7089),(16292,7083),(16143,7019),(15931,6927),(15526,6749),(15105,6565),(14628,6359),(14108,6133),(13555,5892),(12934,5622),(12231,5317),(11446,4977),(10552,4587),(9476,4119),(8156,3545),(6477,2815),(0,0),(0,0),]), + (b'f', [(23398,1356),(23375,1355),(23162,1343),(22858,1325),(22275,1291),(21670,1256),(20987,1217),(20241,1173),(19447,1127),(18556,1076),(17548,1017),(16423,952),(15139,878),(13595,788),(11701,678),(9292,539),(0,0),(0,0),]), + (b'g', [(24754,1197),(24730,1196),(24505,1185),(24183,1170),(23566,1140),(22926,1109),(22204,1074),(21414,1036),(20574,995),(19632,949),(18565,898),(17375,841),(16017,775),(14383,696),(12379,599),(9831,475),(0,0),(0,0),]), + (b'h', [(25951,3491),(25926,3488),(25690,3457),(25353,3411),(24706,3324),(24035,3233),(23278,3132),(22450,3020),(21569,2902),(20581,2769),(19463,2619),(18216,2451),(16792,2259),(15079,2029),(12978,1746),(10306,1386),(0,0),(0,0),]), + (b'i', [(29442,4311),(29414,4307),(29147,4268),(28764,4212),(28030,4105),(27268,3993),(26410,3868),(25470,3730),(24471,3583),(23350,3419),(22082,3234),(20667,3027),(19051,2790),(17108,2505),(14724,2156),(11692,1712),(0,0),(0,0),]), + (b'j', [(33753,59),(33721,59),(33415,58),(32976,58),(32135,56),(31261,55),(30278,53),(29200,51),(28054,49),(26769,47),(25316,44),(23694,41),(21841,38),(19613,34),(16880,29),(13404,23),(0,0),(0,0),]), + (b'k', [(33812,407),(33780,407),(33473,403),(33034,398),(32191,387),(31316,377),(30331,365),(29251,352),(28103,338),(26816,323),(25360,305),(23735,286),(21879,263),(19647,236),(16909,203),(13427,162),(0,0),(0,0),]), + (b'l', [(34219,2347),(34187,2345),(33876,2324),(33432,2294),(32578,2235),(31693,2174),(30696,2106),(29603,2031),(28441,1951),(27139,1862),(25665,1761),(24021,1648),(22142,1519),(19883,1364),(17112,1174),(13589,932),(0,0),(0,0),]), + (b'm', [(36566,1598),(36532,1597),(36200,1582),(35726,1562),(34813,1522),(33867,1480),(32802,1434),(31634,1383),(30392,1328),(29001,1268),(27426,1199),(25669,1122),(23661,1034),(21247,929),(18286,799),(14521,635),(0,0),(0,0),]), + (b'n', [(38164,4099),(38129,4095),(37782,4058),(37288,4005),(36335,3903),(35347,3796),(34236,3677),(33017,3546),(31720,3406),(30269,3251),(28625,3074),(26791,2878),(24695,2652),(22176,2382),(19085,2050),(15156,1627),(0,0),(0,0),]), + (b'o', [(42263,4529),(42224,4525),(41840,4485),(41293,4426),(40238,4312),(39143,4195),(37913,4063),(36563,3918),(35126,3764),(33520,3592),(31699,3397),(29669,3180),(27347,2931),(24558,2632),(21135,2265),(16783,1798),(0,0),(0,0),]), + (b'p', [(46792,1073),(46749,1072),(46325,1063),(45719,1049),(44550,1022),(43338,994),(41976,963),(40481,929),(38890,892),(37112,851),(35096,805),(32849,754),(30278,695),(27190,624),(23400,537),(18581,426),(0,0),(0,0),]), + (b'q', [(47865,65),(47821,65),(47388,64),(46768,63),(45572,62),(44332,60),(42939,58),(41410,56),(39782,54),(37963,51),(35901,49),(33603,46),(30973,42),(27814,38),(23937,32),(19007,26),(0,0),(0,0),]), + (b'r', [(47930,3550),(47886,3547),(47452,3515),(46831,3469),(45634,3380),(44392,3288),(42997,3185),(41466,3071),(39836,2951),(38014,2816),(35950,2663),(33649,2493),(31015,2297),(27852,2063),(23969,1775),(19033,1410),(0,0),(0,0),]), + (b's', [(51480,3704),(51433,3700),(50967,3667),(50300,3619),(49014,3526),(47680,3430),(46182,3323),(44537,3204),(42787,3078),(40830,2937),(38613,2778),(36142,2600),(33312,2397),(29915,2152),(25744,1852),(20443,1471),(0,0),(0,0),]), + (b't', [(55184,5367),(55133,5362),(54634,5314),(53919,5244),(52540,5110),(51110,4970),(49505,4815),(47741,4643),(45865,4460),(43767,4256),(41391,4026),(38742,3768),(35709,3473),(32067,3118),(27596,2684),(21914,2131),(0,0),(0,0),]), + (b'u', [(60551,1699),(60495,1697),(59948,1682),(59163,1660),(57650,1617),(56080,1573),(54320,1524),(52384,1469),(50325,1412),(48023,1347),(45417,1274),(42510,1192),(39182,1099),(35185,987),(30280,849),(24045,674),(0,0),(0,0),]), + (b'v', [(62250,655),(62192,654),(61630,648),(60823,640),(59267,623),(57653,606),(55844,587),(53853,566),(51737,544),(49370,519),(46691,491),(43702,460),(40281,424),(36172,380),(31129,327),(24719,260),(0,0),(0,0),]), + (b'w', [(62905,1233),(62846,1231),(62278,1220),(61463,1204),(59890,1174),(58259,1142),(56431,1106),(54419,1066),(52281,1024),(49889,978),(47182,925),(44162,865),(40705,798),(36552,716),(31456,616),(24979,489),(0,0),(0,0),]), + (b'x', [(64138,100),(64077,100),(63498,99),(62667,98),(61064,95),(59401,93),(57537,90),(55485,87),(53305,83),(50867,80),(48107,75),(45027,70),(41503,65),(37268,58),(32072,50),(25468,40),(0,0),(0,0),]), + (b'y', [(64238,1244),(64177,1243),(63597,1232),(62765,1216),(61159,1185),(59494,1152),(57627,1116),(55572,1077),(53388,1034),(50947,987),(48182,933),(45097,874),(41568,805),(37326,723),(32122,622),(25508,494),(0,0),(0,0),]), + (b'z', [(65482,41),(65420,41),(64829,41),(63981,40),(62344,39),(60646,38),(58743,37),(56649,36),(54422,34),(51934,33),(49115,31),(45971,29),(42373,27),(38049,24),(32744,21),(26002,16),(0,0),(0,0),]), +]; diff --git a/tests/demo2.rs b/tests/demo2.rs new file mode 100644 index 0000000..4bb811a --- /dev/null +++ b/tests/demo2.rs @@ -0,0 +1,14 @@ +use macro_rules_attribute::derive; +use uniserde::Build; + +#[derive(Build!)] +struct A { + b: B, + c: B, +} + +#[derive(Build!)] +struct B {} + +#[test] +fn demo() {} |