Skip to main content

arctic/raw/key/
unsized.rs

1//! Support for dynamically sized keys.
2
3use core::fmt;
4use core::fmt::Debug;
5use core::fmt::Display;
6use core::hash::Hash;
7
8pub(crate) mod boxed_slice;
9pub(crate) mod slice;
10
11/// An invariant of `[u8]` that is sufficient to guarantee the
12/// prefix property (no key is a prefix of another key).
13///
14/// # Safety
15///
16/// Caller must ensure that if `validate` returns `Ok(())`,
17/// then `key` satisfies the prefix property.
18pub unsafe trait Invariant:
19    Debug + Default + Hash + Eq + Ord + Send + Sync + 'static
20{
21    /// Validation error.
22    type Error: core::error::Error;
23
24    /// Implementation detail: some invariants append
25    /// a logical terminator byte to the end of each key.
26    #[expect(private_bounds)]
27    type Terminate: Terminate;
28
29    /// Returns `Ok(())` if and only if `key` satisfies this invariant.
30    fn validate(key: &[u8]) -> Result<(), Self::Error>;
31}
32
33/// [`Invariant`] ZST indicating this key does not contain any null bytes.
34///
35/// Allows a null byte to be internally appended to each key,
36/// which guarantees the prefix property and does not change
37/// the lexicographic ordering of the key.
38#[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
39pub struct NonNull;
40
41/// Index at which a null byte was found within a key.
42#[derive(Clone, Debug)]
43pub struct NonNullError(usize);
44
45impl core::error::Error for NonNullError {}
46
47impl Display for NonNullError {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        write!(f, "Null byte at index ")?;
50        Display::fmt(&self.0, f)
51    }
52}
53
54// SAFETY: a non-null key that appends a null byte terminator
55// satisfies the precondition property.
56unsafe impl Invariant for NonNull {
57    type Error = NonNullError;
58    type Terminate = bool;
59
60    fn validate(key: &[u8]) -> Result<(), Self::Error> {
61        if let Some(index) = key.iter().position(|byte| *byte == 0) {
62            return Err(NonNullError(index));
63        }
64
65        Ok(())
66    }
67}
68
69/// [`Invariant`] ZST indicating this key contains exactly one
70/// `TERMINATOR` byte at the end of the key.
71///
72/// Implies the prefix property.
73#[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
74pub struct Terminated<const TERMINATOR: u8>;
75
76/// Information about why a key does not satisfy the [`Terminated`] invariant.
77#[derive(Clone, Debug)]
78pub enum TerminatedError {
79    /// Terminator byte is missing from key.
80    Missing,
81    /// Terminator byte was found before end of key.
82    Internal(usize),
83}
84
85impl core::error::Error for TerminatedError {}
86
87impl Display for TerminatedError {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        match self {
90            Self::Missing => write!(f, "Missing terminator byte"),
91            Self::Internal(index) => {
92                write!(f, "Internal terminator byte at index {index}")
93            }
94        }
95    }
96}
97
98// SAFETY: a key that ends in a terminator satisfies the precondition property.
99unsafe impl<const TERMINATOR: u8> Invariant for Terminated<TERMINATOR> {
100    type Error = TerminatedError;
101    type Terminate = ();
102
103    fn validate(key: &[u8]) -> Result<(), Self::Error> {
104        match key.iter().position(|byte| *byte == TERMINATOR) {
105            None => Err(TerminatedError::Missing),
106            Some(index) if index < key.len() - 1 => Err(TerminatedError::Internal(index)),
107            Some(_) => Ok(()),
108        }
109    }
110}
111
112// TODO: optimize?
113#[inline]
114fn common_prefix(left: &[u8], right: &[u8]) -> usize {
115    core::iter::zip(left, right)
116        .position(|(l, r)| l != r)
117        .unwrap_or_else(|| left.len().min(right.len()))
118}
119
120#[inline]
121fn read_u64(slice: &[u8]) -> u64 {
122    if slice.len() >= 8 {
123        cfg_select! {
124            // Avoid memcpy (just x86 for now):
125            any(target_arch = "x86", target_arch = "x86_64") => {
126                // - https://rust-lang.github.io/rfcs/1725-unaligned-access.html#detailed-design
127                // - https://github.com/llvm/llvm-project/issues/87440
128                // - https://github.com/rust-lang/rust/issues/92993
129                // - https://github.com/rust-lang/rust/pull/37573
130                // - https://lemire.me/blog/2012/05/31/data-alignment-for-speed-myth-or-reality/
131                let buffer: u64;
132                unsafe {
133                    core::arch::asm! {
134                        "mov {}, [{}]",
135                        out(reg) buffer,
136                        in(reg) slice.as_ptr().cast::<u64>(),
137                        options(pure, readonly, preserves_flags, nostack)
138                    }
139                }
140                buffer
141            }
142            _ => unsafe {
143                slice.as_ptr().cast::<u64>().read_unaligned()
144            }
145        }
146    } else {
147        let mut buffer = [0u8; 8];
148        buffer[..slice.len()].copy_from_slice(slice);
149        u64::from_le_bytes(buffer)
150    }
151}
152
153mod seal {
154    pub trait Seal {}
155}
156
157pub(crate) trait Terminate:
158    Debug + Default + Eq + ribbit::Pack<Packed = Self> + Send + Sync + 'static + seal::Seal
159{
160    const FALSE: Self;
161    const TRUE: Self;
162
163    fn new(terminate: bool) -> Self;
164    fn get(self) -> bool;
165
166    fn try_compress(byte: u8) -> usize;
167
168    fn trim(slice: &[u8]) -> &[u8];
169}
170
171impl seal::Seal for () {}
172impl Terminate for () {
173    const FALSE: Self = ();
174    const TRUE: Self = ();
175
176    #[inline]
177    fn new(_: bool) -> Self {}
178
179    #[inline]
180    fn get(self) -> bool {
181        false
182    }
183
184    #[inline]
185    fn try_compress(_: u8) -> usize {
186        1
187    }
188
189    #[inline]
190    fn trim(slice: &[u8]) -> &[u8] {
191        slice
192    }
193}
194
195impl seal::Seal for bool {}
196impl Terminate for bool {
197    const FALSE: Self = false;
198    const TRUE: Self = true;
199
200    #[inline]
201    fn new(terminate: bool) -> Self {
202        terminate
203    }
204
205    #[inline]
206    fn get(self) -> bool {
207        self
208    }
209
210    #[inline]
211    fn try_compress(byte: u8) -> usize {
212        (byte > 0) as usize
213    }
214
215    #[inline]
216    fn trim(slice: &[u8]) -> &[u8] {
217        let (last, slice) = slice.split_last().expect("Non-empty");
218        validate_eq!(*last, 0);
219        slice
220    }
221}