champ_trie/node.rs
1//! CHAMP trie node types and bitmap helpers.
2
3use std::fmt;
4
5use safe_bump::Idx;
6
7/// Bits per trie level (5 → 32-way branching).
8pub const BITS_PER_LEVEL: u32 = 5;
9
10/// Maximum bit-shift value (depth 12, last level uses 4 bits).
11pub const MAX_SHIFT: u32 = 60;
12
13/// Inline entry storing a key-value pair with its precomputed hash.
14pub struct Entry<K, V> {
15 /// Precomputed 64-bit hash of the key.
16 pub hash: u64,
17 /// The key.
18 pub key: K,
19 /// The value.
20 pub value: V,
21}
22
23/// CHAMP trie node.
24///
25/// Two variants maintain the canonical form invariant:
26/// - [`Inner`](Self::Inner) — bitmap-compressed node at depth `d < D`
27/// - [`Collision`](Self::Collision) — linear node for full 64-bit hash collisions
28pub enum Node<K, V> {
29 /// Bitmap-compressed inner node.
30 ///
31 /// Invariant: `data_map & node_map == 0` (disjoint positions).
32 Inner {
33 /// Bitmap of positions occupied by inline entries.
34 data_map: u32,
35 /// Bitmap of positions occupied by child subtrees.
36 node_map: u32,
37 /// Index of the first inline entry in the entries arena.
38 data_start: Idx<Entry<K, V>>,
39 /// Index of the first child pointer in the children arena.
40 children_start: Idx<Idx<Self>>,
41 /// `AdHash` of this subtree.
42 adhash: u64,
43 },
44 /// Collision node for keys sharing the same 64-bit hash.
45 ///
46 /// Invariant: `entries_len >= 2`.
47 Collision {
48 /// The shared 64-bit hash value.
49 hash: u64,
50 /// Index of the first entry in the entries arena.
51 entries_start: Idx<Entry<K, V>>,
52 /// Number of collision entries.
53 entries_len: u8,
54 /// `AdHash` of this subtree.
55 adhash: u64,
56 },
57}
58
59// ---------------------------------------------------------------------------
60// Bitmap helpers
61// ---------------------------------------------------------------------------
62
63/// Extracts the 5-bit hash fragment at the given bit-shift depth.
64#[inline]
65#[must_use]
66pub const fn fragment(hash: u64, shift: u32) -> u32 {
67 ((hash >> shift) & 0x1F) as u32
68}
69
70/// Returns the single-bit mask for the given fragment (0..31).
71#[inline]
72#[must_use]
73pub const fn mask(frag: u32) -> u32 {
74 1 << frag
75}
76
77/// Returns the compact index of `bit` within `bitmap`.
78///
79/// Counts the number of set bits below `bit`.
80#[inline]
81#[must_use]
82pub const fn index(bitmap: u32, bit: u32) -> usize {
83 (bitmap & (bit - 1)).count_ones() as usize
84}
85
86/// Offsets a base index by `n` positions.
87#[inline]
88#[must_use]
89pub const fn offset<T>(base: Idx<T>, n: usize) -> Idx<T> {
90 Idx::from_raw(base.into_raw() + n)
91}
92
93// ---------------------------------------------------------------------------
94// Node accessors
95// ---------------------------------------------------------------------------
96
97impl<K, V> Node<K, V> {
98 /// Returns the `AdHash` of this node's subtree.
99 #[must_use]
100 pub const fn adhash(&self) -> u64 {
101 match self {
102 Self::Inner { adhash, .. } | Self::Collision { adhash, .. } => *adhash,
103 }
104 }
105
106 /// Returns the number of inline data entries.
107 #[must_use]
108 pub const fn data_len(&self) -> usize {
109 match self {
110 Self::Inner { data_map, .. } => data_map.count_ones() as usize,
111 Self::Collision { entries_len, .. } => *entries_len as usize,
112 }
113 }
114
115 /// Returns the number of child subtrees (always 0 for collision nodes).
116 #[must_use]
117 pub const fn children_len(&self) -> usize {
118 match self {
119 Self::Inner { node_map, .. } => node_map.count_ones() as usize,
120 Self::Collision { .. } => 0,
121 }
122 }
123}
124
125// ---------------------------------------------------------------------------
126// Manual trait impls — avoid false `K: Trait, V: Trait` bounds.
127// Node contains only indices (Copy) and primitives — no actual K/V data.
128// ---------------------------------------------------------------------------
129
130impl<K, V> Clone for Node<K, V> {
131 fn clone(&self) -> Self {
132 *self
133 }
134}
135
136impl<K, V> Copy for Node<K, V> {}
137
138impl<K, V> fmt::Debug for Node<K, V> {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 match self {
141 Self::Inner {
142 data_map,
143 node_map,
144 adhash,
145 ..
146 } => f
147 .debug_struct("Inner")
148 .field("data_map", &format_args!("{data_map:#034b}"))
149 .field("node_map", &format_args!("{node_map:#034b}"))
150 .field("adhash", adhash)
151 .finish(),
152 Self::Collision {
153 hash,
154 entries_len,
155 adhash,
156 ..
157 } => f
158 .debug_struct("Collision")
159 .field("hash", hash)
160 .field("entries_len", entries_len)
161 .field("adhash", adhash)
162 .finish(),
163 }
164 }
165}