diff --git a/Cargo.lock b/Cargo.lock index a93412ad461..eb211193d4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4298,6 +4298,7 @@ version = "0.0.0" dependencies = [ "bincode", "criterion", + "databake", "displaydoc", "icu_benchmark_macros", "litemap", @@ -4306,6 +4307,8 @@ dependencies = [ "rand_pcg", "serde", "serde_json", + "yoke", + "zerofrom", "zerovec", ] diff --git a/experimental/zerotrie/Cargo.toml b/experimental/zerotrie/Cargo.toml index f1997eb1623..bdf6f2ee420 100644 --- a/experimental/zerotrie/Cargo.toml +++ b/experimental/zerotrie/Cargo.toml @@ -24,9 +24,12 @@ all-features = true denylist = ["bench"] [dependencies] +databake = { workspace = true, features = ["derive"], optional = true } displaydoc = { version = "0.2.3", default-features = false } litemap = { workspace = true, features = ["alloc"], optional = true } serde = { version = "1.0", optional = true } +yoke = { workspace = true, features = ["derive"], optional = true } +zerofrom = { workspace = true, optional = true } zerovec = { workspace = true, optional = true } [dev-dependencies] @@ -50,8 +53,11 @@ bench = false # This option is required for Benchmark CI default = [] bench = ["litemap"] alloc = [] +databake = ["dep:databake", "zerovec?/databake"] litemap = ["dep:litemap", "alloc"] serde = ["dep:serde", "dep:litemap", "alloc", "litemap/serde", "zerovec?/serde"] +yoke = ["dep:yoke"] +zerofrom = ["dep:zerofrom"] [[bench]] name = "overview" diff --git a/experimental/zerotrie/src/zerotrie.rs b/experimental/zerotrie/src/zerotrie.rs index 3646ec98a7e..ec01bc53c30 100644 --- a/experimental/zerotrie/src/zerotrie.rs +++ b/experimental/zerotrie/src/zerotrie.rs @@ -61,6 +61,8 @@ use litemap::LiteMap; /// # Ok::<_, zerotrie::ZeroTrieError>(()) /// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq)] +// Note: The absence of the following derive does not cause any test failures in this crate +#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))] pub struct ZeroTrie(pub(crate) ZeroTrieFlavor); #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -96,8 +98,10 @@ pub(crate) enum ZeroTrieFlavor { /// ``` #[repr(transparent)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerotrie))] pub struct ZeroTrieSimpleAscii { - pub(crate) store: Store, + #[doc(hidden)] // for databake, but there are no invariants + pub store: Store, } /// A data structure that compactly maps from byte strings to integers. @@ -126,8 +130,10 @@ pub struct ZeroTrieSimpleAscii { /// ``` #[repr(transparent)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerotrie))] pub struct ZeroTriePerfectHash { - pub(crate) store: Store, + #[doc(hidden)] // for databake, but there are no invariants + pub store: Store, } /// A data structure that maps from a large number of byte strings to integers. @@ -135,8 +141,10 @@ pub struct ZeroTriePerfectHash { /// For more information, see [`ZeroTrie`]. #[repr(transparent)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerotrie))] pub struct ZeroTrieExtendedCapacity { - pub(crate) store: Store, + #[doc(hidden)] // for databake, but there are no invariants + pub store: Store, } macro_rules! impl_zerotrie_subtype { @@ -474,6 +482,16 @@ macro_rules! impl_zerotrie_subtype { core::mem::transmute(Store::from_byte_slice_unchecked(bytes)) } } + #[cfg(feature = "zerofrom")] + impl<'zf, Store1, Store2> zerofrom::ZeroFrom<'zf, $name> for $name + where + Store2: zerofrom::ZeroFrom<'zf, Store1>, + { + #[inline] + fn zero_from(other: &'zf $name) -> Self { + $name::from_store(zerofrom::ZeroFrom::zero_from(&other.store)) + } + } }; } @@ -536,6 +554,19 @@ macro_rules! impl_dispatch { ZeroTrieFlavor::ExtendedCapacity(subtype) => subtype.$inner_fn($arg), } }; + (&$self:ident, $trait:ident::$inner_fn:ident()) => { + match &$self.0 { + ZeroTrieFlavor::SimpleAscii(subtype) => { + ZeroTrie(ZeroTrieFlavor::SimpleAscii($trait::$inner_fn(subtype))) + } + ZeroTrieFlavor::PerfectHash(subtype) => { + ZeroTrie(ZeroTrieFlavor::PerfectHash($trait::$inner_fn(subtype))) + } + ZeroTrieFlavor::ExtendedCapacity(subtype) => { + ZeroTrie(ZeroTrieFlavor::ExtendedCapacity($trait::$inner_fn(subtype))) + } + } + }; } impl ZeroTrie { @@ -625,3 +656,26 @@ where Self::try_from_tuple_slice(byte_str_slice).unwrap() } } + +#[cfg(feature = "databake")] +impl databake::Bake for ZeroTrie +where + Store: databake::Bake, +{ + fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream { + use databake::*; + let inner = impl_dispatch!(&self, bake(env)); + quote! { #inner.into_zerotrie() } + } +} + +#[cfg(feature = "zerofrom")] +impl<'zf, Store1, Store2> zerofrom::ZeroFrom<'zf, ZeroTrie> for ZeroTrie +where + Store2: zerofrom::ZeroFrom<'zf, Store1>, +{ + fn zero_from(other: &'zf ZeroTrie) -> Self { + use zerofrom::ZeroFrom; + impl_dispatch!(&other, ZeroFrom::zero_from()) + } +} diff --git a/experimental/zerotrie/tests/derive_test.rs b/experimental/zerotrie/tests/derive_test.rs new file mode 100644 index 00000000000..006020e4f55 --- /dev/null +++ b/experimental/zerotrie/tests/derive_test.rs @@ -0,0 +1,111 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +#![allow(non_camel_case_types, non_snake_case)] + +use zerotrie::ZeroTrie; +use zerotrie::ZeroTrieExtendedCapacity; +use zerotrie::ZeroTriePerfectHash; +use zerotrie::ZeroTrieSimpleAscii; +use zerovec::ZeroVec; + +#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))] +#[cfg_attr(feature = "zerofrom", derive(zerofrom::ZeroFrom))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = crate))] +struct DeriveTest_ZeroTrie_ZeroVec<'data> { + #[cfg_attr(feature = "serde", serde(borrow))] + _data: ZeroTrie>, +} + +#[test] +#[cfg(all(feature = "databake", feature = "alloc"))] +fn bake_ZeroTrie_ZeroVec() { + use databake::*; + extern crate std; + test_bake!( + DeriveTest_ZeroTrie_ZeroVec<'static>, + crate::DeriveTest_ZeroTrie_ZeroVec { + _data: zerotrie::ZeroTrieSimpleAscii { + store: zerovec::ZeroVec::new(), + } + .into_zerotrie() + }, + ); +} + +#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))] +#[cfg_attr(feature = "zerofrom", derive(zerofrom::ZeroFrom))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = crate))] +struct DeriveTest_ZeroTrieSimpleAscii_ZeroVec<'data> { + #[cfg_attr(feature = "serde", serde(borrow))] + _data: ZeroTrieSimpleAscii>, +} + +#[test] +#[ignore] // https://github.com/rust-lang/rust/issues/98906 +#[cfg(all(feature = "databake", feature = "alloc"))] +fn bake_ZeroTrieSimpleAscii_ZeroVec() { + use databake::*; + extern crate std; + test_bake!( + DeriveTest_ZeroTrieSimpleAscii_ZeroVec<'static>, + crate::DeriveTest_ZeroTrieSimpleAscii_ZeroVec { + _data: zerotrie::ZeroTrieSimpleAscii { + store: zerovec::ZeroVec::new(), + } + }, + ); +} + +#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))] +#[cfg_attr(feature = "zerofrom", derive(zerofrom::ZeroFrom))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = crate))] +struct DeriveTest_ZeroTriePerfectHash_ZeroVec<'data> { + #[cfg_attr(feature = "serde", serde(borrow))] + _data: ZeroTriePerfectHash>, +} + +#[test] +#[ignore] // https://github.com/rust-lang/rust/issues/98906 +#[cfg(all(feature = "databake", feature = "alloc"))] +fn bake_ZeroTriePerfectHash_ZeroVec() { + use databake::*; + extern crate std; + test_bake!( + DeriveTest_ZeroTriePerfectHash_ZeroVec<'static>, + crate::DeriveTest_ZeroTriePerfectHash_ZeroVec { + _data: zerotrie::ZeroTriePerfectHash { + store: zerovec::ZeroVec::new(), + } + }, + ); +} + +#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))] +#[cfg_attr(feature = "zerofrom", derive(zerofrom::ZeroFrom))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = crate))] +struct DeriveTest_ZeroTrieExtendedCapacity_ZeroVec<'data> { + #[cfg_attr(feature = "serde", serde(borrow))] + _data: ZeroTrieExtendedCapacity>, +} + +#[test] +#[ignore] // https://github.com/rust-lang/rust/issues/98906 +#[cfg(all(feature = "databake", feature = "alloc"))] +fn bake_ZeroTrieExtendedCapacity_ZeroVec() { + use databake::*; + extern crate std; + test_bake!( + DeriveTest_ZeroTrieExtendedCapacity_ZeroVec<'static>, + crate::DeriveTest_ZeroTrieExtendedCapacity_ZeroVec { + _data: zerotrie::ZeroTrieExtendedCapacity { + store: zerovec::ZeroVec::new(), + } + }, + ); +}