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}