arctic/raw/key.rs
1//! Abstraction over various key types.
2//!
3//! Unlike comparison-based maps like [`BTreeMap`][std::collections::BTreeMap],
4//! which accept keys implementing [`PartialOrd`], or hash-based maps
5//! like [`HashMap`][std::collections::HashMap], which accept keys implementing
6//! [`Hash`][core::hash::Hash] and [`Eq`], the maps and sets in this crate are
7//! backed by a radix tree, which accepts keys represented by a sequence of bytes.
8//!
9//! We abstract over such keys with the [`Key`] trait. Our implementation additionally
10//! requires keys to satisfy the **prefix property**: no key is a prefix of another
11//! key[^1]. Fixed-size key types like integers (`u8`-`u128`) and arrays (`[u8; N]`)
12//! naturally satisfy the prefix property, but dynamically-sized key types require
13//! additional infrastructure.
14//!
15//! We establish type safety by providing wrappers for `Box<[u8]>` ([`BoxedSlice`])
16//! and `[u8]` ([`Slice`]) that are parameterized by an [`Invariant`]: currently,
17//! this can be either [`NonNull`] or [`Terminated`], which is sufficient to
18//! guarantee the prefix property.
19//!
20//! [^1]: Internally, this prevents one key prefix from mapping to both a node and a value.
21
22mod discard;
23mod len;
24mod sized;
25mod r#unsized;
26
27pub(crate) use discard::Discard;
28pub(crate) use len::Bit;
29pub(crate) use len::Byte;
30pub(crate) use len::Len;
31#[cfg_attr(not(feature = "smr-hazard"), expect(unused))]
32pub(crate) use sized::unsigned;
33pub(crate) use r#unsized::Terminate;
34pub(crate) use r#unsized::boxed_slice;
35#[cfg_attr(not(feature = "smr-hazard"), expect(unused))]
36pub(crate) use r#unsized::slice;
37
38pub use r#unsized::Invariant;
39pub use r#unsized::NonNull;
40pub use r#unsized::Terminated;
41pub use r#unsized::boxed_slice::BoxedSlice;
42pub use r#unsized::slice::Slice;
43
44/// Convenience type alias for a [`Slice`] that is backed by a [`str`].
45pub type Str<I> = Slice<I, str>;
46/// Convenience type alias for a [`BoxedSlice`] that is backed by a [`str`].
47pub type BoxedStr<I> = BoxedSlice<I, str>;
48
49use core::borrow::Borrow;
50use core::fmt;
51
52use crate::raw::edge;
53use crate::raw::edge::Meta as _;
54
55/// Byte sequence that can be stored in an adaptive radix tree.
56///
57/// Must satisfy the prefix property: no key is a prefix of any
58/// other key. Fixed-size keys (e.g., [`u64`], `[u8; N]`) trivially
59/// satisfy this property, but dynamically sized keys (slices, boxed slices)
60/// require some additional [`Invariant`][unsized::Invariant]s.
61///
62/// The following table depicts the most relevant key properties
63/// for users of this crate. Methods that can insert into the tree
64/// take `Insert<'_>`; other methods take `&'_ Borrowed`.
65/// Using the [`Iterator`] API may be expensive for dynamically
66/// allocated key types, as they need to be constructed and cloned
67/// during traversal; see [`crate::sequential::Map`] for workarounds.
68///
69/// | Key Family | Example | Insert<'_> | Borrowed | Clone in iterator? |
70/// |-------------|-------------------------------------------|-------------------------------------|---------------------------------|--------------------|
71/// | Integer | u64 | u64 | u64 | N |
72/// | Array | [u8; 5] | `&'_ [u8; 5]` | [u8; 5] | Y |
73/// | Slice | [`&'a Slice<NonNull>`][Slice] | [`&'a Slice<NonNull>`][Slice] | [`Slice<NonNull>`][Slice] | N |
74/// | Boxed Slice | [`BoxedStr<Terminated<b'\n'>>`][BoxedStr] | [`&'_ Str<Terminated<b'\n'>>`][Str] | [`Str<Terminated<b'\n'>>`][Str] | Y |
75pub trait Key: Borrow<Self::Borrowed> {
76 /// A non-allocated byte sequence that a key can be cheaply borrowed as.
77 type Borrowed: 'static + ?Sized;
78
79 /// Keys can either have edges that store inline bytes (e.g., u64, [`BoxedSlice`]),
80 /// or pointers (i.e., [`Slice`]).
81 ///
82 /// The former can take borrowed bytes with any lifetime when inserting,
83 /// but the latter can only take borrowed bytes that outlive the key type.
84 type Insert<'k>: Copy + Borrow<Self::Borrowed>
85 where
86 Self: 'k;
87
88 /// Tracks key length and allows extracting edges and slicing key bytes.
89 #[expect(private_bounds)]
90 type Read<'k>: Read<Edge = Self::Edge, Len = Self::Len> + From<&'k Self::Borrowed>;
91
92 /// Constructs a key from an initial reader prefix and sequence of bytes and edges.
93 #[expect(private_bounds)]
94 type Write: for<'k> Write<Self::Read<'k>>;
95
96 /// Edge metadata.
97 #[expect(private_bounds)]
98 type Edge: ribbit::Pack<Packed: edge::Meta> + Send + Sync;
99
100 /// Key length.
101 #[expect(private_bounds)]
102 type Len: Len + From<<ribbit::Packed<Self::Edge> as edge::Meta>::Len>;
103
104 /// Convert the key type to the insert type.
105 fn as_insert(&self) -> Self::Insert<'_>;
106
107 /// Convert the insert type to a reader with appropriate lifetime.
108 fn insert_as_read<'k>(insert: Self::Insert<'k>) -> Self::Read<'k>
109 where
110 Self: 'k;
111
112 /// Convert the insert type to the key type.
113 fn insert_to_key<'k>(insert: Self::Insert<'k>) -> Self
114 where
115 Self: 'k;
116
117 /// Convert a reference to a writer into the insert type.
118 ///
119 /// # Safety
120 ///
121 /// Caller must guarantee that `writer` contains a valid key.
122 unsafe fn write_as_insert<'k>(writer: &'k Self::Write) -> Self::Insert<'k>
123 where
124 Self: 'k;
125}
126
127/// Key types that can split off their last byte,
128/// which enables an efficient set implementation.
129pub trait Split: Key {
130 /// Split a key into a reader and the last byte.
131 fn split_last<'k>(key: &'k Self::Borrowed) -> (Self::Read<'k>, u8);
132}
133
134pub(crate) trait Read: Copy + fmt::Debug + Default + Eq {
135 // Hint for fixed-size keys
136 const LEN: Option<Self::Len>;
137
138 type Edge: ribbit::Pack<Packed: edge::Meta>;
139 type Len: Len
140 + From<<ribbit::Packed<Self::Edge> as edge::Meta>::Len>
141 + Into<<ribbit::Packed<Self::Edge> as edge::Meta>::Len>;
142
143 fn len(&self) -> Self::Len;
144
145 fn get_edge(
146 &self,
147 len: <ribbit::Packed<Self::Edge> as edge::Meta>::Len,
148 ) -> ribbit::Packed<Self::Edge>;
149
150 fn get_byte(&self, index: <ribbit::Packed<Self::Edge> as edge::Meta>::Len) -> Option<u8>;
151
152 #[inline]
153 unsafe fn get_byte_unchecked(
154 &self,
155 index: <ribbit::Packed<Self::Edge> as edge::Meta>::Len,
156 ) -> u8 {
157 match self.get_byte(index) {
158 Some(byte) => byte,
159 None => if_validate!(unreachable!(), unsafe {
160 core::hint::unreachable_unchecked()
161 }),
162 }
163 }
164
165 #[inline]
166 fn match_exact(
167 &self,
168 meta: <Self::Edge as ribbit::Pack>::Packed,
169 ) -> Option<<ribbit::Packed<Self::Edge> as edge::Meta>::Len> {
170 let len = self.match_prefix(meta);
171 (len >= meta.len().into()).then_some(meta.len())
172 }
173
174 fn match_prefix(&self, meta: <Self::Edge as ribbit::Pack>::Packed) -> Self::Len;
175
176 fn prefix(self, end: Self::Len) -> Self;
177 fn suffix(self, start: Self::Len) -> Self;
178 fn common_prefix(self, other: Self) -> Self;
179}
180
181pub(crate) trait Write<R: Read>: fmt::Debug + Default {
182 type Len: Copy + fmt::Debug;
183
184 fn new(prefix: R, key: ribbit::Packed<R::Edge>) -> (Self, Self::Len);
185
186 /// Replace bytes starting at `start` with bytes from `node` and `edge`
187 fn replace(&mut self, start: Self::Len, node: u8, edge: ribbit::Packed<R::Edge>) -> Self::Len;
188}