1use crate::reader;
6
7use core::borrow::Borrow;
8
9#[cfg(feature = "alloc")]
10use crate::{
11 builder::bytestr::ByteStr, builder::nonconst::ZeroTrieBuilder, error::ZeroTrieBuildError,
12};
13#[cfg(feature = "alloc")]
14use alloc::{boxed::Box, collections::BTreeMap, collections::VecDeque, string::String, vec::Vec};
15#[cfg(feature = "litemap")]
16use litemap::LiteMap;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
68pub struct ZeroTrie<Store>(pub(crate) ZeroTrieFlavor<Store>);
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub(crate) enum ZeroTrieFlavor<Store> {
72 SimpleAscii(ZeroTrieSimpleAscii<Store>),
73 PerfectHash(ZeroTriePerfectHash<Store>),
74 ExtendedCapacity(ZeroTrieExtendedCapacity<Store>),
75}
76
77#[repr(transparent)]
113#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
114#[cfg_attr(feature = "databake", derive(databake::Bake))]
115#[cfg_attr(feature = "databake", databake(path = zerotrie))]
116#[allow(clippy::exhaustive_structs)] pub struct ZeroTrieSimpleAscii<Store: ?Sized> {
118 #[doc(hidden)] pub store: Store,
120}
121
122impl<Store: ?Sized> ZeroTrieSimpleAscii<Store> {
123 fn transparent_ref_from_store(s: &Store) -> &Self {
124 unsafe {
125 core::mem::transmute(s)
127 }
128 }
129}
130
131impl<Store> ZeroTrieSimpleAscii<Store> {
132 #[inline]
134 pub const fn into_zerotrie(self) -> ZeroTrie<Store> {
135 ZeroTrie(ZeroTrieFlavor::SimpleAscii(self))
136 }
137}
138
139#[repr(transparent)]
180#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
181#[cfg_attr(feature = "databake", derive(databake::Bake))]
182#[cfg_attr(feature = "databake", databake(path = zerotrie))]
183#[allow(clippy::exhaustive_structs)] pub struct ZeroAsciiIgnoreCaseTrie<Store: ?Sized> {
185 #[doc(hidden)] pub store: Store,
187}
188
189impl<Store: ?Sized> ZeroAsciiIgnoreCaseTrie<Store> {
190 fn transparent_ref_from_store(s: &Store) -> &Self {
191 unsafe {
192 core::mem::transmute(s)
194 }
195 }
196}
197
198#[repr(transparent)]
225#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
226#[cfg_attr(feature = "databake", derive(databake::Bake))]
227#[cfg_attr(feature = "databake", databake(path = zerotrie))]
228#[allow(clippy::exhaustive_structs)] pub struct ZeroTriePerfectHash<Store: ?Sized> {
230 #[doc(hidden)] pub store: Store,
232}
233
234impl<Store: ?Sized> ZeroTriePerfectHash<Store> {
235 fn transparent_ref_from_store(s: &Store) -> &Self {
236 unsafe {
237 core::mem::transmute(s)
239 }
240 }
241}
242
243impl<Store> ZeroTriePerfectHash<Store> {
244 #[inline]
246 pub const fn into_zerotrie(self) -> ZeroTrie<Store> {
247 ZeroTrie(ZeroTrieFlavor::PerfectHash(self))
248 }
249}
250
251#[repr(transparent)]
255#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
256#[cfg_attr(feature = "databake", derive(databake::Bake))]
257#[cfg_attr(feature = "databake", databake(path = zerotrie))]
258#[allow(clippy::exhaustive_structs)] pub struct ZeroTrieExtendedCapacity<Store: ?Sized> {
260 #[doc(hidden)] pub store: Store,
262}
263
264impl<Store: ?Sized> ZeroTrieExtendedCapacity<Store> {
265 fn transparent_ref_from_store(s: &Store) -> &Self {
266 unsafe {
267 core::mem::transmute(s)
269 }
270 }
271}
272
273impl<Store> ZeroTrieExtendedCapacity<Store> {
274 #[inline]
276 pub const fn into_zerotrie(self) -> ZeroTrie<Store> {
277 ZeroTrie(ZeroTrieFlavor::ExtendedCapacity(self))
278 }
279}
280
281macro_rules! impl_zerotrie_subtype {
282 ($name:ident, $iter_element:ty, $iter_fn:path, $iter_ty:ty, $cnv_fn:path) => {
283 impl<Store> $name<Store> {
284 #[inline]
288 pub const fn from_store(store: Store) -> Self {
289 Self { store }
290 }
291 #[inline]
293 pub fn into_store(self) -> Store {
294 self.store
295 }
296 #[doc = concat!("For example, use this to change `", stringify!($name), "<Vec<u8>>` to `", stringify!($name), "<Cow<[u8]>>`.")]
299 #[doc = concat!("use zerotrie::", stringify!($name), ";")]
305 #[doc = concat!("let trie: ", stringify!($name), "<Vec<u8>> = ", stringify!($name), "::from_bytes(b\"abc\\x85\").to_owned();")]
307 #[doc = concat!("let cow: ", stringify!($name), "<Cow<[u8]>> = trie.convert_store();")]
308 pub fn convert_store<X: From<Store>>(self) -> $name<X> {
312 $name::<X>::from_store(X::from(self.store))
313 }
314 }
315 impl<Store> $name<Store>
316 where
317 Store: AsRef<[u8]> + ?Sized,
318 {
319 pub fn get<K>(&self, key: K) -> Option<usize> where K: AsRef<[u8]> {
321 reader::get_parameterized::<Self>(self.store.as_ref(), key.as_ref())
323 }
324 #[inline]
326 pub fn is_empty(&self) -> bool {
327 self.store.as_ref().is_empty()
328 }
329 #[doc = concat!("use zerotrie::", stringify!($name), ";")]
335 #[doc = concat!("let trie: &", stringify!($name), "<[u8]> = ", stringify!($name), "::from_bytes(b\"abc\\x80def\\x81\");")]
338 #[inline]
343 pub fn byte_len(&self) -> usize {
344 self.store.as_ref().len()
345 }
346 #[inline]
348 pub fn as_bytes(&self) -> &[u8] {
349 self.store.as_ref()
350 }
351 #[inline]
353 pub fn as_borrowed(&self) -> &$name<[u8]> {
354 $name::from_bytes(self.store.as_ref())
355 }
356 #[inline]
358 pub fn as_borrowed_slice(&self) -> $name<&[u8]> {
359 $name::from_store(self.store.as_ref())
360 }
361 }
362 impl<Store> AsRef<$name<[u8]>> for $name<Store>
363 where
364 Store: AsRef<[u8]> + ?Sized,
365 {
366 #[inline]
367 fn as_ref(&self) -> &$name<[u8]> {
368 self.as_borrowed()
369 }
370 }
371 #[cfg(feature = "alloc")]
372 impl<Store> $name<Store>
373 where
374 Store: AsRef<[u8]> + ?Sized,
375 {
376 #[doc = concat!("use zerotrie::", stringify!($name), ";")]
384 #[doc = concat!("let trie: &", stringify!($name), "<[u8]> = ", stringify!($name), "::from_bytes(b\"abc\\x85\");")]
386 #[doc = concat!("let owned: ", stringify!($name), "<Vec<u8>> = trie.to_owned();")]
387 #[inline]
392 pub fn to_owned(&self) -> $name<Vec<u8>> {
393 $name::from_store(
394 Vec::from(self.store.as_ref()),
395 )
396 }
397 #[doc = concat!("use zerotrie::", stringify!($name), ";")]
405 #[doc = concat!("let trie: &", stringify!($name), "<[u8]> = ", stringify!($name), "::from_bytes(b\"abc\\x80def\\x81\");")]
408 #[inline]
415 #[allow(clippy::type_complexity)]
416 pub fn iter(&self) -> $iter_ty {
417 $iter_fn(self.as_bytes())
418 }
419 }
420 impl $name<[u8]> {
421 #[inline]
425 pub fn from_bytes(trie: &[u8]) -> &Self {
426 Self::transparent_ref_from_store(trie)
427 }
428 }
429 #[cfg(feature = "alloc")]
430 impl $name<Vec<u8>> {
431 pub(crate) fn try_from_tuple_slice(items: &[(&ByteStr, usize)]) -> Result<Self, ZeroTrieBuildError> {
432 use crate::options::ZeroTrieWithOptions;
433 ZeroTrieBuilder::<VecDeque<u8>>::from_sorted_tuple_slice(
434 items,
435 Self::OPTIONS,
436 )
437 .map(|s| Self {
438 store: s.to_bytes(),
439 })
440 }
441 }
442 #[cfg(feature = "alloc")]
443 impl<'a, K> FromIterator<(K, usize)> for $name<Vec<u8>>
444 where
445 K: AsRef<[u8]>
446 {
447 fn from_iter<T: IntoIterator<Item = (K, usize)>>(iter: T) -> Self {
448 use crate::options::ZeroTrieWithOptions;
449 use crate::builder::nonconst::ZeroTrieBuilder;
450 ZeroTrieBuilder::<VecDeque<u8>>::from_bytes_iter(
451 iter,
452 Self::OPTIONS
453 )
454 .map(|s| Self {
455 store: s.to_bytes(),
456 })
457 .unwrap()
458 }
459 }
460 #[cfg(feature = "alloc")]
461 impl<'a, K> TryFrom<&'a BTreeMap<K, usize>> for $name<Vec<u8>>
462 where
463 K: Borrow<[u8]>
464 {
465 type Error = crate::error::ZeroTrieBuildError;
466 fn try_from(map: &'a BTreeMap<K, usize>) -> Result<Self, Self::Error> {
467 let tuples: Vec<(&[u8], usize)> = map
468 .iter()
469 .map(|(k, v)| (k.borrow(), *v))
470 .collect();
471 let byte_str_slice = ByteStr::from_byte_slice_with_value(&tuples);
472 Self::try_from_tuple_slice(byte_str_slice)
473 }
474 }
475 #[cfg(feature = "alloc")]
476 impl<Store> $name<Store>
477 where
478 Store: AsRef<[u8]> + ?Sized
479 {
480 #[doc = concat!("use zerotrie::", stringify!($name), ";")]
488 #[doc = concat!("let trie = ", stringify!($name), "::from_bytes(b\"abc\\x81def\\x82\");")]
491 #[doc = concat!("let recovered_trie: ", stringify!($name), "<Vec<u8>> = items")]
496 pub fn to_btreemap(&self) -> BTreeMap<$iter_element, usize> {
501 self.iter().collect()
502 }
503 #[allow(dead_code)] pub(crate) fn to_btreemap_bytes(&self) -> BTreeMap<Box<[u8]>, usize> {
505 self.iter().map(|(k, v)| ($cnv_fn(k), v)).collect()
506 }
507 }
508 #[cfg(feature = "alloc")]
509 impl<Store> From<&$name<Store>> for BTreeMap<$iter_element, usize>
510 where
511 Store: AsRef<[u8]> + ?Sized,
512 {
513 #[inline]
514 fn from(other: &$name<Store>) -> Self {
515 other.to_btreemap()
516 }
517 }
518 #[cfg(feature = "litemap")]
519 impl<'a, K, S> TryFrom<&'a LiteMap<K, usize, S>> for $name<Vec<u8>>
520 where
521 K: Borrow<[u8]>,
522 S: litemap::store::StoreIterable<'a, K, usize>,
523 {
524 type Error = crate::error::ZeroTrieBuildError;
525 fn try_from(map: &'a LiteMap<K, usize, S>) -> Result<Self, Self::Error> {
526 let tuples: Vec<(&[u8], usize)> = map
527 .iter()
528 .map(|(k, v)| (k.borrow(), *v))
529 .collect();
530 let byte_str_slice = ByteStr::from_byte_slice_with_value(&tuples);
531 Self::try_from_tuple_slice(byte_str_slice)
532 }
533 }
534 #[cfg(feature = "litemap")]
535 impl<Store> $name<Store>
536 where
537 Store: AsRef<[u8]> + ?Sized,
538 {
539 #[doc = concat!("use zerotrie::", stringify!($name), ";")]
547 #[doc = concat!("let trie = ", stringify!($name), "::from_bytes(b\"abc\\x81def\\x82\");")]
550 #[doc = concat!("let recovered_trie: ", stringify!($name), "<Vec<u8>> = items")]
555 pub fn to_litemap(&self) -> LiteMap<$iter_element, usize> {
561 self.iter().collect()
562 }
563 #[allow(dead_code)] pub(crate) fn to_litemap_bytes(&self) -> LiteMap<Box<[u8]>, usize> {
565 self.iter().map(|(k, v)| ($cnv_fn(k), v)).collect()
566 }
567 }
568 #[cfg(feature = "litemap")]
569 impl<Store> From<&$name<Store>> for LiteMap<$iter_element, usize>
570 where
571 Store: AsRef<[u8]> + ?Sized,
572 {
573 #[inline]
574 fn from(other: &$name<Store>) -> Self {
575 other.to_litemap()
576 }
577 }
578 #[cfg(feature = "litemap")]
579 impl $name<Vec<u8>>
580 {
581 #[cfg(feature = "serde")]
582 pub(crate) fn try_from_serde_litemap(items: &LiteMap<Box<ByteStr>, usize>) -> Result<Self, ZeroTrieBuildError> {
583 let lm_borrowed: LiteMap<&ByteStr, usize> = items.to_borrowed_keys();
584 Self::try_from_tuple_slice(lm_borrowed.as_slice())
585 }
586 }
587 impl Borrow<$name<[u8]>> for $name<&[u8]> {
589 #[inline]
590 fn borrow(&self) -> &$name<[u8]> {
591 self.as_borrowed()
592 }
593 }
594 #[cfg(feature = "alloc")]
596 impl Borrow<$name<[u8]>> for $name<Box<[u8]>> {
597 #[inline]
598 fn borrow(&self) -> &$name<[u8]> {
599 self.as_borrowed()
600 }
601 }
602 #[cfg(feature = "alloc")]
604 impl Borrow<$name<[u8]>> for $name<Vec<u8>> {
605 #[inline]
606 fn borrow(&self) -> &$name<[u8]> {
607 self.as_borrowed()
608 }
609 }
610 #[cfg(feature = "alloc")]
611 impl alloc::borrow::ToOwned for $name<[u8]> {
612 type Owned = $name<Box<[u8]>>;
613 #[doc = concat!("This impl allows [`", stringify!($name), "`] to be used inside of a [`Cow`](alloc::borrow::Cow).")]
614 #[doc = concat!("Note that it is also possible to use `", stringify!($name), "<ZeroVec<u8>>` for a similar result.")]
616 #[doc = concat!("use zerotrie::", stringify!($name), ";")]
624 #[doc = concat!("let trie: Cow<", stringify!($name), "<[u8]>> = Cow::Borrowed(", stringify!($name), "::from_bytes(b\"abc\\x85\"));")]
626 fn to_owned(&self) -> Self::Owned {
629 let bytes: &[u8] = self.store.as_ref();
630 $name::from_store(
631 Vec::from(bytes).into_boxed_slice(),
632 )
633 }
634 }
635 #[cfg(feature = "zerovec")]
647 unsafe impl<Store> zerovec::ule::VarULE for $name<Store>
648 where
649 Store: zerovec::ule::VarULE,
650 {
651 #[inline]
652 fn validate_bytes(bytes: &[u8]) -> Result<(), zerovec::ule::UleError> {
653 Store::validate_bytes(bytes)
654 }
655 #[inline]
656 unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
657 Self::transparent_ref_from_store(Store::from_bytes_unchecked(bytes))
659 }
660 }
661 #[cfg(feature = "zerofrom")]
662 impl<'zf, Store1, Store2> zerofrom::ZeroFrom<'zf, $name<Store1>> for $name<Store2>
663 where
664 Store2: zerofrom::ZeroFrom<'zf, Store1>,
665 {
666 #[inline]
667 fn zero_from(other: &'zf $name<Store1>) -> Self {
668 $name::from_store(zerofrom::ZeroFrom::zero_from(&other.store))
669 }
670 }
671 };
672}
673
674#[cfg(feature = "alloc")]
675fn string_to_box_u8(input: String) -> Box<[u8]> {
676 input.into_boxed_str().into_boxed_bytes()
677}
678
679#[doc(hidden)] #[cfg(feature = "alloc")]
681pub type ZeroTrieStringIterator<'a> =
682 core::iter::Map<reader::ZeroTrieIterator<'a>, fn((Vec<u8>, usize)) -> (String, usize)>;
683
684impl_zerotrie_subtype!(
685 ZeroTrieSimpleAscii,
686 String,
687 reader::get_iter_ascii_or_panic,
688 ZeroTrieStringIterator,
689 string_to_box_u8
690);
691impl_zerotrie_subtype!(
692 ZeroAsciiIgnoreCaseTrie,
693 String,
694 reader::get_iter_ascii_or_panic,
695 ZeroTrieStringIterator,
696 string_to_box_u8
697);
698impl_zerotrie_subtype!(
699 ZeroTriePerfectHash,
700 Vec<u8>,
701 reader::get_iter_phf,
702 reader::ZeroTrieIterator<'_>,
703 Vec::into_boxed_slice
704);
705impl_zerotrie_subtype!(
706 ZeroTrieExtendedCapacity,
707 Vec<u8>,
708 reader::get_iter_phf,
709 reader::ZeroTrieIterator<'_>,
710 Vec::into_boxed_slice
711);
712
713macro_rules! impl_dispatch {
714 ($self:ident, $inner_fn:ident()) => {
715 match $self.0 {
716 ZeroTrieFlavor::SimpleAscii(subtype) => subtype.$inner_fn(),
717 ZeroTrieFlavor::PerfectHash(subtype) => subtype.$inner_fn(),
718 ZeroTrieFlavor::ExtendedCapacity(subtype) => subtype.$inner_fn(),
719 }
720 };
721 ($self:ident, $inner_fn:ident().into_zerotrie()) => {
722 match $self.0 {
723 ZeroTrieFlavor::SimpleAscii(subtype) => subtype.$inner_fn().into_zerotrie(),
724 ZeroTrieFlavor::PerfectHash(subtype) => subtype.$inner_fn().into_zerotrie(),
725 ZeroTrieFlavor::ExtendedCapacity(subtype) => subtype.$inner_fn().into_zerotrie(),
726 }
727 };
728 (&$self:ident, $inner_fn:ident()) => {
729 match &$self.0 {
730 ZeroTrieFlavor::SimpleAscii(subtype) => subtype.$inner_fn(),
731 ZeroTrieFlavor::PerfectHash(subtype) => subtype.$inner_fn(),
732 ZeroTrieFlavor::ExtendedCapacity(subtype) => subtype.$inner_fn(),
733 }
734 };
735 ($self:ident, $inner_fn:ident($arg:ident)) => {
736 match $self.0 {
737 ZeroTrieFlavor::SimpleAscii(subtype) => subtype.$inner_fn($arg),
738 ZeroTrieFlavor::PerfectHash(subtype) => subtype.$inner_fn($arg),
739 ZeroTrieFlavor::ExtendedCapacity(subtype) => subtype.$inner_fn($arg),
740 }
741 };
742 (&$self:ident, $inner_fn:ident($arg:ident)) => {
743 match &$self.0 {
744 ZeroTrieFlavor::SimpleAscii(subtype) => subtype.$inner_fn($arg),
745 ZeroTrieFlavor::PerfectHash(subtype) => subtype.$inner_fn($arg),
746 ZeroTrieFlavor::ExtendedCapacity(subtype) => subtype.$inner_fn($arg),
747 }
748 };
749 (&$self:ident, $trait:ident::$inner_fn:ident()) => {
750 match &$self.0 {
751 ZeroTrieFlavor::SimpleAscii(subtype) => {
752 ZeroTrie(ZeroTrieFlavor::SimpleAscii($trait::$inner_fn(subtype)))
753 }
754 ZeroTrieFlavor::PerfectHash(subtype) => {
755 ZeroTrie(ZeroTrieFlavor::PerfectHash($trait::$inner_fn(subtype)))
756 }
757 ZeroTrieFlavor::ExtendedCapacity(subtype) => {
758 ZeroTrie(ZeroTrieFlavor::ExtendedCapacity($trait::$inner_fn(subtype)))
759 }
760 }
761 };
762}
763
764impl<Store> ZeroTrie<Store> {
765 pub fn into_store(self) -> Store {
767 impl_dispatch!(self, into_store())
768 }
769 pub fn convert_store<NewStore>(self) -> ZeroTrie<NewStore>
773 where
774 NewStore: From<Store>,
775 {
776 impl_dispatch!(self, convert_store().into_zerotrie())
777 }
778}
779
780impl<Store> ZeroTrie<Store>
781where
782 Store: AsRef<[u8]>,
783{
784 pub fn get<K>(&self, key: K) -> Option<usize>
786 where
787 K: AsRef<[u8]>,
788 {
789 impl_dispatch!(&self, get(key))
790 }
791 pub fn is_empty(&self) -> bool {
793 impl_dispatch!(&self, is_empty())
794 }
795 pub fn byte_len(&self) -> usize {
799 impl_dispatch!(&self, byte_len())
800 }
801}
802
803#[cfg(feature = "alloc")]
804impl<Store> ZeroTrie<Store>
805where
806 Store: AsRef<[u8]>,
807{
808 pub fn to_btreemap(&self) -> BTreeMap<Box<[u8]>, usize> {
810 impl_dispatch!(&self, to_btreemap_bytes())
811 }
812}
813
814#[cfg(feature = "litemap")]
815impl<Store> ZeroTrie<Store>
816where
817 Store: AsRef<[u8]>,
818{
819 pub fn to_litemap(&self) -> LiteMap<Box<[u8]>, usize> {
821 impl_dispatch!(&self, to_litemap_bytes())
822 }
823}
824
825#[cfg(feature = "alloc")]
826impl ZeroTrie<Vec<u8>> {
827 pub(crate) fn try_from_tuple_slice(
828 items: &[(&ByteStr, usize)],
829 ) -> Result<Self, ZeroTrieBuildError> {
830 let is_all_ascii = items.iter().all(|(s, _)| s.is_all_ascii());
831 if is_all_ascii && items.len() < 512 {
832 ZeroTrieSimpleAscii::try_from_tuple_slice(items).map(|x| x.into_zerotrie())
833 } else {
834 ZeroTriePerfectHash::try_from_tuple_slice(items).map(|x| x.into_zerotrie())
835 }
836 }
837}
838
839#[cfg(feature = "alloc")]
840impl<K> FromIterator<(K, usize)> for ZeroTrie<Vec<u8>>
841where
842 K: AsRef<[u8]>,
843{
844 fn from_iter<T: IntoIterator<Item = (K, usize)>>(iter: T) -> Self {
845 let items = Vec::from_iter(iter);
847 let mut items: Vec<(&[u8], usize)> = items.iter().map(|(k, v)| (k.as_ref(), *v)).collect();
848 items.sort();
849 let byte_str_slice = ByteStr::from_byte_slice_with_value(&items);
850 #[allow(clippy::unwrap_used)] Self::try_from_tuple_slice(byte_str_slice).unwrap()
852 }
853}
854
855#[cfg(feature = "databake")]
856impl<Store> databake::Bake for ZeroTrie<Store>
857where
858 Store: databake::Bake,
859{
860 fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream {
861 use databake::*;
862 let inner = impl_dispatch!(&self, bake(env));
863 quote! { #inner.into_zerotrie() }
864 }
865}
866
867#[cfg(feature = "databake")]
868impl<Store> databake::BakeSize for ZeroTrie<Store>
869where
870 Store: databake::BakeSize,
871{
872 fn borrows_size(&self) -> usize {
873 impl_dispatch!(&self, borrows_size())
874 }
875}
876
877#[cfg(feature = "zerofrom")]
878impl<'zf, Store1, Store2> zerofrom::ZeroFrom<'zf, ZeroTrie<Store1>> for ZeroTrie<Store2>
879where
880 Store2: zerofrom::ZeroFrom<'zf, Store1>,
881{
882 fn zero_from(other: &'zf ZeroTrie<Store1>) -> Self {
883 use zerofrom::ZeroFrom;
884 impl_dispatch!(&other, ZeroFrom::zero_from())
885 }
886}