allocated_btree/compressed/
wrapper.rs

1//! Ergonomic wrapper for the compressed B-Tree implementation.
2//!
3//! This module provides [`CompressedBTreeMap<K, V, B, A>`], a wrapper around
4//! the allocated B-Tree that owns an allocator, making it safe and
5//! ergonomic to use.
6
7use core::borrow::Borrow;
8use core::fmt::Debug;
9use core::mem::ManuallyDrop;
10use core::ops::Add;
11use core::ops::Mul;
12
13use allocator_api2::alloc::{Allocator, Global};
14use generic_array::ArrayLength;
15use typenum::{Prod, Sum, U1, U2, U6};
16
17use allocated::{AllocResult, AllocResultExt, DropIn};
18
19use super::{AllocatedBTreeMap, Entry, Iter, Keys, OccupiedEntry, Values, ValuesMut};
20
21/// An ergonomic compressed B-Tree map that owns its allocator.
22///
23/// This is a memory-optimized B-Tree that uses specialized leaf nodes
24/// without child pointers, reducing memory usage by approximately 30%
25/// compared to the naive implementation.
26///
27/// This is the recommended type for most use cases. It wraps the allocated
28/// B-Tree and provides safe methods without requiring `unsafe` blocks or
29/// passing allocators manually.
30///
31/// # Example
32///
33/// ```
34/// use allocated_btree::CompressedBTreeMap;
35///
36/// let mut map = CompressedBTreeMap::new();
37/// map.insert(1, "one").unwrap();
38/// map.insert(2, "two").unwrap();
39///
40/// assert_eq!(map.get(&1), Some(&"one"));
41/// assert_eq!(map.len(), 2);
42/// ```
43pub struct CompressedBTreeMap<
44    K: core::cmp::PartialOrd + core::fmt::Debug,
45    V,
46    B: ArrayLength = U6,
47    A: Allocator = Global,
48> where
49    U2: Mul<B>,
50    Prod<U2, B>: ArrayLength,
51    U1: Add<Prod<U2, B>>,
52    Sum<U1, Prod<U2, B>>: ArrayLength,
53{
54    alloc: A,
55    raw: ManuallyDrop<AllocatedBTreeMap<K, V, B>>,
56}
57
58impl<K: core::cmp::PartialOrd + Debug, V> CompressedBTreeMap<K, V> {
59    /// Create a new empty compressed B-Tree using the global allocator.
60    ///
61    /// # Panics
62    ///
63    /// Panics if allocation fails.
64    #[inline]
65    pub fn new() -> Self {
66        let raw = AllocatedBTreeMap::new_in(&Global)
67            .handle_alloc_error()
68            .into_inner();
69
70        Self { alloc: Global, raw }
71    }
72}
73
74impl<K: core::cmp::PartialOrd + Debug, V> Default for CompressedBTreeMap<K, V> {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80impl<K: core::cmp::PartialOrd + Debug, V, B: ArrayLength, A: Allocator> Drop
81    for CompressedBTreeMap<K, V, B, A>
82where
83    U2: Mul<B>,
84    Prod<U2, B>: ArrayLength,
85    U1: Add<Prod<U2, B>>,
86    Sum<U1, Prod<U2, B>>: ArrayLength,
87{
88    fn drop(&mut self) {
89        // SAFETY: `self.raw` was allocated by `self.alloc`
90        unsafe {
91            self.raw.drop_in(&self.alloc);
92        }
93    }
94}
95
96impl<K: core::cmp::PartialOrd + Debug, V, B: ArrayLength, A: Allocator>
97    CompressedBTreeMap<K, V, B, A>
98where
99    U2: Mul<B>,
100    Prod<U2, B>: ArrayLength,
101    U1: Add<Prod<U2, B>>,
102    Sum<U1, Prod<U2, B>>: ArrayLength,
103{
104    /// Create a new empty compressed B-Tree using the provided allocator.
105    ///
106    /// # Errors
107    ///
108    /// Returns an error if allocation fails.
109    pub fn new_in(alloc: A) -> AllocResult<Self> {
110        let raw = AllocatedBTreeMap::new_in(&alloc)?.into_inner();
111        Ok(Self { alloc, raw })
112    }
113
114    /// Returns the number of elements in the map.
115    #[inline]
116    pub fn len(&self) -> usize {
117        self.raw.len()
118    }
119
120    /// Returns `true` if the map contains no elements.
121    #[inline]
122    pub fn is_empty(&self) -> bool {
123        self.raw.is_empty()
124    }
125
126    /// Returns `true` if the map contains a value for the specified key.
127    pub fn contains_key<Q>(&self, key: &Q) -> bool
128    where
129        K: Borrow<Q>,
130        Q: core::cmp::PartialOrd + Debug,
131    {
132        self.raw.contains_key(key)
133    }
134
135    /// Returns a reference to the value corresponding to the key.
136    pub fn get<Q>(&self, key: &Q) -> Option<&V>
137    where
138        K: Borrow<Q>,
139        Q: core::cmp::PartialOrd + Debug,
140    {
141        self.raw.get(key)
142    }
143
144    /// Returns the key-value pair corresponding to the supplied key.
145    pub fn get_key_value<'s, Q>(&'s self, key: &'s Q) -> Option<(&'s K, &'s V)>
146    where
147        K: Borrow<Q>,
148        Q: core::cmp::PartialOrd + Debug,
149    {
150        self.raw.get_key_value(key)
151    }
152
153    /// Returns a mutable reference to the value corresponding to the key.
154    pub fn get_mut<'s, Q>(&'s mut self, key: &'s Q) -> Option<&'s mut V>
155    where
156        K: Borrow<Q>,
157        Q: core::cmp::PartialOrd + Debug,
158    {
159        self.raw.get_mut(key)
160    }
161
162    /// Inserts a key-value pair into the map.
163    ///
164    /// If the map did not have this key present, `None` is returned.
165    /// If the map did have this key present, the value is updated,
166    /// and the old value is returned.
167    ///
168    /// # Errors
169    ///
170    /// Returns an error if allocation fails during tree rebalancing.
171    pub fn insert(&mut self, key: K, value: V) -> AllocResult<Option<V>> {
172        // SAFETY: `self.alloc` was used to allocate `self.raw`
173        unsafe { self.raw.insert_in(&self.alloc, key, value) }
174    }
175
176    /// Clears the map, removing all key-value pairs.
177    ///
178    /// # Errors
179    ///
180    /// Returns an error if allocation fails.
181    pub fn clear(&mut self) -> AllocResult<()> {
182        // SAFETY: `self.alloc` was used to allocate `self.raw`
183        unsafe { self.raw.clear_in(&self.alloc) }
184    }
185
186    /// Gets the given key's corresponding entry in the map for in-place manipulation.
187    pub fn entry(&mut self, key: K) -> Entry<'_, '_, A, K, V, B> {
188        // SAFETY: `self.alloc` was used to allocate `self.raw`
189        unsafe { self.raw.entry_in(&self.alloc, key) }
190    }
191
192    /// Gets the given key's corresponding occupied entry in the map for in-place manipulation.
193    ///
194    /// Returns `None` if the key is not present in the map.
195    ///
196    /// The key may be any borrowed form of the map's key type, but the ordering
197    /// on the borrowed form *must* match the ordering on the key type.
198    ///
199    /// # Examples
200    ///
201    /// ```
202    /// use allocated_btree::CompressedBTreeMap;
203    ///
204    /// let mut map = CompressedBTreeMap::new();
205    /// map.insert(1, "a").unwrap();
206    ///
207    /// // Get the entry if it exists and modify it
208    /// if let Some(mut entry) = map.entry_ref(&1) {
209    ///     *entry.get_mut() = "b";
210    /// }
211    /// assert_eq!(map.get(&1), Some(&"b"));
212    ///
213    /// // Non-existent key returns None
214    /// assert!(map.entry_ref(&2).is_none());
215    /// ```
216    ///
217    /// Using `String` keys with `&str` lookups:
218    ///
219    /// ```
220    /// use allocated_btree::CompressedBTreeMap;
221    ///
222    /// let mut map = CompressedBTreeMap::new();
223    /// map.insert("hello".to_string(), 42).unwrap();
224    ///
225    /// if let Some(entry) = map.entry_ref("hello") {
226    ///     assert_eq!(entry.key(), &"hello".to_string());
227    /// }
228    /// ```
229    pub fn entry_ref<Q>(&mut self, key: &Q) -> Option<OccupiedEntry<'_, '_, A, K, V, B>>
230    where
231        K: Borrow<Q>,
232        Q: PartialOrd + core::fmt::Debug + ?Sized,
233    {
234        // SAFETY: self.alloc was used to allocate self.raw
235        unsafe { self.raw.entry_ref_in(&self.alloc, key) }
236    }
237
238    /// Gets the first entry in the map for in-place manipulation.
239    pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, '_, A, K, V, B>> {
240        // SAFETY: `self.alloc` was used to allocate `self.raw`
241        unsafe { self.raw.first_entry_in(&self.alloc) }
242    }
243
244    /// Returns the first key-value pair in the map.
245    pub fn first_key_value(&self) -> Option<(&K, &V)> {
246        self.raw.first_key_value()
247    }
248
249    /// Gets the last entry in the map for in-place manipulation.
250    pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, '_, A, K, V, B>> {
251        // SAFETY: `self.alloc` was used to allocate `self.raw`
252        unsafe { self.raw.last_entry_in(&self.alloc) }
253    }
254
255    /// Returns the last key-value pair in the map.
256    pub fn last_key_value(&self) -> Option<(&K, &V)> {
257        self.raw.last_key_value()
258    }
259
260    /// Returns the first key in the map.
261    pub fn first(&self) -> Option<&K> {
262        self.raw.first()
263    }
264
265    /// Returns the last key in the map.
266    pub fn last(&self) -> Option<&K> {
267        self.raw.last()
268    }
269
270    /// Removes and returns the first key-value pair in the map.
271    /// The key in this pair is the minimum key that was in the map.
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// use allocated_btree::CompressedBTreeMap;
277    ///
278    /// let mut map = CompressedBTreeMap::new();
279    /// map.insert(1, "a").unwrap();
280    /// map.insert(2, "b").unwrap();
281    ///
282    /// assert_eq!(map.pop_first(), Some((1, "a")));
283    /// assert_eq!(map.pop_first(), Some((2, "b")));
284    /// assert_eq!(map.pop_first(), None);
285    /// ```
286    pub fn pop_first(&mut self) -> Option<(K, V)> {
287        self.first_entry().map(|entry| entry.remove_entry())
288    }
289
290    /// Removes and returns the last key-value pair in the map.
291    /// The key in this pair is the maximum key that was in the map.
292    ///
293    /// # Examples
294    ///
295    /// ```
296    /// use allocated_btree::CompressedBTreeMap;
297    ///
298    /// let mut map = CompressedBTreeMap::new();
299    /// map.insert(1, "a").unwrap();
300    /// map.insert(2, "b").unwrap();
301    ///
302    /// assert_eq!(map.pop_last(), Some((2, "b")));
303    /// assert_eq!(map.pop_last(), Some((1, "a")));
304    /// assert_eq!(map.pop_last(), None);
305    /// ```
306    pub fn pop_last(&mut self) -> Option<(K, V)> {
307        self.last_entry().map(|entry| entry.remove_entry())
308    }
309
310    /// Removes a key from the map, returning the value at the key if the key
311    /// was previously in the map.
312    ///
313    /// The key may be any borrowed form of the map's key type, but the ordering
314    /// on the borrowed form *must* match the ordering on the key type.
315    ///
316    /// # Examples
317    ///
318    /// ```
319    /// use allocated_btree::CompressedBTreeMap;
320    ///
321    /// let mut map = CompressedBTreeMap::new();
322    /// map.insert(1, "a").unwrap();
323    /// assert_eq!(map.remove(&1), Some("a"));
324    /// assert_eq!(map.remove(&1), None);
325    /// ```
326    ///
327    /// Using `String` keys with `&str` lookups:
328    ///
329    /// ```
330    /// use allocated_btree::CompressedBTreeMap;
331    ///
332    /// let mut map = CompressedBTreeMap::new();
333    /// map.insert("hello".to_string(), 42).unwrap();
334    /// assert_eq!(map.remove("hello"), Some(42));  // Note: &str, not &String
335    /// assert_eq!(map.remove("hello"), None);
336    /// ```
337    pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
338    where
339        K: Borrow<Q>,
340        Q: PartialOrd + core::fmt::Debug + ?Sized,
341    {
342        // SAFETY: self.alloc was used to allocate self.raw
343        unsafe { self.raw.remove_in(&self.alloc, key) }
344    }
345
346    /// Removes a key from the map, returning the stored key and value if the key
347    /// was previously in the map.
348    ///
349    /// The key may be any borrowed form of the map's key type, but the ordering
350    /// on the borrowed form *must* match the ordering on the key type.
351    ///
352    /// # Examples
353    ///
354    /// ```
355    /// use allocated_btree::CompressedBTreeMap;
356    ///
357    /// let mut map = CompressedBTreeMap::new();
358    /// map.insert(1, "a").unwrap();
359    /// assert_eq!(map.remove_entry(&1), Some((1, "a")));
360    /// assert_eq!(map.remove_entry(&1), None);
361    /// ```
362    pub fn remove_entry<Q>(&mut self, key: &Q) -> Option<(K, V)>
363    where
364        K: Borrow<Q>,
365        Q: PartialOrd + core::fmt::Debug + ?Sized,
366    {
367        // SAFETY: self.alloc was used to allocate self.raw
368        unsafe { self.raw.remove_entry_in(&self.alloc, key) }
369    }
370
371    /// Gets an iterator over the entries of the map, sorted by key.
372    pub fn iter(&self) -> Iter<'_, K, V, B> {
373        self.raw.iter()
374    }
375
376    /// Gets an iterator over the keys of the map, in sorted order.
377    pub fn keys(&self) -> Keys<'_, K, V, B> {
378        self.raw.keys()
379    }
380
381    /// Gets an iterator over the values of the map, in order by key.
382    pub fn values(&self) -> Values<'_, K, V, B> {
383        self.raw.values()
384    }
385
386    /// Gets a mutable iterator over the values of the map, in order by key.
387    pub fn values_mut(&mut self) -> ValuesMut<'_, K, V, B> {
388        self.raw.values_mut()
389    }
390}
391
392impl<'s, K: core::cmp::PartialOrd + Debug, V, B: ArrayLength, A: Allocator> IntoIterator
393    for &'s CompressedBTreeMap<K, V, B, A>
394where
395    U2: Mul<B>,
396    Prod<U2, B>: ArrayLength,
397    U1: Add<Prod<U2, B>>,
398    Sum<U1, Prod<U2, B>>: ArrayLength,
399{
400    type IntoIter = Iter<'s, K, V, B>;
401    type Item = (&'s K, &'s V);
402
403    fn into_iter(self) -> Self::IntoIter {
404        self.iter()
405    }
406}
407
408impl<K: PartialOrd + Debug, V> FromIterator<(K, V)> for CompressedBTreeMap<K, V> {
409    /// Creates a B-tree map from an iterator of key-value pairs.
410    ///
411    /// If the iterator yields multiple values for the same key, the last value wins.
412    ///
413    /// # Panics
414    ///
415    /// Panics if allocation fails during construction.
416    ///
417    /// # Examples
418    ///
419    /// ```
420    /// use allocated_btree::CompressedBTreeMap;
421    ///
422    /// let items = vec![(1, "a"), (2, "b"), (3, "c")];
423    /// let map: CompressedBTreeMap<_, _> = items.into_iter().collect();
424    ///
425    /// assert_eq!(map.get(&1), Some(&"a"));
426    /// assert_eq!(map.get(&2), Some(&"b"));
427    /// assert_eq!(map.get(&3), Some(&"c"));
428    /// assert_eq!(map.len(), 3);
429    /// ```
430    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
431        use allocated::{AllocResultExt, FromIteratorIn};
432        use allocator_api2::alloc::Global;
433
434        let alloc = Global;
435        let raw = AllocatedBTreeMap::from_iter_in(&alloc, iter)
436            .handle_alloc_error()
437            .into_inner();
438        Self { alloc, raw }
439    }
440}
441
442impl<K: PartialOrd + Debug, V, B: ArrayLength, A: Allocator> Extend<(K, V)>
443    for CompressedBTreeMap<K, V, B, A>
444where
445    U2: Mul<B>,
446    Prod<U2, B>: ArrayLength,
447    U1: Add<Prod<U2, B>>,
448    Sum<U1, Prod<U2, B>>: ArrayLength,
449{
450    /// Extends the map with the contents of an iterator.
451    ///
452    /// If the iterator yields duplicate keys, the last value wins.
453    ///
454    /// # Panics
455    ///
456    /// Panics if allocation fails during insertion.
457    ///
458    /// # Examples
459    ///
460    /// ```
461    /// use allocated_btree::CompressedBTreeMap;
462    ///
463    /// let mut map = CompressedBTreeMap::new();
464    /// map.insert(1, "a").unwrap();
465    ///
466    /// let more_items = vec![(2, "b"), (3, "c")];
467    /// map.extend(more_items);
468    ///
469    /// assert_eq!(map.len(), 3);
470    /// assert_eq!(map.get(&2), Some(&"b"));
471    /// ```
472    fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
473        use allocated::AllocResultExt;
474
475        for (k, v) in iter {
476            let _ = self.insert(k, v).handle_alloc_error();
477        }
478    }
479}