nybbles/
nibbles.rs

1use cfg_if::cfg_if;
2use core::{
3    cmp::{self, Ordering},
4    fmt,
5    mem::MaybeUninit,
6    ops::{Bound, Deref, RangeBounds},
7    slice,
8};
9use ruint::aliases::U256;
10use smallvec::SmallVec;
11
12#[cfg(not(feature = "nightly"))]
13#[allow(unused_imports)]
14use core::convert::{identity as likely, identity as unlikely};
15#[cfg(feature = "nightly")]
16#[allow(unused_imports)]
17use core::intrinsics::{likely, unlikely};
18
19#[cfg(not(feature = "std"))]
20use alloc::vec::Vec;
21
22/// The size of [`U256`] in nibbles.
23const NIBBLES: usize = 64;
24
25/// This array contains 65 bitmasks used in [`Nibbles::slice`].
26///
27/// Each mask is a [`U256`] where:
28/// - Index 0 is just 0 (no bits set)
29/// - Index 1 has the highest 4 bits set (one nibble)
30/// - Index 2 has the highest 8 bits set (two nibbles)
31/// - ...and so on
32/// - Index 64 has all bits set ([`U256::MAX`])
33static SLICE_MASKS: [U256; 65] = {
34    let mut masks = [U256::ZERO; 65];
35    let mut i = 0;
36    while i <= NIBBLES {
37        masks[i] = if i == 0 { U256::ZERO } else { U256::MAX.wrapping_shl((NIBBLES - i) * 4) };
38        i += 1;
39    }
40    masks
41};
42
43/// This array contains 65 values to add that are used in [`Nibbles::increment`].
44///
45/// Each value is a [`U256`] equal to `1 << ((64 - i) * 4)`.
46static INCREMENT_VALUES: [U256; 65] = {
47    let mut masks = [U256::ZERO; 65];
48    let mut i = 0;
49    while i <= NIBBLES {
50        masks[i] = U256::ONE.wrapping_shl((NIBBLES - i) * 4);
51        i += 1;
52    }
53    masks
54};
55
56/// Structure representing a sequence of nibbles.
57///
58/// A nibble is a 4-bit value, and this structure is used to store the nibble sequence representing
59/// the keys in a Merkle Patricia Trie (MPT).
60/// Using nibbles simplifies trie operations and enables consistent key representation in the MPT.
61///
62/// # Internal representation
63///
64/// The internal representation is currently a [`U256`] that stores two nibbles per byte. Nibbles
65/// are stored inline (on the stack), and can be up to a length of 64 nibbles, or 32 unpacked bytes.
66///
67/// Additionally, a separate `length` field is stored to track the actual length of the nibble
68/// sequence. When the [`U256`] is modified directly, the `length` field must be updated
69/// accordingly. Otherwise, the behavior is undefined.
70///
71/// Nibbles are stored with most significant bits set first, meaning that a nibble sequence `0x101`
72/// will be stored as `0x101...0`, and not `0x0...101`.
73///
74/// # Examples
75///
76/// ```
77/// use nybbles::Nibbles;
78///
79/// let bytes = [0xAB, 0xCD];
80/// let nibbles = Nibbles::unpack(&bytes);
81/// assert_eq!(nibbles, Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0D]));
82/// assert_eq!(nibbles.to_vec(), vec![0x0A, 0x0B, 0x0C, 0x0D]);
83///
84/// let packed = nibbles.pack();
85/// assert_eq!(&packed[..], &bytes[..]);
86/// ```
87#[repr(C)] // We want to preserve the order of fields in the memory layout.
88#[derive(Default, Clone, Copy, Hash, PartialEq, Eq)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90pub struct Nibbles {
91    /// Nibbles length.
92    // This field goes first, because the derived implementation of `PartialEq` compares the fields
93    // in order, so we can short-circuit the comparison if the `length` field differs.
94    pub(crate) length: usize,
95    /// The nibbles themselves, stored as a 256-bit unsigned integer with most significant bits set
96    /// first.
97    pub(crate) nibbles: U256,
98}
99
100impl fmt::Debug for Nibbles {
101    #[inline]
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        if self.is_empty() {
104            write!(f, "Nibbles(0x)")
105        } else {
106            let shifted = self.nibbles >> ((NIBBLES - self.len()) * 4);
107            write!(f, "Nibbles(0x{:0width$x})", shifted, width = self.len())
108        }
109    }
110}
111
112#[cfg(feature = "arbitrary")]
113impl<'a> arbitrary::Arbitrary<'a> for Nibbles {
114    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
115        let length = u.int_in_range(0..=NIBBLES)?;
116        let nibbles = Nibbles::from_nibbles_unchecked(
117            (0..length).map(|_| u.int_in_range(0..=0xf)).collect::<Result<Vec<_>, _>>()?,
118        );
119        Ok(nibbles)
120    }
121}
122
123// Deriving [`Ord`] for [`Nibbles`] is not correct, because they will be compared as unsigned
124// integers without accounting for length. This is incorrect, because `0x1` should be considered
125// greater than `0x02`.
126impl Ord for Nibbles {
127    #[inline]
128    fn cmp(&self, other: &Self) -> Ordering {
129        let self_len = self.len().div_ceil(2);
130        let other_len = other.len().div_ceil(2);
131        let l = cmp::min(self_len, other_len);
132
133        // Slice to the loop iteration range to enable bound check
134        // elimination in the compiler
135        let lhs = &as_le_slice(&self.nibbles)[U256::BYTES - l..];
136        let rhs = &as_le_slice(&other.nibbles)[U256::BYTES - l..];
137
138        for i in (0..l).rev() {
139            match lhs[i].cmp(&rhs[i]) {
140                Ordering::Equal => (),
141                non_eq => return non_eq,
142            }
143        }
144
145        self.len().cmp(&other.len())
146    }
147}
148
149impl PartialOrd for Nibbles {
150    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
151        Some(self.cmp(other))
152    }
153}
154
155impl FromIterator<u8> for Nibbles {
156    fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
157        let mut nibbles = Self::default();
158        for n in iter {
159            nibbles.push(n);
160        }
161        nibbles
162    }
163}
164
165#[cfg(feature = "rlp")]
166impl alloy_rlp::Encodable for Nibbles {
167    #[inline]
168    fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
169        alloy_rlp::Header { list: true, payload_length: self.len() }.encode(out);
170        for i in 0..self.len() {
171            self.get_unchecked(i).encode(out);
172        }
173    }
174
175    #[inline]
176    fn length(&self) -> usize {
177        let payload_length = self.len();
178        payload_length + alloy_rlp::length_of_length(payload_length)
179    }
180}
181
182#[cfg(feature = "arbitrary")]
183impl proptest::arbitrary::Arbitrary for Nibbles {
184    type Parameters = proptest::collection::SizeRange;
185    type Strategy = proptest::strategy::Map<
186        proptest::collection::VecStrategy<core::ops::RangeInclusive<u8>>,
187        fn(Vec<u8>) -> Self,
188    >;
189
190    #[inline]
191    fn arbitrary_with(size: proptest::collection::SizeRange) -> Self::Strategy {
192        use proptest::prelude::*;
193        proptest::collection::vec(0x0..=0xf, size).prop_map(Self::from_nibbles_unchecked)
194    }
195}
196
197impl Nibbles {
198    /// Creates a new empty [`Nibbles`] instance.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// # use nybbles::Nibbles;
204    /// let nibbles = Nibbles::new();
205    /// assert_eq!(nibbles.len(), 0);
206    /// ```
207    #[inline]
208    pub const fn new() -> Self {
209        Self { length: 0, nibbles: U256::ZERO }
210    }
211
212    /// Creates a new [`Nibbles`] instance from the given iterator over nibbles, without checking
213    /// their validity.
214    ///
215    /// Note that only the low nibble of every byte will be stored as a nibble, i.e. for `0x12` we
216    /// will store a nibble `2`.
217    ///
218    /// For checked version, use the [`FromIterator`] implementation.
219    ///
220    /// # Examples
221    ///
222    /// ```
223    /// # use nybbles::Nibbles;
224    /// let nibbles = Nibbles::from_iter_unchecked([0x0A, 0x0B, 0x0C, 0x0D]);
225    /// assert_eq!(nibbles.to_vec(), vec![0x0A, 0x0B, 0x0C, 0x0D]);
226    pub fn from_iter_unchecked<I>(iter: I) -> Self
227    where
228        I: IntoIterator<Item = u8>,
229    {
230        let mut packed = Self::default();
231        for n in iter {
232            packed.push_unchecked(n);
233        }
234        packed
235    }
236
237    /// Creates a new [`Nibbles`] instance from the given nibbles.
238    ///
239    /// # Panics
240    ///
241    /// Panics if the any of the bytes is not a valid nibble (`0..=0x0f`).
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// # use nybbles::Nibbles;
247    /// let nibbles = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0D]);
248    /// assert_eq!(nibbles.to_vec(), vec![0x0A, 0x0B, 0x0C, 0x0D]);
249    /// ```
250    ///
251    /// Invalid values will cause panics:
252    ///
253    /// ```should_panic
254    /// # use nybbles::Nibbles;
255    /// let nibbles = Nibbles::from_nibbles(&[0xFF]);
256    /// ```
257    #[inline]
258    #[track_caller]
259    pub fn from_nibbles<T: AsRef<[u8]>>(nibbles: T) -> Self {
260        let bytes = nibbles.as_ref();
261        check_nibbles(bytes);
262        Self::from_iter_unchecked(bytes.iter().copied())
263    }
264
265    /// Creates a new [`Nibbles`] instance from the given nibbles.
266    ///
267    /// Note that only the low nibble of every byte will be stored as a nibble, i.e. for `0x12` we
268    /// will store a nibble `2`.
269    ///
270    /// For checked version, use [`Nibbles::from_nibbles`].
271    ///
272    /// # Examples
273    ///
274    /// ```
275    /// # use nybbles::Nibbles;
276    /// let nibbles = Nibbles::from_nibbles_unchecked(&[0x0A, 0x0B, 0x0C, 0x0D]);
277    /// assert_eq!(nibbles.to_vec(), vec![0x0A, 0x0B, 0x0C, 0x0D]);
278    /// ```
279    ///
280    /// Invalid values will not cause panics:
281    ///
282    /// ```
283    /// # use nybbles::Nibbles;
284    /// let nibbles = Nibbles::from_nibbles_unchecked(&[0xFF]);
285    /// assert_eq!(nibbles.to_vec(), vec![0x0F]);
286    /// ```
287    #[inline]
288    pub fn from_nibbles_unchecked<T: AsRef<[u8]>>(nibbles: T) -> Self {
289        Self::from_iter_unchecked(nibbles.as_ref().iter().copied())
290    }
291
292    /// Converts a byte slice into a [`Nibbles`] instance containing the nibbles (half-bytes or 4
293    /// bits) that make up the input byte data.
294    ///
295    /// # Panics
296    ///
297    /// Panics if the length of the input is greater than 32 bytes.
298    ///
299    /// # Examples
300    ///
301    /// ```
302    /// # use nybbles::Nibbles;
303    /// let nibbles = Nibbles::unpack(&[0xAB, 0xCD]);
304    /// assert_eq!(nibbles.to_vec(), vec![0x0A, 0x0B, 0x0C, 0x0D]);
305    /// ```
306    ///
307    /// ```should_panic
308    /// # use nybbles::Nibbles;
309    /// let nibbles = Nibbles::unpack(&[0xAB; 33]);
310    /// ```
311    #[inline]
312    #[track_caller]
313    pub fn unpack(data: impl AsRef<[u8]>) -> Self {
314        assert!(data.as_ref().len() <= U256::BYTES);
315        // SAFETY: we checked that the length is less than or equal to the size of U256
316        unsafe { Self::unpack_unchecked(data.as_ref()) }
317    }
318
319    /// Converts a byte slice into a [`Nibbles`] instance containing the nibbles (half-bytes or 4
320    /// bits) that make up the input byte data.
321    ///
322    /// # Safety
323    ///
324    /// The caller must ensure that the length of the input is less than or equal to the size of
325    /// U256, which is 32 bytes.
326    ///
327    /// # Examples
328    ///
329    /// ```
330    /// # use nybbles::Nibbles;
331    /// // SAFETY: the length of the input is less than 32 bytes.
332    /// let nibbles = unsafe { Nibbles::unpack_unchecked(&[0xAB, 0xCD]) };
333    /// assert_eq!(nibbles.to_vec(), vec![0x0A, 0x0B, 0x0C, 0x0D]);
334    /// ```
335    pub unsafe fn unpack_unchecked(data: &[u8]) -> Self {
336        let length = data.len() * 2;
337        debug_assert!(length <= NIBBLES);
338
339        cfg_if! {
340            if #[cfg(target_endian = "little")] {
341                let mut nibbles = U256::ZERO;
342                let nibbles_slice = nibbles.as_le_slice_mut();
343            } else {
344                let mut nibbles_slice = [0u8; 32];
345            }
346        }
347
348        // Source pointer is at the beginning
349        let mut src = data.as_ptr().cast::<u8>();
350        // Move destination pointer to the end of the little endian slice
351        let mut dst = nibbles_slice.as_mut_ptr().add(U256::BYTES);
352        // On each iteration, decrement the destination pointer by one, set the destination
353        // byte, and increment the source pointer by one
354        for _ in 0..data.len() {
355            dst = dst.sub(1);
356            *dst = *src;
357            src = src.add(1);
358        }
359
360        cfg_if! {
361            if #[cfg(target_endian = "big")] {
362                let nibbles = U256::from_le_bytes(nibbles_slice);
363            }
364        }
365
366        Self { length, nibbles }
367    }
368
369    /// Packs the nibbles into the given slice.
370    ///
371    /// This method combines each pair of consecutive nibbles into a single byte,
372    /// effectively reducing the size of the data by a factor of two.
373    ///
374    /// If the number of nibbles is odd, the last nibble is shifted left by 4 bits and
375    /// added to the packed byte vector.
376    ///
377    /// # Examples
378    ///
379    /// ```
380    /// # use nybbles::Nibbles;
381    /// let nibbles = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C]);
382    /// assert_eq!(nibbles.pack()[..], [0xAB, 0xC0]);
383    /// ```
384    #[inline]
385    pub fn pack(&self) -> SmallVec<[u8; 32]> {
386        let packed_len = self.len().div_ceil(2);
387        unsafe { smallvec_with(packed_len, |out| self.pack_to_slice_unchecked(out)) }
388    }
389
390    /// Packs the nibbles into the given slice.
391    ///
392    /// See [`pack`](Self::pack) for more information.
393    ///
394    /// # Panics
395    ///
396    /// Panics if the slice is not at least `(self.len() + 1) / 2` bytes long.
397    ///
398    /// # Examples
399    ///
400    /// ```
401    /// # use nybbles::Nibbles;
402    /// let nibbles = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C]);
403    /// let mut packed = [0; 2];
404    /// nibbles.pack_to(&mut packed);
405    /// assert_eq!(packed[..], [0xAB, 0xC0]);
406    /// ```
407    #[inline]
408    #[track_caller]
409    pub fn pack_to(&self, out: &mut [u8]) {
410        assert!(out.len() >= self.len().div_ceil(2));
411        // SAFETY: asserted length.
412        unsafe { self.pack_to_unchecked(out.as_mut_ptr()) }
413    }
414
415    /// Packs the nibbles into the given pointer.
416    ///
417    /// See [`pack`](Self::pack) for more information.
418    ///
419    /// # Safety
420    ///
421    /// `ptr` must be valid for at least `(self.len() + 1) / 2` bytes.
422    ///
423    /// # Examples
424    ///
425    /// ```
426    /// # use nybbles::Nibbles;
427    /// let nibbles = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0D]);
428    /// let mut packed = [0; 2];
429    /// // SAFETY: enough capacity.
430    /// unsafe { nibbles.pack_to_unchecked(packed.as_mut_ptr()) };
431    /// assert_eq!(packed[..], [0xAB, 0xCD]);
432    /// ```
433    #[inline]
434    pub unsafe fn pack_to_unchecked(&self, ptr: *mut u8) {
435        let slice = slice::from_raw_parts_mut(ptr.cast(), self.len().div_ceil(2));
436        pack_to_unchecked(self, slice);
437    }
438
439    /// Packs the nibbles into the given slice without checking its length.
440    ///
441    /// See [`pack`](Self::pack) for more information.
442    ///
443    /// # Safety
444    ///
445    /// `out` must be valid for at least `(self.len() + 1) / 2` bytes.
446    #[inline]
447    pub unsafe fn pack_to_slice_unchecked(&self, out: &mut [MaybeUninit<u8>]) {
448        pack_to_unchecked(self, out)
449    }
450
451    /// Converts the nibbles into a vector of nibbles.
452    ///
453    /// # Examples
454    ///
455    /// ```
456    /// # use nybbles::Nibbles;
457    /// let nibbles = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0D]);
458    /// assert_eq!(nibbles.to_vec(), vec![0x0A, 0x0B, 0x0C, 0x0D]);
459    /// ```
460    pub fn to_vec(&self) -> Vec<u8> {
461        self.iter().collect()
462    }
463
464    /// Returns an iterator over the nibbles.
465    ///
466    /// # Examples
467    ///
468    /// ```
469    /// # use nybbles::Nibbles;
470    /// let nibbles = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0D]);
471    /// let collected: Vec<u8> = nibbles.iter().collect();
472    /// assert_eq!(collected, vec![0x0A, 0x0B, 0x0C, 0x0D]);
473    /// ```
474    #[inline]
475    pub const fn iter(&self) -> NibblesIter<'_> {
476        NibblesIter { current: 0, nibbles: self }
477    }
478
479    /// Gets the byte at the given index by combining two consecutive nibbles.
480    ///
481    /// # Examples
482    ///
483    /// ```
484    /// # use nybbles::Nibbles;
485    /// let nibbles = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0D]);
486    /// assert_eq!(nibbles.get_byte(0), Some(0xAB));
487    /// assert_eq!(nibbles.get_byte(1), Some(0xBC));
488    /// assert_eq!(nibbles.get_byte(2), Some(0xCD));
489    /// assert_eq!(nibbles.get_byte(3), None);
490    /// ```
491    pub fn get_byte(&self, i: usize) -> Option<u8> {
492        if likely((i < usize::MAX) & self.check_index(i.wrapping_add(1))) {
493            Some(self.get_byte_unchecked(i))
494        } else {
495            None
496        }
497    }
498
499    /// Gets the byte at the given index by combining two consecutive nibbles.
500    ///
501    /// # Panics
502    ///
503    /// Panics if `i..i + 1` is out of bounds.
504    ///
505    /// # Examples
506    ///
507    /// ```
508    /// # use nybbles::Nibbles;
509    /// let nibbles = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0D]);
510    /// // SAFETY: in range.
511    /// unsafe {
512    ///     assert_eq!(nibbles.get_byte_unchecked(0), 0xAB);
513    ///     assert_eq!(nibbles.get_byte_unchecked(1), 0xBC);
514    ///     assert_eq!(nibbles.get_byte_unchecked(2), 0xCD);
515    /// }
516    /// ```
517    #[inline]
518    #[track_caller]
519    pub fn get_byte_unchecked(&self, i: usize) -> u8 {
520        self.assert_index(i);
521        if i % 2 == 0 {
522            as_le_slice(&self.nibbles)[U256::BYTES - i / 2 - 1]
523        } else {
524            self.get_unchecked(i) << 4 | self.get_unchecked(i + 1)
525        }
526    }
527
528    /// Increments the nibble sequence by one.
529    #[inline]
530    pub fn increment(&self) -> Option<Self> {
531        if self.is_empty() || self.nibbles == SLICE_MASKS[self.len()] {
532            return None;
533        }
534
535        let mut incremented = *self;
536        let add = INCREMENT_VALUES[self.len()];
537        incremented.nibbles += add;
538        Some(incremented)
539    }
540
541    /// The last element of the hex vector is used to determine whether the nibble sequence
542    /// represents a leaf or an extension node. If the last element is 0x10 (16), then it's a leaf.
543    #[inline]
544    pub fn is_leaf(&self) -> bool {
545        self.last() == Some(16)
546    }
547
548    /// Returns `true` if this nibble sequence starts with the given prefix.
549    pub fn starts_with(&self, other: &Self) -> bool {
550        // Fast path: if lengths don't allow prefix, return false
551        if other.len() > self.len() {
552            return false;
553        }
554
555        // Fast path: empty prefix always matches
556        if other.is_empty() {
557            return true;
558        }
559
560        // Direct comparison using masks
561        let mask = SLICE_MASKS[other.len()];
562        (self.nibbles & mask) == other.nibbles
563    }
564
565    /// Returns `true` if this nibble sequence ends with the given suffix.
566    pub fn ends_with(&self, other: &Self) -> bool {
567        // If other is empty, it's a suffix of any sequence
568        if other.is_empty() {
569            return true;
570        }
571
572        // If other is longer than self, it can't be a suffix
573        if other.len() > self.len() {
574            return false;
575        }
576
577        // Fast path for even-even and odd-odd sequences
578        if self.len() % 2 == other.len() % 2 {
579            return as_le_slice(&self.nibbles)
580                [(NIBBLES - self.len()) / 2..(NIBBLES - self.len() + other.len()) / 2]
581                == as_le_slice(&other.nibbles)[(NIBBLES - other.len()) / 2..];
582        }
583
584        let mut i = 0;
585        while i < other.len() {
586            if self.get_unchecked(self.len() - i - 1) != other.get_unchecked(other.len() - i - 1) {
587                return false;
588            }
589            i += 1;
590        }
591
592        true
593    }
594
595    /// Returns the nibble at the given index.
596    #[inline]
597    pub fn get(&self, i: usize) -> Option<u8> {
598        if self.check_index(i) {
599            Some(self.get_unchecked(i))
600        } else {
601            None
602        }
603    }
604
605    /// Returns the nibble at the given index.
606    ///
607    /// # Panics
608    ///
609    /// Panics if the index is out of bounds.
610    #[inline]
611    #[track_caller]
612    pub fn get_unchecked(&self, i: usize) -> u8 {
613        self.assert_index(i);
614        let byte = as_le_slice(&self.nibbles)[U256::BYTES - i / 2 - 1];
615        if i % 2 == 0 {
616            byte >> 4
617        } else {
618            byte & 0x0F
619        }
620    }
621
622    /// Sets the nibble at the given index.
623    ///
624    /// # Panics
625    ///
626    /// Panics if the index is out of bounds, or if `value` is not a valid nibble (`0..=0x0f`).
627    #[inline]
628    #[track_caller]
629    pub fn set_at(&mut self, i: usize, value: u8) {
630        assert!(self.check_index(i) && value <= 0xf);
631        // SAFETY: index is checked above
632        unsafe { self.set_at_unchecked(i, value) };
633    }
634
635    /// Sets the nibble at the given index, without checking its validity.
636    ///
637    /// # Safety
638    ///
639    /// The caller must ensure that the index is within bounds.
640    #[inline]
641    pub unsafe fn set_at_unchecked(&mut self, i: usize, value: u8) {
642        let byte_index = U256::BYTES - i / 2 - 1;
643
644        // SAFETY: index checked above
645        cfg_if! {
646            if #[cfg(target_endian = "little")] {
647                let byte = unsafe { &mut self.nibbles.as_le_slice_mut()[byte_index] };
648            } else {
649                // Big-endian targets must first copy the nibbles to a little-endian slice.
650                // Underneath the hood, `as_le_slice` will perform a stack copy, and we
651                // replace the underlying `nibbles` after we've updated the given nibble.
652                let mut le_copy = as_le_slice(&self.nibbles);
653                let byte = &mut le_copy.to_mut()[byte_index];
654            }
655        }
656
657        if i % 2 == 0 {
658            *byte = *byte & 0x0f | value << 4;
659        } else {
660            *byte = *byte & 0xf0 | value;
661        }
662
663        // For big-endian targets, replace the underlying U256 with the mutated LE slice.
664        #[cfg(target_endian = "big")]
665        {
666            self.nibbles = U256::from_le_slice(&le_copy);
667        }
668    }
669
670    /// Returns the first nibble of this nibble sequence.
671    #[inline]
672    pub fn first(&self) -> Option<u8> {
673        self.get(0)
674    }
675
676    /// Returns the last nibble of this nibble sequence.
677    #[inline]
678    pub fn last(&self) -> Option<u8> {
679        let len = self.len();
680        if len == 0 {
681            None
682        } else {
683            Some(self.get_unchecked(len - 1))
684        }
685    }
686
687    /// Returns the length of the common prefix between this nibble sequence and the given.
688    ///
689    /// # Examples
690    ///
691    /// ```
692    /// # use nybbles::Nibbles;
693    /// let a = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]);
694    /// let b = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0E]);
695    /// assert_eq!(a.common_prefix_length(&b), 3);
696    /// ```
697    pub fn common_prefix_length(&self, other: &Self) -> usize {
698        // Handle empty cases
699        if self.is_empty() || other.is_empty() {
700            return 0;
701        }
702
703        let min_nibble_len = self.len().min(other.len());
704
705        // Fast path for small sequences that fit in one u64 limb
706        if min_nibble_len <= 16 {
707            // Extract the highest u64 limb which contains all the nibbles
708            let self_limb = self.nibbles.as_limbs()[3];
709            let other_limb = other.nibbles.as_limbs()[3];
710
711            // Create mask for the nibbles we care about
712            let mask = u64::MAX << ((16 - min_nibble_len) * 4);
713            let xor = (self_limb ^ other_limb) & mask;
714
715            if xor == 0 {
716                return min_nibble_len;
717            } else {
718                return xor.leading_zeros() as usize / 4;
719            }
720        }
721
722        let xor = if min_nibble_len == NIBBLES && self.len() == other.len() {
723            // No need to mask for 64 nibble sequences, just XOR
724            self.nibbles ^ other.nibbles
725        } else {
726            // For other lengths, mask the nibbles we care about, and then XOR
727            let mask = SLICE_MASKS[min_nibble_len];
728            (self.nibbles ^ other.nibbles) & mask
729        };
730
731        if xor == U256::ZERO {
732            min_nibble_len
733        } else {
734            xor.leading_zeros() / 4
735        }
736    }
737
738    /// Returns the total number of bits in this [`Nibbles`].
739    #[inline]
740    const fn bit_len(&self) -> usize {
741        self.len() * 4
742    }
743
744    /// Returns `true` if this [`Nibbles`] is empty.
745    #[inline]
746    pub const fn is_empty(&self) -> bool {
747        self.len() == 0
748    }
749
750    /// Returns the total number of nibbles in this [`Nibbles`].
751    #[inline]
752    pub const fn len(&self) -> usize {
753        let len = self.length;
754        debug_assert!(len <= 64);
755        unsafe { core::hint::assert_unchecked(len <= 64) };
756        len
757    }
758
759    /// Returns a mutable reference to the underlying [`U256`].
760    ///
761    /// Note that it is possible to create invalid [`Nibbles`] instances using this method. See
762    /// [the type docs](Self) for more details.
763    #[inline]
764    pub fn as_mut_uint_unchecked(&mut self) -> &mut U256 {
765        &mut self.nibbles
766    }
767
768    /// Creates new nibbles containing the nibbles in the specified range `[start, end)`
769    /// without checking bounds.
770    ///
771    /// # Safety
772    ///
773    /// This method does not verify that the provided range is valid for this nibble sequence.
774    /// The caller must ensure that `start <= end` and `end <= self.len()`.
775    #[inline]
776    pub fn slice_unchecked(&self, start: usize, end: usize) -> Self {
777        // Fast path for empty slice
778        if end == 0 || end <= start {
779            return Self::new();
780        }
781
782        // Fast path for full slice
783        let slice_to_end = end == self.len();
784        if start == 0 && slice_to_end {
785            return *self;
786        }
787
788        let nibble_len = end - start;
789
790        // Optimize for common case where start == 0
791        let nibbles = if start == 0 {
792            // When slicing from the beginning, we can just apply the mask and avoid XORing
793            self.nibbles & SLICE_MASKS[end]
794        } else {
795            // For middle and to_end cases, always shift first
796            let shifted = self.nibbles << (start * 4);
797            if slice_to_end {
798                // When slicing to the end, no mask needed after shift
799                shifted
800            } else {
801                // For middle slices, apply end mask after shift
802                shifted & SLICE_MASKS[end - start]
803            }
804        };
805
806        Self { length: nibble_len, nibbles }
807    }
808
809    /// Creates new nibbles containing the nibbles in the specified range.
810    ///
811    /// # Panics
812    ///
813    /// This method will panic if the range is out of bounds for this nibble sequence.
814    pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
815        // Determine the start and end nibble indices from the range bounds
816        let start = match range.start_bound() {
817            Bound::Included(&idx) => idx,
818            Bound::Excluded(&idx) => idx + 1,
819            Bound::Unbounded => 0,
820        };
821        let end = match range.end_bound() {
822            Bound::Included(&idx) => idx + 1,
823            Bound::Excluded(&idx) => idx,
824            Bound::Unbounded => self.len(),
825        };
826        assert!(start <= end, "Cannot slice with a start index greater than the end index");
827        assert!(
828            end <= self.len(),
829            "Cannot slice with an end index greater than the length of the nibbles"
830        );
831
832        self.slice_unchecked(start, end)
833    }
834
835    /// Join two nibble sequences together.
836    #[inline]
837    pub fn join(&self, other: &Self) -> Self {
838        let mut new = *self;
839        new.extend(other);
840        new
841    }
842
843    /// Pushes a nibble to the end of the current nibbles.
844    ///
845    /// # Panics
846    ///
847    /// Panics if the nibble is not a valid nibble (`0..=0x0f`).
848    #[inline]
849    #[track_caller]
850    pub fn push(&mut self, nibble: u8) {
851        assert!(nibble <= 0xf);
852        self.push_unchecked(nibble);
853    }
854
855    /// Pushes a nibble to the end of the current nibbles without checking its validity.
856    ///
857    /// Note that only the low nibble of the byte is used. For example, for byte `0x12`, only the
858    /// nibble `0x2` is pushed.
859    #[inline]
860    pub fn push_unchecked(&mut self, nibble: u8) {
861        let nibble_val = (nibble & 0x0F) as u64;
862        if nibble_val == 0 {
863            // Nothing to do, limb nibbles are already set to zero by default
864            self.length += 1;
865            return;
866        }
867
868        let bit_pos = (NIBBLES - self.len() - 1) * 4;
869        let limb_idx = bit_pos / 64;
870        let shift_in_limb = bit_pos % 64;
871
872        // SAFETY: limb_idx is always valid because bit_pos < 256
873        unsafe {
874            let limbs = self.nibbles.as_limbs_mut();
875            limbs[limb_idx] |= nibble_val << shift_in_limb;
876        }
877
878        self.length += 1;
879    }
880
881    /// Pops a nibble from the end of the current nibbles.
882    pub fn pop(&mut self) -> Option<u8> {
883        if self.is_empty() {
884            return None;
885        }
886
887        // The last nibble is at bit position (64 - length) * 4 from the MSB
888        let shift = (NIBBLES - self.len()) * 4;
889
890        // Extract the nibble - after shifting right, it's in the lowest bits of limb 0
891        let nibble = (self.nibbles >> shift).as_limbs()[0] as u8 & 0xF;
892
893        // Clear the nibble using a more efficient mask creation
894        // Instead of U256::from(0xF_u8) << shift, we can create the mask directly
895        let mask_limb_idx = shift / 64;
896        let mask_shift = shift % 64;
897
898        if mask_limb_idx < 4 {
899            // SAFETY: We know the limb index is valid
900            unsafe {
901                let limbs = self.nibbles.as_limbs_mut();
902                limbs[mask_limb_idx] &= !(0xF << mask_shift);
903            }
904        }
905
906        self.length -= 1;
907        Some(nibble)
908    }
909
910    /// Extend the current nibbles with another nibbles.
911    pub fn extend(&mut self, other: &Nibbles) {
912        if other.is_empty() {
913            return;
914        }
915
916        self.nibbles |= other.nibbles >> self.bit_len();
917        self.length += other.length;
918    }
919
920    /// Extend the current nibbles with another byte slice.
921    pub fn extend_from_slice(&mut self, other: &[u8]) {
922        assert!(
923            self.length + other.len() * 2 <= NIBBLES,
924            "Cannot extend: resulting length would exceed maximum capacity"
925        );
926        self.extend_from_slice_unchecked(other);
927    }
928
929    /// Extend the current nibbles with another byte slice.
930    ///
931    /// # Caution
932    ///
933    /// This method does not check if the resulting length would exceed the maximum capacity of
934    /// [`Nibbles`].
935    pub fn extend_from_slice_unchecked(&mut self, other: &[u8]) {
936        if other.is_empty() {
937            return;
938        }
939
940        let len_bytes = other.len();
941        let mut other = U256::from_be_slice(other);
942        if len_bytes > 0 {
943            other <<= (U256::BYTES - len_bytes) * 8;
944        }
945        self.nibbles |= other >> self.bit_len();
946        self.length += len_bytes * 2;
947    }
948
949    /// Truncates the current nibbles to the given length.
950    #[inline]
951    pub fn truncate(&mut self, new_len: usize) {
952        assert!(
953            new_len <= self.len(),
954            "Cannot truncate to a length greater than the current length"
955        );
956        *self = self.slice_unchecked(0, new_len);
957    }
958
959    /// Clears the current nibbles.
960    #[inline]
961    pub fn clear(&mut self) {
962        *self = Self::new();
963    }
964
965    /// Checks if the given index is within the bounds of the current nibbles.
966    #[inline]
967    const fn check_index(&self, i: usize) -> bool {
968        i < self.len()
969    }
970
971    /// Panics if the given index is out of bounds of the current nibbles.
972    #[inline]
973    fn assert_index(&self, i: usize) {
974        let len = self.len();
975        if i >= len {
976            panic_invalid_index(len, i);
977        }
978    }
979}
980
981/// Iterator over individual nibbles within a [`Nibbles`] structure.
982///
983/// This iterator provides efficient access to each nibble in sequence,
984/// using unchecked access for performance.
985///
986/// # Examples
987///
988/// ```
989/// # use nybbles::Nibbles;
990/// let nibbles = Nibbles::from_nibbles(&[0x0A, 0x0B, 0x0C, 0x0D]);
991/// let collected: Vec<u8> = nibbles.iter().collect();
992/// assert_eq!(collected, vec![0x0A, 0x0B, 0x0C, 0x0D]);
993/// ```
994#[derive(Debug, Clone)]
995pub struct NibblesIter<'a> {
996    /// Current position in the iteration.
997    current: usize,
998    /// Reference to the nibbles being iterated over.
999    nibbles: &'a Nibbles,
1000}
1001
1002impl<'a> Iterator for NibblesIter<'a> {
1003    type Item = u8;
1004
1005    #[inline]
1006    fn next(&mut self) -> Option<Self::Item> {
1007        self.nibbles.get(self.current).inspect(|_| self.current += 1)
1008    }
1009
1010    #[inline]
1011    fn size_hint(&self) -> (usize, Option<usize>) {
1012        let len = self.len();
1013        (len, Some(len))
1014    }
1015}
1016
1017impl<'a> ExactSizeIterator for NibblesIter<'a> {
1018    #[inline]
1019    fn len(&self) -> usize {
1020        self.nibbles.len() - self.current
1021    }
1022}
1023
1024/// Packs the nibbles into the given slice without checking its length.
1025///
1026/// # Safety
1027///
1028/// `out` must be valid for at least `(self.len() + 1) / 2` bytes.
1029#[inline]
1030unsafe fn pack_to_unchecked(nibbles: &Nibbles, out: &mut [MaybeUninit<u8>]) {
1031    let byte_len = nibbles.len().div_ceil(2);
1032    debug_assert!(out.len() >= byte_len);
1033    // Move source pointer to the end of the little endian slice
1034    let mut src = as_le_slice(&nibbles.nibbles).as_ptr().add(U256::BYTES);
1035    // Destination pointer is at the beginning of the output slice
1036    let mut dst = out.as_mut_ptr().cast::<u8>();
1037    // On each iteration, decrement the source pointer by one, set the destination byte, and
1038    // increment the destination pointer by one
1039    for _ in 0..byte_len {
1040        src = src.sub(1);
1041        *dst = *src;
1042        dst = dst.add(1);
1043    }
1044}
1045
1046/// Initializes a smallvec with the given length and a closure that initializes the buffer.
1047///
1048/// Optimized version of `SmallVec::with_capacity` + `f()` + `.set_len`.
1049///
1050/// # Safety
1051///
1052/// The closure must fully initialize the buffer with the given length.
1053#[inline]
1054pub unsafe fn smallvec_with<const N: usize>(
1055    len: usize,
1056    f: impl FnOnce(&mut [MaybeUninit<u8>]),
1057) -> SmallVec<[u8; N]> {
1058    let mut buf = smallvec_with_len::<N>(len);
1059    f(unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), len) });
1060    buf
1061}
1062
1063#[inline]
1064#[allow(clippy::uninit_vec)]
1065unsafe fn smallvec_with_len<const N: usize>(len: usize) -> SmallVec<[u8; N]> {
1066    if likely(len <= N) {
1067        SmallVec::from_buf_and_len_unchecked(MaybeUninit::<[u8; N]>::uninit(), len)
1068    } else {
1069        let mut vec = Vec::with_capacity(len);
1070        unsafe { vec.set_len(len) };
1071        SmallVec::from_vec(vec)
1072    }
1073}
1074
1075#[inline]
1076#[track_caller]
1077fn check_nibbles(nibbles: &[u8]) {
1078    if !valid_nibbles(nibbles) {
1079        panic_invalid_nibbles();
1080    }
1081}
1082
1083fn valid_nibbles(nibbles: &[u8]) -> bool {
1084    nibbles.iter().all(|&nibble| nibble <= 0xf)
1085}
1086
1087#[cold]
1088#[inline(never)]
1089#[cfg_attr(debug_assertions, track_caller)]
1090const fn panic_invalid_nibbles() -> ! {
1091    panic!("attempted to create invalid nibbles");
1092}
1093
1094#[cold]
1095#[inline(never)]
1096#[cfg_attr(debug_assertions, track_caller)]
1097fn panic_invalid_index(len: usize, i: usize) -> ! {
1098    panic!("index out of bounds: {i} for nibbles of length {len}");
1099}
1100
1101/// Internal container for owned/borrowed byte slices.
1102enum ByteContainer<'a, const N: usize> {
1103    /// Borrowed variant holds a reference to a slice of bytes.
1104    #[cfg_attr(target_endian = "big", allow(unused))]
1105    Borrowed(&'a [u8]),
1106    /// Owned variant holds a fixed-size array of bytes.
1107    #[cfg_attr(target_endian = "little", allow(unused))]
1108    Owned([u8; N]),
1109}
1110
1111impl<'a, const N: usize> ByteContainer<'a, N> {
1112    /// Returns a mutable reference to the underlying byte array, converting from borrowed to owned
1113    /// if necessary.
1114    ///
1115    /// ## Panics
1116    /// Panics if the current variant is `Borrowed` and the slice length is less than `N`.
1117    #[cfg_attr(target_endian = "little", allow(unused))]
1118    pub(crate) fn to_mut(&mut self) -> &mut [u8; N] {
1119        match self {
1120            ByteContainer::Borrowed(slice) => {
1121                let mut array = [0u8; N];
1122                array[..N].copy_from_slice(&slice[..N]);
1123                *self = ByteContainer::Owned(array);
1124                self.to_mut()
1125            }
1126            ByteContainer::Owned(ref mut array) => array,
1127        }
1128    }
1129}
1130
1131impl<'a, const N: usize> Deref for ByteContainer<'a, N> {
1132    type Target = [u8];
1133
1134    fn deref(&self) -> &Self::Target {
1135        match self {
1136            ByteContainer::Borrowed(slice) => slice,
1137            ByteContainer::Owned(array) => array.as_slice(),
1138        }
1139    }
1140}
1141
1142/// Returns a little-endian byte slice representation of the given [`U256`] value.
1143#[inline]
1144const fn as_le_slice(x: &U256) -> ByteContainer<'_, { U256::BYTES }> {
1145    cfg_if! {
1146        if #[cfg(target_endian = "little")] {
1147            ByteContainer::Borrowed(x.as_le_slice())
1148        } else {
1149            ByteContainer::Owned(x.to_le_bytes())
1150        }
1151    }
1152}
1153
1154#[cfg(test)]
1155mod tests {
1156    use super::*;
1157
1158    #[test]
1159    fn pack() {
1160        let tests = [
1161            (&[][..], &[][..]),
1162            (&[0xa], &[0xa0]),
1163            (&[0xa, 0x0], &[0xa0]),
1164            (&[0xa, 0xb], &[0xab]),
1165            (&[0xa, 0xb, 0x2], &[0xab, 0x20]),
1166            (&[0xa, 0xb, 0x2, 0x0], &[0xab, 0x20]),
1167            (&[0xa, 0xb, 0x2, 0x7], &[0xab, 0x27]),
1168        ];
1169        for (input, expected) in tests {
1170            assert!(valid_nibbles(input));
1171            let nibbles = Nibbles::from_nibbles(input);
1172            let encoded = nibbles.pack();
1173            assert_eq!(
1174                &encoded[..],
1175                expected,
1176                "input: {input:x?}, expected: {expected:x?}, got: {encoded:x?}",
1177            );
1178        }
1179    }
1180
1181    #[test]
1182    fn get_unchecked() {
1183        for len in 0..NIBBLES {
1184            let raw = (0..16).cycle().take(len).collect::<Vec<u8>>();
1185            let nibbles = Nibbles::from_nibbles(&raw);
1186            for (i, raw_nibble) in raw.into_iter().enumerate() {
1187                assert_eq!(nibbles.get_unchecked(i), raw_nibble);
1188            }
1189        }
1190    }
1191
1192    #[test]
1193    fn set_at_unchecked() {
1194        for len in 0..=NIBBLES {
1195            let raw = (0..16).cycle().take(len).collect::<Vec<u8>>();
1196            let mut nibbles = Nibbles::from_nibbles(&raw);
1197            for (i, raw_nibble) in raw.iter().enumerate() {
1198                let new_nibble = (raw_nibble + 1) % 16;
1199                unsafe { nibbles.set_at_unchecked(i, new_nibble) };
1200
1201                let mut new_raw_nibbles = nibbles.clone().to_vec();
1202                new_raw_nibbles[i] = new_nibble;
1203                let new_nibbles = Nibbles::from_nibbles(&new_raw_nibbles);
1204                assert_eq!(nibbles, new_nibbles,);
1205            }
1206        }
1207    }
1208
1209    #[test]
1210    fn push_pop() {
1211        let mut nibbles = Nibbles::new();
1212        nibbles.push(0x0A);
1213        assert_eq!(nibbles.get_unchecked(0), 0x0A);
1214        assert_eq!(nibbles.len(), 1);
1215
1216        assert_eq!(nibbles.pop(), Some(0x0A));
1217        assert_eq!(nibbles.len(), 0);
1218    }
1219
1220    #[test]
1221    fn get_byte() {
1222        let nibbles = Nibbles::from_nibbles([0x0A, 0x0B, 0x0C, 0x0D]);
1223        assert_eq!(nibbles.get_byte(0), Some(0xAB));
1224        assert_eq!(nibbles.get_byte(1), Some(0xBC));
1225        assert_eq!(nibbles.get_byte(2), Some(0xCD));
1226        assert_eq!(nibbles.get_byte(3), None);
1227        assert_eq!(nibbles.get_byte(usize::MAX), None);
1228    }
1229
1230    #[test]
1231    fn get_byte_unchecked() {
1232        let nibbles = Nibbles::from_nibbles([0x0A, 0x0B, 0x0C, 0x0D]);
1233        assert_eq!(nibbles.get_byte_unchecked(0), 0xAB);
1234        assert_eq!(nibbles.get_byte_unchecked(1), 0xBC);
1235        assert_eq!(nibbles.get_byte_unchecked(2), 0xCD);
1236    }
1237
1238    #[test]
1239    fn clone() {
1240        let a = Nibbles::from_nibbles([1, 2, 3]);
1241        #[allow(clippy::redundant_clone)]
1242        let b = a;
1243        assert_eq!(a, b);
1244    }
1245
1246    #[test]
1247    fn ord() {
1248        // Test empty nibbles
1249        let nibbles1 = Nibbles::default();
1250        let nibbles2 = Nibbles::default();
1251        assert_eq!(nibbles1.cmp(&nibbles2), Ordering::Equal);
1252
1253        // Test with one empty
1254        let nibbles1 = Nibbles::default();
1255        let nibbles2 = Nibbles::from_nibbles([0]);
1256        assert_eq!(nibbles2.cmp(&nibbles1), Ordering::Greater);
1257
1258        // Test with same nibbles
1259        let nibbles1 = Nibbles::unpack([0x12, 0x34]);
1260        let nibbles2 = Nibbles::unpack([0x12, 0x34]);
1261        assert_eq!(nibbles1.cmp(&nibbles2), Ordering::Equal);
1262
1263        // Test with different lengths
1264        let short = Nibbles::unpack([0x12]);
1265        let long = Nibbles::unpack([0x12, 0x34]);
1266        assert_eq!(short.cmp(&long), Ordering::Less);
1267
1268        // Test with common prefix but different values
1269        let nibbles1 = Nibbles::unpack([0x12, 0x34]);
1270        let nibbles2 = Nibbles::unpack([0x12, 0x35]);
1271        assert_eq!(nibbles1.cmp(&nibbles2), Ordering::Less);
1272
1273        // Test with differing first byte
1274        let nibbles1 = Nibbles::unpack([0x12, 0x34]);
1275        let nibbles2 = Nibbles::unpack([0x13, 0x34]);
1276        assert_eq!(nibbles1.cmp(&nibbles2), Ordering::Less);
1277
1278        // Test with odd length nibbles
1279        let nibbles1 = Nibbles::unpack([0x1]);
1280        let nibbles2 = Nibbles::unpack([0x2]);
1281        assert_eq!(nibbles1.cmp(&nibbles2), Ordering::Less);
1282
1283        // Test with odd and even length nibbles
1284        let odd = Nibbles::unpack([0x1]);
1285        let even = Nibbles::unpack([0x12]);
1286        assert_eq!(odd.cmp(&even), Ordering::Less);
1287
1288        // Test with longer sequences
1289        let nibbles1 = Nibbles::unpack([0x12, 0x34, 0x56, 0x78]);
1290        let nibbles2 = Nibbles::unpack([0x12, 0x34, 0x56, 0x79]);
1291        assert_eq!(nibbles1.cmp(&nibbles2), Ordering::Less);
1292
1293        let nibbles1 = Nibbles::from_nibbles([0x0, 0x0]);
1294        let nibbles2 = Nibbles::from_nibbles([0x1]);
1295        assert_eq!(nibbles1.cmp(&nibbles2), Ordering::Less);
1296
1297        let nibbles1 = Nibbles::from_nibbles([0x1]);
1298        let nibbles2 = Nibbles::from_nibbles([0x0, 0x2]);
1299        assert_eq!(nibbles1.cmp(&nibbles2), Ordering::Greater);
1300
1301        let nibbles1 = Nibbles::from_nibbles([vec![0; 61], vec![1; 1], vec![0; 1]].concat());
1302        let nibbles2 = Nibbles::from_nibbles([vec![0; 61], vec![1; 1], vec![0; 2]].concat());
1303        assert_eq!(nibbles1.cmp(&nibbles2), Ordering::Less);
1304    }
1305
1306    #[test]
1307    fn starts_with() {
1308        let nibbles = Nibbles::from_nibbles([1, 2, 3, 4]);
1309
1310        // Test empty nibbles
1311        let empty = Nibbles::default();
1312        assert!(nibbles.starts_with(&empty));
1313        assert!(empty.starts_with(&empty));
1314        assert!(!empty.starts_with(&nibbles));
1315
1316        // Test with same nibbles
1317        assert!(nibbles.starts_with(&nibbles));
1318
1319        // Test with prefix
1320        let prefix = Nibbles::from_nibbles([1, 2]);
1321        assert!(nibbles.starts_with(&prefix));
1322        assert!(!prefix.starts_with(&nibbles));
1323
1324        // Test with different first nibble
1325        let different = Nibbles::from_nibbles([2, 2, 3, 4]);
1326        assert!(!nibbles.starts_with(&different));
1327
1328        // Test with longer sequence
1329        let longer = Nibbles::from_nibbles([1, 2, 3, 4, 5, 6]);
1330        assert!(!nibbles.starts_with(&longer));
1331
1332        // Test with even nibbles and odd prefix
1333        let even_nibbles = Nibbles::from_nibbles([1, 2, 3, 4]);
1334        let odd_prefix = Nibbles::from_nibbles([1, 2, 3]);
1335        assert!(even_nibbles.starts_with(&odd_prefix));
1336
1337        // Test with odd nibbles and even prefix
1338        let odd_nibbles = Nibbles::from_nibbles([1, 2, 3]);
1339        let even_prefix = Nibbles::from_nibbles([1, 2]);
1340        assert!(odd_nibbles.starts_with(&even_prefix));
1341    }
1342
1343    #[test]
1344    fn ends_with() {
1345        let nibbles = Nibbles::from_nibbles([1, 2, 3, 4]);
1346
1347        // Test empty nibbles
1348        let empty = Nibbles::default();
1349        assert!(nibbles.ends_with(&empty));
1350        assert!(empty.ends_with(&empty));
1351        assert!(!empty.ends_with(&nibbles));
1352
1353        // Test with same nibbles
1354        assert!(nibbles.ends_with(&nibbles));
1355
1356        // Test with suffix
1357        let suffix = Nibbles::from_nibbles([3, 4]);
1358        assert!(nibbles.ends_with(&suffix));
1359        assert!(!suffix.ends_with(&nibbles));
1360
1361        // Test with different last nibble
1362        let different = Nibbles::from_nibbles([2, 3, 5]);
1363        assert!(!nibbles.ends_with(&different));
1364
1365        // Test with longer sequence
1366        let longer = Nibbles::from_nibbles([2, 3, 4, 5, 6]);
1367        assert!(!nibbles.ends_with(&longer));
1368
1369        // Test with even nibbles and odd suffix
1370        let even_nibbles = Nibbles::from_nibbles([1, 2, 3, 4]);
1371        let odd_suffix = Nibbles::from_nibbles([2, 3, 4]);
1372        assert!(even_nibbles.ends_with(&odd_suffix));
1373
1374        // Test with odd nibbles and even suffix
1375        let odd_nibbles = Nibbles::from_nibbles([1, 2, 3]);
1376        let even_suffix = Nibbles::from_nibbles([2, 3]);
1377        assert!(odd_nibbles.ends_with(&even_suffix));
1378    }
1379
1380    #[test]
1381    fn slice() {
1382        // Test with empty nibbles
1383        let empty = Nibbles::default();
1384        assert_eq!(empty.slice(..), empty);
1385
1386        // Test with even number of nibbles
1387        let even = Nibbles::from_nibbles([0, 1, 2, 3, 4, 5]);
1388
1389        // Full slice
1390        assert_eq!(even.slice(..), even);
1391        assert_eq!(even.slice(0..6), even);
1392
1393        // Empty slice
1394        assert_eq!(even.slice(3..3), Nibbles::default());
1395
1396        // Beginning slices (even start)
1397        assert_eq!(even.slice(0..2), Nibbles::from_iter(0..2));
1398
1399        // Middle slices (even start, even end)
1400        assert_eq!(even.slice(2..4), Nibbles::from_iter(2..4));
1401
1402        // End slices (even start)
1403        assert_eq!(even.slice(4..6), Nibbles::from_iter(4..6));
1404
1405        // Test with odd number of nibbles
1406        let odd = Nibbles::from_iter(0..5);
1407
1408        // Full slice
1409        assert_eq!(odd.slice(..), odd);
1410        assert_eq!(odd.slice(0..5), odd);
1411
1412        // Beginning slices (odd length)
1413        assert_eq!(odd.slice(0..3), Nibbles::from_iter(0..3));
1414
1415        // Middle slices with odd start
1416        assert_eq!(odd.slice(1..4), Nibbles::from_iter(1..4));
1417
1418        // Middle slices with odd end
1419        assert_eq!(odd.slice(1..3), Nibbles::from_iter(1..3));
1420
1421        // End slices (odd start)
1422        assert_eq!(odd.slice(2..5), Nibbles::from_iter(2..5));
1423
1424        // Special cases - both odd start and end
1425        assert_eq!(odd.slice(1..4), Nibbles::from_iter(1..4));
1426
1427        // Single nibble slices
1428        assert_eq!(even.slice(2..3), Nibbles::from_iter(2..3));
1429
1430        assert_eq!(even.slice(3..4), Nibbles::from_iter(3..4));
1431
1432        // Test with alternate syntax
1433        assert_eq!(even.slice(2..), Nibbles::from_iter(2..6));
1434        assert_eq!(even.slice(..4), Nibbles::from_iter(0..4));
1435        assert_eq!(even.slice(..=3), Nibbles::from_iter(0..4));
1436
1437        // More complex test case with the max length array sliced at the end
1438        assert_eq!(
1439            Nibbles::from_iter((0..16).cycle().take(64)).slice(1..),
1440            Nibbles::from_iter((0..16).cycle().take(64).skip(1))
1441        );
1442    }
1443
1444    #[test]
1445    fn common_prefix_length() {
1446        // Test with empty nibbles
1447        let empty = Nibbles::default();
1448        assert_eq!(empty.common_prefix_length(&empty), 0);
1449
1450        // Test with same nibbles
1451        let nibbles1 = Nibbles::from_nibbles([1, 2, 3, 4]);
1452        let nibbles2 = Nibbles::from_nibbles([1, 2, 3, 4]);
1453        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 4);
1454        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 4);
1455
1456        // Test with partial common prefix (byte aligned)
1457        let nibbles1 = Nibbles::from_nibbles([1, 2, 3, 4]);
1458        let nibbles2 = Nibbles::from_nibbles([1, 2, 5, 6]);
1459        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 2);
1460        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 2);
1461
1462        // Test with partial common prefix (half-byte aligned)
1463        let nibbles1 = Nibbles::from_nibbles([1, 2, 3, 4]);
1464        let nibbles2 = Nibbles::from_nibbles([1, 2, 3, 7]);
1465        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 3);
1466        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 3);
1467
1468        // Test with no common prefix
1469        let nibbles1 = Nibbles::from_nibbles([5, 6, 7, 8]);
1470        let nibbles2 = Nibbles::from_nibbles([1, 2, 3, 4]);
1471        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 0);
1472        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 0);
1473
1474        // Test with different lengths but common prefix
1475        let nibbles1 = Nibbles::from_nibbles([1, 2, 3, 4, 5, 6]);
1476        let nibbles2 = Nibbles::from_nibbles([1, 2, 3]);
1477        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 3);
1478        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 3);
1479
1480        // Test with odd number of nibbles
1481        let nibbles1 = Nibbles::from_nibbles([1, 2, 3]);
1482        let nibbles2 = Nibbles::from_nibbles([1, 2, 7]);
1483        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 2);
1484        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 2);
1485
1486        // Test with half-byte difference in first byte
1487        let nibbles1 = Nibbles::from_nibbles([1, 2, 3, 4]);
1488        let nibbles2 = Nibbles::from_nibbles([5, 2, 3, 4]);
1489        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 0);
1490        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 0);
1491
1492        // Test with one empty and one non-empty
1493        let nibbles1 = Nibbles::from_nibbles([1, 2, 3, 4]);
1494        assert_eq!(nibbles1.common_prefix_length(&empty), 0);
1495        assert_eq!(empty.common_prefix_length(&nibbles1), 0);
1496
1497        // Test with longer sequences (16 nibbles)
1498        let nibbles1 =
1499            Nibbles::from_nibbles([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0]);
1500        let nibbles2 =
1501            Nibbles::from_nibbles([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1]);
1502        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 15);
1503        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 15);
1504
1505        // Test with different lengths but same prefix (32 vs 16 nibbles)
1506        let nibbles1 =
1507            Nibbles::from_nibbles([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0]);
1508        let nibbles2 = Nibbles::from_nibbles([
1509            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1510            11, 12, 13, 14, 15, 0,
1511        ]);
1512        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 16);
1513        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 16);
1514
1515        // Test with very long sequences (32 nibbles) with different endings
1516        let nibbles1 = Nibbles::from_nibbles([
1517            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1518            11, 12, 13, 14, 15, 0,
1519        ]);
1520        let nibbles2 = Nibbles::from_nibbles([
1521            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1522            11, 12, 13, 14, 15, 1,
1523        ]);
1524        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 31);
1525        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 31);
1526
1527        // Test with 48 nibbles with different endings
1528        let nibbles1 = Nibbles::from_nibbles([
1529            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1530            11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0,
1531        ]);
1532        let nibbles2 = Nibbles::from_nibbles([
1533            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1534            11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1,
1535        ]);
1536        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 47);
1537        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 47);
1538
1539        // Test with 64 nibbles with different endings
1540        let nibbles1 = Nibbles::from_nibbles([
1541            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1542            11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3,
1543            4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0,
1544        ]);
1545        let nibbles2 = Nibbles::from_nibbles([
1546            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1547            11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3,
1548            4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1,
1549        ]);
1550        assert_eq!(nibbles1.common_prefix_length(&nibbles2), 63);
1551        assert_eq!(nibbles2.common_prefix_length(&nibbles1), 63);
1552
1553        let current = Nibbles::from_nibbles([0u8; 64]);
1554        let path = Nibbles::from_nibbles([vec![0u8; 63], vec![2u8]].concat());
1555        assert_eq!(current.common_prefix_length(&path), 63);
1556
1557        let current = Nibbles::from_nibbles([0u8; 63]);
1558        let path = Nibbles::from_nibbles([vec![0u8; 62], vec![1u8], vec![0u8]].concat());
1559        assert_eq!(current.common_prefix_length(&path), 62);
1560    }
1561
1562    #[test]
1563    fn truncate() {
1564        // Test truncating empty nibbles
1565        let mut nibbles = Nibbles::default();
1566        nibbles.truncate(0);
1567        assert_eq!(nibbles, Nibbles::default());
1568
1569        // Test truncating to zero length
1570        let mut nibbles = Nibbles::from_nibbles([1, 2, 3, 4]);
1571        nibbles.truncate(0);
1572        assert_eq!(nibbles, Nibbles::default());
1573
1574        // Test truncating to same length (should be no-op)
1575        let mut nibbles = Nibbles::from_nibbles([1, 2, 3, 4]);
1576        nibbles.truncate(4);
1577        assert_eq!(nibbles, Nibbles::from_nibbles([1, 2, 3, 4]));
1578
1579        // Individual nibble test with a simple 2-nibble truncation
1580        let mut nibbles = Nibbles::from_nibbles([1, 2, 3, 4]);
1581        nibbles.truncate(2);
1582        assert_eq!(nibbles, Nibbles::from_nibbles([1, 2]));
1583
1584        // Test simple truncation
1585        let mut nibbles = Nibbles::from_nibbles([1, 2, 3, 4]);
1586        nibbles.truncate(2);
1587        assert_eq!(nibbles, Nibbles::from_nibbles([1, 2]));
1588
1589        // Test truncating to single nibble
1590        let mut nibbles = Nibbles::from_nibbles([5, 6, 7, 8]);
1591        nibbles.truncate(1);
1592        assert_eq!(nibbles, Nibbles::from_nibbles([5]));
1593    }
1594
1595    #[test]
1596    fn push_unchecked() {
1597        // Test pushing to empty nibbles
1598        let mut nibbles = Nibbles::default();
1599        nibbles.push_unchecked(0x5);
1600        assert_eq!(nibbles, Nibbles::from_nibbles([0x5]));
1601
1602        // Test pushing a second nibble
1603        nibbles.push_unchecked(0xA);
1604        assert_eq!(nibbles, Nibbles::from_nibbles([0x5, 0xA]));
1605
1606        // Test pushing multiple nibbles to build a sequence
1607        let mut nibbles = Nibbles::default();
1608        for nibble in [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8] {
1609            nibbles.push_unchecked(nibble);
1610        }
1611        assert_eq!(nibbles, Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]));
1612
1613        // Test pushing nibbles with values that exceed 4 bits (should be masked to 0x0F)
1614        let mut nibbles = Nibbles::default();
1615        nibbles.push_unchecked(0xFF); // Should become 0xF
1616        nibbles.push_unchecked(0x1A); // Should become 0xA
1617        nibbles.push_unchecked(0x25); // Should become 0x5
1618        assert_eq!(nibbles, Nibbles::from_nibbles([0xF, 0xA, 0x5]));
1619
1620        // Test pushing to existing nibbles (adding to the end)
1621        let mut nibbles = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
1622        nibbles.push_unchecked(0x4);
1623        assert_eq!(nibbles, Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]));
1624
1625        // Test boundary values (0 and 15)
1626        let mut nibbles = Nibbles::default();
1627        nibbles.push_unchecked(0x0);
1628        nibbles.push_unchecked(0xF);
1629        assert_eq!(nibbles, Nibbles::from_nibbles([0x0, 0xF]));
1630
1631        // Test pushing many nibbles to verify no overflow issues
1632        let mut nibbles = Nibbles::default();
1633        let test_sequence: Vec<u8> = (0..32).map(|i| i % 16).collect();
1634        for &nibble in &test_sequence {
1635            nibbles.push_unchecked(nibble);
1636        }
1637        assert_eq!(nibbles, Nibbles::from_nibbles(test_sequence));
1638    }
1639
1640    #[test]
1641    fn unpack() {
1642        for (test_idx, bytes) in (0..=32).map(|i| vec![0xFF; i]).enumerate() {
1643            let packed_nibbles = Nibbles::unpack(&bytes);
1644            let nibbles = Nibbles::unpack(&bytes);
1645
1646            assert_eq!(
1647                packed_nibbles.len(),
1648                nibbles.len(),
1649                "Test case {test_idx}: Length mismatch for bytes {bytes:?}",
1650            );
1651            assert_eq!(
1652                packed_nibbles.len(),
1653                bytes.len() * 2,
1654                "Test case {test_idx}: Expected length to be 2x byte length",
1655            );
1656
1657            // Compare each nibble individually
1658            for i in 0..packed_nibbles.len() {
1659                assert_eq!(
1660                    packed_nibbles.get_unchecked(i),
1661                    nibbles.get_unchecked(i),
1662                    "Test case {}: Nibble at index {} differs for bytes {:?}: Nibbles={:?}, Nibbles={:?}",
1663                    test_idx,
1664                    i,
1665                    bytes,
1666                    packed_nibbles.get_unchecked(i),
1667                    nibbles.get_unchecked(i)
1668                );
1669            }
1670        }
1671
1672        let nibbles = Nibbles::unpack([0xAB, 0xCD]);
1673        assert_eq!(nibbles.to_vec(), vec![0x0A, 0x0B, 0x0C, 0x0D]);
1674    }
1675
1676    #[test]
1677    fn increment() {
1678        // Test basic increment
1679        assert_eq!(
1680            Nibbles::from_nibbles([0x0, 0x0, 0x0]).increment().unwrap(),
1681            Nibbles::from_nibbles([0x0, 0x0, 0x1])
1682        );
1683
1684        // Test increment with carry
1685        assert_eq!(
1686            Nibbles::from_nibbles([0x0, 0x0, 0xF]).increment().unwrap(),
1687            Nibbles::from_nibbles([0x0, 0x1, 0x0])
1688        );
1689
1690        // Test multiple carries
1691        assert_eq!(
1692            Nibbles::from_nibbles([0x0, 0xF, 0xF]).increment().unwrap(),
1693            Nibbles::from_nibbles([0x1, 0x0, 0x0])
1694        );
1695
1696        // Test increment from all F's except first nibble
1697        assert_eq!(
1698            Nibbles::from_nibbles([0xE, 0xF, 0xF]).increment().unwrap(),
1699            Nibbles::from_nibbles([0xF, 0x0, 0x0])
1700        );
1701
1702        // Test overflow - all nibbles are 0xF
1703        assert_eq!(Nibbles::from_nibbles([0xF, 0xF, 0xF]).increment(), None);
1704
1705        // Test empty nibbles
1706        assert_eq!(Nibbles::new().increment(), None);
1707
1708        // Test single nibble
1709        assert_eq!(Nibbles::from_nibbles([0x5]).increment().unwrap(), Nibbles::from_nibbles([0x6]));
1710
1711        // Test single nibble at max
1712        assert_eq!(Nibbles::from_nibbles([0xF]).increment(), None);
1713
1714        // Test longer sequence
1715        assert_eq!(
1716            Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5]).increment().unwrap(),
1717            Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x6])
1718        );
1719
1720        // Test longer sequence with carries
1721        assert_eq!(
1722            Nibbles::from_nibbles([0x1, 0x2, 0x3, 0xF, 0xF]).increment().unwrap(),
1723            Nibbles::from_nibbles([0x1, 0x2, 0x4, 0x0, 0x0])
1724        );
1725    }
1726
1727    #[test]
1728    fn iter() {
1729        // Test empty nibbles
1730        let empty = Nibbles::new();
1731        assert_eq!(empty.iter().collect::<Vec<_>>(), vec![]);
1732
1733        // Test basic iteration
1734        let nibbles = Nibbles::from_nibbles([0x0A, 0x0B, 0x0C, 0x0D]);
1735        let collected: Vec<u8> = nibbles.iter().collect();
1736        assert_eq!(collected, vec![0x0A, 0x0B, 0x0C, 0x0D]);
1737
1738        // Test that iter() produces same result as to_vec()
1739        assert_eq!(nibbles.iter().collect::<Vec<_>>(), nibbles.to_vec());
1740
1741        // Test single nibble
1742        let single = Nibbles::from_nibbles([0x05]);
1743        assert_eq!(single.iter().collect::<Vec<_>>(), vec![0x05]);
1744
1745        // Test odd number of nibbles
1746        let odd = Nibbles::from_nibbles([0x01, 0x02, 0x03]);
1747        assert_eq!(odd.iter().collect::<Vec<_>>(), vec![0x01, 0x02, 0x03]);
1748
1749        // Test max length nibbles
1750        let max_nibbles: Vec<u8> = (0..64).map(|i| (i % 16) as u8).collect();
1751        let max = Nibbles::from_nibbles(&max_nibbles);
1752        assert_eq!(max.iter().collect::<Vec<_>>(), max_nibbles);
1753
1754        // Test iterator size_hint and len
1755        let nibbles = Nibbles::from_nibbles([0x0A, 0x0B, 0x0C, 0x0D]);
1756        let mut iter = nibbles.iter();
1757        assert_eq!(iter.len(), 4);
1758        assert_eq!(iter.size_hint(), (4, Some(4)));
1759
1760        iter.next();
1761        assert_eq!(iter.len(), 3);
1762        assert_eq!(iter.size_hint(), (3, Some(3)));
1763
1764        iter.next();
1765        iter.next();
1766        assert_eq!(iter.len(), 1);
1767        assert_eq!(iter.size_hint(), (1, Some(1)));
1768
1769        iter.next();
1770        assert_eq!(iter.len(), 0);
1771        assert_eq!(iter.size_hint(), (0, Some(0)));
1772        assert_eq!(iter.next(), None);
1773
1774        // Test cloning iterator
1775        let nibbles = Nibbles::from_nibbles([0x01, 0x02, 0x03, 0x04]);
1776        let mut iter1 = nibbles.iter();
1777        iter1.next();
1778        let iter2 = iter1.clone();
1779
1780        assert_eq!(iter1.collect::<Vec<_>>(), vec![0x02, 0x03, 0x04]);
1781        assert_eq!(iter2.collect::<Vec<_>>(), vec![0x02, 0x03, 0x04]);
1782    }
1783
1784    #[cfg(feature = "arbitrary")]
1785    mod arbitrary {
1786        use super::*;
1787        use proptest::{collection::vec, prelude::*};
1788
1789        proptest::proptest! {
1790            #[test]
1791            #[cfg_attr(miri, ignore = "no proptest")]
1792            fn pack_unpack_roundtrip(input in vec(any::<u8>(), 0..32)) {
1793                let nibbles = Nibbles::unpack(&input);
1794                prop_assert!(valid_nibbles(&nibbles.to_vec()));
1795                let packed = nibbles.pack();
1796                prop_assert_eq!(&packed[..], input);
1797            }
1798        }
1799    }
1800}