extarray/
lib.rs

1//
2// Copyright (c) 2025 Nathan Fiedler
3//
4
5//! An implemenation of extensible arrays as described in section 3 of the paper
6//! "Immediate-Access Indexing Using Space-Efficient Extensible Arrays" by
7//! Alistair Moffat and Joel Mackenzie, published in 2022.
8//!
9//! * ACM ISBN 979-8-4007-0021-7/22/12
10//! * https://doi.org/10.1145/3572960.3572984
11//!
12//! # Memory Usage
13//!
14//! An empty resizable array is approximately 88 bytes in size, and while
15//! holding elements it will have a space overhead on the order of O(√N). As
16//! elements are added the array will grow by allocating additional data blocks.
17//! Likewise, as elements are removed from the end of the array, data blocks
18//! will be deallocated as they become empty. At most one empty data block will
19//! be retained as an optimization.
20//!
21//! # Performance
22//!
23//! Most operations are either constant time, log2, or sqrt of the collection
24//! size. However, the lookup operation involves several calculations and as
25//! such the overall performance will be worse than `Vec`. The advantage is that
26//! the memory overhead will be on the order of O(√N) vs O(N).
27//!
28//! # Safety
29//!
30//! Because this data structure is allocating memory, copying bytes using
31//! pointers, and de-allocating memory as needed, there are many `unsafe` blocks
32//! throughout the code.
33
34use std::alloc::{Layout, alloc, dealloc, handle_alloc_error};
35use std::fmt;
36use std::ops::{Index, IndexMut};
37
38/// The number of bits in a usize for computing the block size of segments.
39const WORD_SIZE: u32 = (8 * std::mem::size_of::<usize>()) as u32;
40
41/// The number of bits in a usize plus one to make the math in the mapping()
42/// function easier.
43const BEE_BASE: u32 = WORD_SIZE + 1;
44
45/// Compute the largest segment number for which the segment length is 2^l.
46///
47/// From first paragraph of section 3 of the Moffat 2022 paper.
48#[inline]
49fn last_segment_for_l(l: usize) -> usize {
50    // This is simply the Thabit number minus 1, while adjusting the one-based l
51    // to a zero-based n (the l value is increased by 1 when the segment offset
52    // matches a Thabit number).
53    3 * (1 << (l - 1)) - 2
54}
55
56/// Compute the number of elements that data blocks in level l can hold.
57///
58/// From first paragraph of section 3 of the Moffat 2022 paper.
59#[inline]
60fn datablock_capacity(l: usize) -> usize {
61    1 << l
62}
63
64/// Compute the number of segments of the same length for some l.
65#[inline]
66fn superblock_capacity(l: usize) -> usize {
67    if l == 1 { 2 } else { 3 * (1 << (l - 2)) }
68}
69
70// Compute the total volume V(l) of all segments of length less than or equal to
71// 2^l which is simplified to 2^(2*l)
72//
73// N.B. this does not account for the actual allocated segments, so the value is
74// off by a bit since it only considers the l value.
75#[inline]
76fn capacity_for_l(l: usize) -> usize {
77    1 << (l << 1)
78}
79
80/// Derive the l value for the given segment offset.
81fn l_for_segment(segment: usize) -> usize {
82    // While unstated in the paper, the first segment for each zero-based level
83    // is a Thabit number (https://en.wikipedia.org/wiki/Thabit_number). Note
84    // that in the paper, l is one-based hence the conversion to zero-based n
85    // for the Thabit series.
86    let j = (segment + 1).div_ceil(3);
87    let k = (WORD_SIZE - j.leading_zeros() - 1) as usize;
88    let thabit = 3 * (1 << k) - 1;
89    if segment >= thabit { k + 2 } else { k + 1 }
90}
91
92/// Determine the number of slots in the given segment.
93fn slots_in_segment(segment: usize) -> usize {
94    datablock_capacity(l_for_segment(segment))
95}
96
97/// Compute the mapping from a one-dimensional array index to a two-dimensional
98/// segment number and offset pair.
99///
100/// Algorithm 1 from the Moffat 2022 paper
101fn mapping(v: usize) -> (usize, usize) {
102    // inlining this function is indeed faster, but increases the code size
103    // given the many call sites for this function; what's more, the other
104    // resizable array implementations cannot simply inline the locate function,
105    // so the performance comparisons become skewed
106    let b = if v == 0 {
107        1
108    } else {
109        (BEE_BASE - v.leading_zeros()) >> 1
110    };
111    let segment = (v >> b) + (1 << (b - 1)) - 1;
112    let slot = v & ((1 << b) - 1);
113    (segment, slot)
114}
115
116/// Compute the capacity for an extensible array for a given dope length.
117///
118/// # Time complexity
119///
120/// Constant time.
121fn array_capacity(dope_len: usize) -> usize {
122    if dope_len == 0 {
123        0
124    } else {
125        let last_segment = dope_len - 1;
126        let level = l_for_segment(last_segment);
127        let level_capacity = capacity_for_l(level);
128        let block_capacity = datablock_capacity(level);
129        let most_segment = last_segment_for_l(level);
130        let unalloc_capacity = block_capacity * (most_segment - last_segment);
131        level_capacity - unalloc_capacity
132    }
133}
134
135///
136/// Growable array as described by Moffat and Mackenzie.
137///
138pub struct ExtensibleArray<T> {
139    /// dope vector, holds pointers to allocated segments
140    dope: Vec<*mut T>,
141    /// number of elements stored in the array
142    count: usize,
143    /// the 'l' value from the Moffat 2022 paper
144    level: usize,
145    /// number of non-empty data blocks
146    d: usize,
147    /// number of empty data blocks (either 0 or 1)
148    empty: usize,
149    /// number of elements in last non-empty data block
150    last_db_length: usize,
151    /// capacity of the last non-empty data block
152    last_db_capacity: usize,
153    /// number of data blocks in last non-empty super block
154    last_sb_length: usize,
155    /// capacity of data blocks the last non-empty super block
156    last_sb_capacity: usize,
157}
158
159impl<T> ExtensibleArray<T> {
160    /// Return an empty extensible array with zero capacity.
161    ///
162    /// Note that pre-allocating capacity has no benefit with this data
163    /// structure since append operations are always constant time and no
164    /// reallocation and copy is ever performed.
165    pub fn new() -> Self {
166        Self {
167            dope: vec![],
168            count: 0,
169            level: 1,
170            d: 0,
171            empty: 0,
172            last_db_length: 0,
173            last_db_capacity: 0,
174            last_sb_length: 0,
175            last_sb_capacity: 0,
176        }
177    }
178
179    /// Appends an element to the back of a collection.
180    ///
181    /// # Panics
182    ///
183    /// Panics if a new segment is allocated that would exceed `isize::MAX` _bytes_.
184    ///
185    /// # Time complexity
186    ///
187    /// Constant time.
188    pub fn push(&mut self, value: T) {
189        // if the last non-empty data block is full...
190        if self.last_db_capacity == self.last_db_length {
191            // if the last superblock is full...
192            if self.last_sb_capacity == self.last_sb_length {
193                self.last_sb_capacity = superblock_capacity(self.level);
194                self.last_db_capacity = datablock_capacity(self.level);
195                self.last_sb_length = 0;
196                self.level += 1;
197            }
198            // if there are no empty data blocks...
199            if self.empty == 0 {
200                // allocate new data block, add to dope vector
201                let layout =
202                    Layout::array::<T>(self.last_db_capacity).expect("unexpected overflow");
203                unsafe {
204                    let ptr = alloc(layout).cast::<T>();
205                    if ptr.is_null() {
206                        handle_alloc_error(layout);
207                    }
208                    self.dope.push(ptr);
209                }
210            } else {
211                // reuse the previously allocated empty data block
212                self.empty = 0;
213            }
214            self.d += 1;
215            self.last_sb_length += 1;
216            self.last_db_length = 0;
217        }
218        let (segment, slot) = mapping(self.count);
219        unsafe {
220            std::ptr::write(self.dope[segment].add(slot), value);
221        }
222        self.count += 1;
223        self.last_db_length += 1;
224    }
225
226    /// Appends an element if there is sufficient spare capacity, otherwise an
227    /// error is returned with the element.
228    ///
229    /// # Time complexity
230    ///
231    /// Constant time.
232    pub fn push_within_capacity(&mut self, value: T) -> Result<(), T> {
233        let (segment, _slot) = mapping(self.count);
234        if self.dope.len() <= segment {
235            Err(value)
236        } else {
237            self.push(value);
238            Ok(())
239        }
240    }
241
242    /// Deallocate segments as they become empty.
243    fn shrink(&mut self) {
244        self.count -= 1;
245        self.last_db_length -= 1;
246        // if last data block is empty...
247        if self.last_db_length == 0 {
248            // if there is another empty data block, Deallocate it
249            if self.empty == 1 {
250                let ptr = self.dope.pop().expect("programmer error");
251                let block = self.dope.len();
252                let block_len = slots_in_segment(block);
253                let layout = Layout::array::<T>(block_len).expect("unexpected overflow");
254                unsafe {
255                    dealloc(ptr as *mut u8, layout);
256                }
257            }
258            // leave this last empty data block in case more pushes occur and we
259            // would soon be allocating the same sized block again
260            self.empty = 1;
261            // if the index block is quarter full, shrink to half
262            if self.dope.len() * 4 <= self.dope.capacity() {
263                self.dope.shrink_to(self.dope.capacity() / 2);
264            }
265            // decrement d and number of data blocks in last superblock
266            self.d -= 1;
267            self.last_sb_length -= 1;
268            // if last superblock is empty...
269            if self.last_sb_length == 0 {
270                self.level -= 1;
271                if self.level == 1 {
272                    self.last_sb_capacity = 0;
273                    self.last_db_capacity = 0;
274                } else {
275                    self.last_sb_capacity = superblock_capacity(self.level - 1);
276                    self.last_db_capacity = datablock_capacity(self.level - 1);
277                }
278                // set occupancy of last superblock to full
279                self.last_sb_length = self.last_sb_capacity;
280            }
281            // set occupancy of last data block to full
282            self.last_db_length = self.last_db_capacity;
283        }
284    }
285
286    /// Removes the last element from an array and returns it, or `None` if it
287    /// is empty.
288    ///
289    /// # Time complexity
290    ///
291    /// Constant time.
292    pub fn pop(&mut self) -> Option<T> {
293        if self.count > 0 {
294            let (segment, slot) = mapping(self.count - 1);
295            let value = unsafe { Some((self.dope[segment].add(slot)).read()) };
296            self.shrink();
297            value
298        } else {
299            None
300        }
301    }
302
303    /// Removes and returns the last element from a vector if the predicate
304    /// returns true, or None if the predicate returns false or the vector is
305    /// empty (the predicate will not be called in that case).
306    ///
307    /// # Time complexity
308    ///
309    /// Constant time.
310    pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T> {
311        if self.count == 0 {
312            None
313        } else if let Some(last) = self.get_mut(self.count - 1) {
314            if predicate(last) { self.pop() } else { None }
315        } else {
316            None
317        }
318    }
319
320    /// Return the number of elements in the array.
321    ///
322    /// # Time complexity
323    ///
324    /// Constant time.
325    pub fn len(&self) -> usize {
326        self.count
327    }
328
329    /// Returns the total number of elements the extensible array can hold
330    /// without reallocating.
331    ///
332    /// # Time complexity
333    ///
334    /// log2(count)/2
335    pub fn capacity(&self) -> usize {
336        array_capacity(self.dope.len())
337    }
338
339    /// Returns true if the array has a length of 0.
340    ///
341    /// # Time complexity
342    ///
343    /// Constant time.
344    pub fn is_empty(&self) -> bool {
345        self.count == 0
346    }
347
348    /// Retrieve a reference to the element at the given offset.
349    ///
350    /// # Time complexity
351    ///
352    /// Constant time.
353    pub fn get(&self, index: usize) -> Option<&T> {
354        if index >= self.count {
355            None
356        } else {
357            let (segment, slot) = mapping(index);
358            unsafe { (self.dope[segment].add(slot)).as_ref() }
359        }
360    }
361
362    /// Returns a mutable reference to an element.
363    ///
364    /// # Time complexity
365    ///
366    /// Constant time.
367    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
368        if index >= self.count {
369            None
370        } else {
371            let (segment, slot) = mapping(index);
372            unsafe { (self.dope[segment].add(slot)).as_mut() }
373        }
374    }
375
376    /// Removes an element from the vector and returns it.
377    ///
378    /// The removed element is replaced by the last element of the vector.
379    ///
380    /// This does not preserve ordering of the remaining elements.
381    ///
382    /// # Time complexity
383    ///
384    /// Constant time.
385    pub fn swap_remove(&mut self, index: usize) -> T {
386        if index >= self.count {
387            panic!(
388                "swap_remove index (is {index}) should be < len (is {})",
389                self.count
390            );
391        }
392        // retreive the value at index before overwriting
393        let (segment, slot) = mapping(index);
394        unsafe {
395            let index_ptr = self.dope[segment].add(slot);
396            let value = index_ptr.read();
397            // find the pointer of the last element and copy to index pointer
398            let (segment, slot) = mapping(self.count - 1);
399            let last_ptr = self.dope[segment].add(slot);
400            std::ptr::copy(last_ptr, index_ptr, 1);
401            self.shrink();
402            value
403        }
404    }
405
406    /// Returns an iterator over the extensible array.
407    ///
408    /// The iterator yields all items from start to end.
409    pub fn iter(&self) -> ExtArrayIter<'_, T> {
410        ExtArrayIter {
411            array: self,
412            index: 0,
413        }
414    }
415
416    /// Clears the extensible array, removing and dropping all values and
417    /// deallocating all previously allocated segments.
418    ///
419    /// # Time complexity
420    ///
421    /// O(n) if elements are droppable, otherwise O(sqrt(n))
422    pub fn clear(&mut self) {
423        use std::ptr::{drop_in_place, slice_from_raw_parts_mut};
424
425        if self.count > 0 {
426            if std::mem::needs_drop::<T>() {
427                // find the last segment that contains values and drop them
428                let (last_segment, last_slot) = mapping(self.count - 1);
429                unsafe {
430                    // last_slot is pointing at the last element, need to add
431                    // one to include it in the slice
432                    drop_in_place(slice_from_raw_parts_mut(
433                        self.dope[last_segment],
434                        last_slot + 1,
435                    ));
436                }
437
438                // drop the values in all of the preceding segments
439                let mut segment = 0;
440                for level in 1..=self.level {
441                    let level_limit = last_segment_for_l(level);
442                    let segment_len = datablock_capacity(level);
443                    while segment <= level_limit && segment < last_segment {
444                        unsafe {
445                            drop_in_place(slice_from_raw_parts_mut(
446                                self.dope[segment],
447                                segment_len,
448                            ));
449                        }
450                        segment += 1;
451                    }
452                }
453            }
454
455            self.level = 1;
456            self.count = 0;
457            self.d = 0;
458            self.empty = 0;
459            self.last_db_length = 0;
460            self.last_db_capacity = 0;
461            self.last_sb_length = 0;
462            self.last_sb_capacity = 0;
463        }
464
465        // deallocate all segments using the index as the source of truth
466        for segment in 0..self.dope.len() {
467            let segment_len = slots_in_segment(segment);
468            let layout = Layout::array::<T>(segment_len).expect("unexpected overflow");
469            unsafe {
470                dealloc(self.dope[segment] as *mut u8, layout);
471            }
472        }
473        self.dope.clear();
474    }
475}
476
477impl<T> Default for ExtensibleArray<T> {
478    fn default() -> Self {
479        Self::new()
480    }
481}
482
483impl<T> fmt::Display for ExtensibleArray<T> {
484    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485        write!(
486            f,
487            "ExtensibleArray(n: {}, s: {}, d: {}, e: {}, dl: {}, dc: {}, sl: {}, sc: {})",
488            self.count,
489            self.level,
490            self.d,
491            self.empty,
492            self.last_db_length,
493            self.last_db_capacity,
494            self.last_sb_length,
495            self.last_sb_capacity
496        )
497    }
498}
499
500impl<T> Drop for ExtensibleArray<T> {
501    fn drop(&mut self) {
502        self.clear();
503    }
504}
505
506impl<T> Index<usize> for ExtensibleArray<T> {
507    type Output = T;
508
509    fn index(&self, index: usize) -> &Self::Output {
510        let Some(item) = self.get(index) else {
511            panic!("index out of bounds: {}", index);
512        };
513        item
514    }
515}
516
517impl<T> IndexMut<usize> for ExtensibleArray<T> {
518    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
519        let Some(item) = self.get_mut(index) else {
520            panic!("index out of bounds: {}", index);
521        };
522        item
523    }
524}
525
526impl<A> FromIterator<A> for ExtensibleArray<A> {
527    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
528        let mut arr: ExtensibleArray<A> = ExtensibleArray::new();
529        for value in iter {
530            arr.push(value)
531        }
532        arr
533    }
534}
535
536/// Immutable extensible array iterator.
537pub struct ExtArrayIter<'a, T> {
538    array: &'a ExtensibleArray<T>,
539    index: usize,
540}
541
542impl<'a, T> Iterator for ExtArrayIter<'a, T> {
543    type Item = &'a T;
544
545    fn next(&mut self) -> Option<Self::Item> {
546        let value = self.array.get(self.index);
547        self.index += 1;
548        value
549    }
550}
551
552/// An iterator that moves out of a extensible array.
553pub struct ExtArrayIntoIter<T> {
554    index: usize,
555    count: usize,
556    level: usize,
557    dope: Vec<*mut T>,
558}
559
560impl<T> Iterator for ExtArrayIntoIter<T> {
561    type Item = T;
562
563    fn next(&mut self) -> Option<Self::Item> {
564        if self.index < self.count {
565            let (segment, slot) = mapping(self.index);
566            self.index += 1;
567            unsafe { Some((self.dope[segment].add(slot)).read()) }
568        } else {
569            None
570        }
571    }
572}
573
574impl<T> Drop for ExtArrayIntoIter<T> {
575    fn drop(&mut self) {
576        use std::ptr::{drop_in_place, slice_from_raw_parts_mut};
577
578        if self.count > 0 && std::mem::needs_drop::<T>() {
579            let (first_segment, first_slot) = mapping(self.index);
580            let (last_segment, last_slot) = mapping(self.count - 1);
581            if first_segment == last_segment {
582                // special-case, remaining values are in only one segment
583                if first_slot <= last_slot {
584                    unsafe {
585                        // last_slot is pointing at the last element, need to
586                        // add one to include it in the slice
587                        drop_in_place(slice_from_raw_parts_mut(
588                            self.dope[first_segment].add(first_slot),
589                            last_slot - first_slot + 1,
590                        ));
591                    }
592                }
593            } else if first_segment < last_segment {
594                // drop the remaining values in the first segment
595                let segment_len = slots_in_segment(first_segment);
596                if segment_len < self.count {
597                    unsafe {
598                        drop_in_place(slice_from_raw_parts_mut(
599                            self.dope[first_segment].add(first_slot),
600                            segment_len - first_slot,
601                        ));
602                    }
603                }
604
605                // drop the values in the last segment
606                unsafe {
607                    drop_in_place(slice_from_raw_parts_mut(
608                        self.dope[last_segment],
609                        last_slot + 1,
610                    ));
611                }
612
613                // drop the values in all of the other segments
614                for segment in first_segment + 1..last_segment {
615                    let segment_len = slots_in_segment(segment);
616                    unsafe {
617                        drop_in_place(slice_from_raw_parts_mut(self.dope[segment], segment_len));
618                    }
619                }
620            }
621        }
622
623        // deallocate all of the segments
624        for segment in 0..self.dope.len() {
625            let segment_len = slots_in_segment(segment);
626            let layout = Layout::array::<T>(segment_len).expect("unexpected overflow");
627            unsafe {
628                dealloc(self.dope[segment] as *mut u8, layout);
629            }
630        }
631
632        self.dope.clear();
633        self.index = 0;
634        self.level = 1;
635        self.count = 0;
636    }
637}
638
639impl<T> IntoIterator for ExtensibleArray<T> {
640    type Item = T;
641    type IntoIter = ExtArrayIntoIter<Self::Item>;
642
643    fn into_iter(self) -> Self::IntoIter {
644        let mut me = std::mem::ManuallyDrop::new(self);
645        let dope = std::mem::take(&mut me.dope);
646        ExtArrayIntoIter {
647            index: 0,
648            count: me.count,
649            level: me.level,
650            dope,
651        }
652    }
653}
654
655#[cfg(test)]
656mod tests {
657    use super::*;
658
659    #[test]
660    fn test_push_get_one_item() {
661        let item = String::from("hello world");
662        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
663        assert_eq!(sut.len(), 0);
664        assert!(sut.is_empty());
665        sut.push(item);
666        assert_eq!(sut.len(), 1);
667        assert!(!sut.is_empty());
668        let maybe = sut.get(0);
669        assert!(maybe.is_some());
670        let actual = maybe.unwrap();
671        assert_eq!("hello world", actual);
672        let missing = sut.get(10);
673        assert!(missing.is_none());
674    }
675
676    #[test]
677    fn test_push_get_several_strings() {
678        let inputs = [
679            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
680        ];
681        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
682        for item in inputs {
683            sut.push(item.to_owned());
684        }
685        assert_eq!(sut.len(), 9);
686        for (idx, item) in inputs.iter().enumerate() {
687            let maybe = sut.get(idx);
688            assert!(maybe.is_some(), "{idx} is none");
689            let actual = maybe.unwrap();
690            assert_eq!(item, actual);
691        }
692        let maybe = sut.get(10);
693        assert!(maybe.is_none());
694        assert_eq!(sut[3], "four");
695    }
696
697    #[test]
698    fn test_push_get_hundred_ints() {
699        let mut sut: ExtensibleArray<i32> = ExtensibleArray::new();
700        for value in 0..100 {
701            sut.push(value);
702        }
703        assert_eq!(sut.len(), 100);
704        for idx in 0..100 {
705            let maybe = sut.get(idx);
706            assert!(maybe.is_some(), "{idx} is none");
707            let actual = maybe.unwrap();
708            assert_eq!(idx, *actual as usize);
709        }
710        assert_eq!(sut[99], 99);
711    }
712
713    #[test]
714    fn test_len_and_capacity() {
715        let mut sut: ExtensibleArray<i32> = ExtensibleArray::new();
716        assert_eq!(sut.len(), 0);
717        assert_eq!(sut.capacity(), 0);
718        for value in 0..100 {
719            sut.push(value);
720        }
721        assert_eq!(sut.len(), 100);
722        // 2 * 2 + 4 * 3 + 8 * 6 + 16 * 3
723        assert_eq!(sut.capacity(), 112);
724    }
725
726    #[test]
727    fn test_pop_and_shrink() {
728        let mut sut: ExtensibleArray<usize> = ExtensibleArray::new();
729        for value in 0..8 {
730            sut.push(value);
731        }
732        assert_eq!(sut.len(), 8);
733        assert_eq!(sut.capacity(), 8);
734        while !sut.is_empty() {
735            sut.pop();
736        }
737        assert_eq!(sut.len(), 0);
738        // empty block has size 2
739        assert_eq!(sut.capacity(), 2);
740    }
741
742    #[test]
743    fn test_push_within_capacity() {
744        // empty array has no allocated space
745        let mut sut: ExtensibleArray<u32> = ExtensibleArray::new();
746        assert_eq!(sut.push_within_capacity(101), Err(101));
747        sut.push(10);
748        assert_eq!(sut.push_within_capacity(101), Ok(()));
749
750        // edge case (first segment is 2 elements long)
751        let mut sut: ExtensibleArray<u32> = ExtensibleArray::new();
752        sut.push(1);
753        assert_eq!(sut.push_within_capacity(2), Ok(()));
754        assert_eq!(sut.push_within_capacity(3), Err(3));
755    }
756
757    #[test]
758    fn test_get_mut_index_mut() {
759        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
760        sut.push(String::from("first"));
761        sut.push(String::from("second"));
762        sut.push(String::from("third"));
763        if let Some(value) = sut.get_mut(1) {
764            value.push_str(" place");
765        } else {
766            panic!("get_mut() returned None")
767        }
768        assert_eq!(sut[1], "second place");
769        sut[2] = "third planet".into();
770        assert_eq!(sut[2], "third planet");
771    }
772
773    #[test]
774    #[should_panic(expected = "index out of bounds:")]
775    fn test_index_out_of_bounds() {
776        let mut sut: ExtensibleArray<i32> = ExtensibleArray::new();
777        sut.push(10);
778        sut.push(20);
779        let _ = sut[2];
780    }
781
782    #[test]
783    #[should_panic(expected = "index out of bounds:")]
784    fn test_index_mut_out_of_bounds() {
785        let mut sut: ExtensibleArray<i32> = ExtensibleArray::new();
786        sut.push(10);
787        sut.push(20);
788        sut[2] = 30;
789    }
790
791    #[test]
792    fn test_push_many_pop_all_verify() {
793        // push many values, then pop all off and verify
794        let mut sut: ExtensibleArray<usize> = ExtensibleArray::new();
795        assert_eq!(sut.len(), 0);
796        assert_eq!(sut.capacity(), 0);
797        for value in 0..16384 {
798            sut.push(value);
799        }
800        assert_eq!(sut.len(), 16384);
801        assert_eq!(sut.capacity(), 16384);
802        for value in (0..16384).rev() {
803            assert_eq!(sut.pop(), Some(value));
804        }
805        assert_eq!(sut.len(), 0);
806        // empty block has size 2
807        assert_eq!(sut.capacity(), 2);
808    }
809
810    #[test]
811    fn test_push_pop_grow_shrink_empty_block() {
812        // test the handling of the extra empty data block when pushing and
813        // popping values that cross over a level boundary and thereby the extra
814        // empty data block is a different size than the newly emptied data
815        // block (push enough to reach level 3, then pop enough to get to level
816        // 2, then push again)
817        let mut sut: ExtensibleArray<usize> = ExtensibleArray::new();
818        assert_eq!(sut.len(), 0);
819        assert_eq!(sut.capacity(), 0);
820        for value in 0..20 {
821            sut.push(value);
822        }
823        assert_eq!(sut.len(), 20);
824        assert_eq!(sut.capacity(), 24);
825        for _ in 0..5 {
826            sut.pop();
827        }
828        assert_eq!(sut.len(), 15);
829        assert_eq!(sut.capacity(), 24);
830        for _ in 0..5 {
831            sut.pop();
832        }
833        assert_eq!(sut.len(), 10);
834        assert_eq!(sut.capacity(), 16);
835        for value in 10..22 {
836            sut.push(value);
837        }
838        assert_eq!(sut.len(), 22);
839        assert_eq!(sut.capacity(), 24);
840        for (idx, elem) in sut.iter().enumerate() {
841            assert_eq!(idx, *elem);
842        }
843
844        // try to trigger any clear/drop logic
845        sut.clear();
846    }
847
848    #[test]
849    fn test_push_pop_iter() {
850        let inputs = [
851            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
852        ];
853        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
854        assert!(sut.pop().is_none());
855        for item in inputs {
856            sut.push(item.to_owned());
857        }
858        assert_eq!(sut.len(), 9);
859        for (idx, elem) in sut.iter().enumerate() {
860            assert_eq!(inputs[idx], elem);
861        }
862        let maybe = sut.pop();
863        assert!(maybe.is_some());
864        let value = maybe.unwrap();
865        assert_eq!(value, "nine");
866        assert_eq!(sut.len(), 8);
867        sut.push(String::from("nine"));
868        assert_eq!(sut.len(), 9);
869        for (idx, elem) in sut.iter().enumerate() {
870            assert_eq!(inputs[idx], elem);
871        }
872
873        // pop everything and add back again
874        while !sut.is_empty() {
875            sut.pop();
876        }
877        assert_eq!(sut.len(), 0);
878        for item in inputs {
879            sut.push(item.to_owned());
880        }
881        assert_eq!(sut.len(), 9);
882        for (idx, elem) in sut.iter().enumerate() {
883            assert_eq!(inputs[idx], elem);
884        }
885    }
886
887    #[test]
888    fn test_pop_if() {
889        let mut sut: ExtensibleArray<u32> = ExtensibleArray::new();
890        assert!(sut.pop_if(|_| panic!("should not be called")).is_none());
891        for value in 0..10 {
892            sut.push(value);
893        }
894        assert!(sut.pop_if(|_| false).is_none());
895        let maybe = sut.pop_if(|v| *v == 9);
896        assert_eq!(maybe.unwrap(), 9);
897        assert!(sut.pop_if(|v| *v == 9).is_none());
898    }
899
900    #[test]
901    fn test_swap_remove_single_segment() {
902        let mut sut: ExtensibleArray<u32> = ExtensibleArray::new();
903        sut.push(1);
904        sut.push(2);
905        assert_eq!(sut.len(), 2);
906        let one = sut.swap_remove(0);
907        assert_eq!(one, 1);
908        assert_eq!(sut[0], 2);
909    }
910
911    #[test]
912    fn test_swap_remove_multiple_segments() {
913        let mut sut: ExtensibleArray<u32> = ExtensibleArray::new();
914        for value in 0..512 {
915            sut.push(value);
916        }
917        assert_eq!(sut.len(), 512);
918        let eighty = sut.swap_remove(80);
919        assert_eq!(eighty, 80);
920        assert_eq!(sut.pop(), Some(510));
921        assert_eq!(sut[80], 511);
922    }
923
924    #[test]
925    #[should_panic(expected = "swap_remove index (is 0) should be < len (is 0)")]
926    fn test_swap_remove_panic_empty() {
927        let mut sut: ExtensibleArray<u32> = ExtensibleArray::new();
928        sut.swap_remove(0);
929    }
930
931    #[test]
932    #[should_panic(expected = "swap_remove index (is 1) should be < len (is 1)")]
933    fn test_swap_remove_panic_range_edge() {
934        let mut sut: ExtensibleArray<u32> = ExtensibleArray::new();
935        sut.push(1);
936        sut.swap_remove(1);
937    }
938
939    #[test]
940    #[should_panic(expected = "swap_remove index (is 2) should be < len (is 1)")]
941    fn test_swap_remove_panic_range_exceed() {
942        let mut sut: ExtensibleArray<u32> = ExtensibleArray::new();
943        sut.push(1);
944        sut.swap_remove(2);
945    }
946
947    #[test]
948    fn test_clear_and_reuse_tiny() {
949        // clear an array that allocated only one segment
950        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
951        sut.push(String::from("one"));
952        sut.push(String::from("two"));
953        assert_eq!(sut.len(), 2);
954        sut.clear();
955        assert_eq!(sut.len(), 0);
956        sut.push(String::from("one"));
957        sut.push(String::from("two"));
958        assert_eq!(sut.len(), 2);
959        // implicitly drop()
960    }
961
962    #[test]
963    fn test_clear_and_reuse_ints() {
964        let mut sut: ExtensibleArray<i32> = ExtensibleArray::new();
965        for value in 0..512 {
966            sut.push(value);
967        }
968        assert_eq!(sut.len(), 512);
969        sut.clear();
970        assert_eq!(sut.len(), 0);
971        for value in 0..512 {
972            sut.push(value);
973        }
974        for idx in 0..512 {
975            let maybe = sut.get(idx);
976            assert!(maybe.is_some(), "{idx} is none");
977            let actual = maybe.unwrap();
978            assert_eq!(idx, *actual as usize);
979        }
980    }
981
982    #[test]
983    fn test_clear_and_reuse_strings() {
984        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
985        for _ in 0..512 {
986            let value = ulid::Ulid::new().to_string();
987            sut.push(value);
988        }
989        assert_eq!(sut.len(), 512);
990        sut.clear();
991        assert_eq!(sut.len(), 0);
992        for _ in 0..512 {
993            let value = ulid::Ulid::new().to_string();
994            sut.push(value);
995        }
996        assert_eq!(sut.len(), 512);
997        // implicitly drop()
998    }
999
1000    #[test]
1001    fn test_push_get_thousands_structs() {
1002        struct MyData {
1003            a: u64,
1004            b: i32,
1005        }
1006        let mut sut: ExtensibleArray<MyData> = ExtensibleArray::new();
1007        for value in 0..88_888i32 {
1008            sut.push(MyData {
1009                a: value as u64,
1010                b: value,
1011            });
1012        }
1013        assert_eq!(sut.len(), 88_888);
1014        for idx in 0..88_888i32 {
1015            let maybe = sut.get(idx as usize);
1016            assert!(maybe.is_some(), "{idx} is none");
1017            let actual = maybe.unwrap();
1018            assert_eq!(idx as u64, actual.a);
1019            assert_eq!(idx, actual.b);
1020        }
1021    }
1022
1023    #[test]
1024    fn test_from_iterator() {
1025        let mut inputs: Vec<i32> = Vec::new();
1026        for value in 0..10_000 {
1027            inputs.push(value);
1028        }
1029        let sut: ExtensibleArray<i32> = inputs.into_iter().collect();
1030        assert_eq!(sut.len(), 10_000);
1031        for idx in 0..10_000i32 {
1032            let maybe = sut.get(idx as usize);
1033            assert!(maybe.is_some(), "{idx} is none");
1034            let actual = maybe.unwrap();
1035            assert_eq!(idx, *actual);
1036        }
1037    }
1038
1039    #[test]
1040    fn test_push_get_many_ints() {
1041        let mut sut: ExtensibleArray<i32> = ExtensibleArray::new();
1042        for value in 0..1_000_000 {
1043            sut.push(value);
1044        }
1045        assert_eq!(sut.len(), 1_000_000);
1046        for idx in 0..1_000_000 {
1047            let maybe = sut.get(idx);
1048            assert!(maybe.is_some(), "{idx} is none");
1049            let actual = maybe.unwrap();
1050            assert_eq!(idx, *actual as usize);
1051        }
1052        assert_eq!(sut[99_999], 99_999);
1053    }
1054
1055    #[test]
1056    fn test_iterator() {
1057        let inputs = [
1058            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
1059        ];
1060        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
1061        for item in inputs {
1062            sut.push(item.to_owned());
1063        }
1064        for (idx, elem) in sut.iter().enumerate() {
1065            assert_eq!(inputs[idx], elem);
1066        }
1067    }
1068
1069    #[test]
1070    fn test_into_iterator() {
1071        // an array that only requires a single segment
1072        let inputs = [
1073            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
1074        ];
1075        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
1076        for item in inputs {
1077            sut.push(item.to_owned());
1078        }
1079        for (idx, elem) in sut.into_iter().enumerate() {
1080            assert_eq!(inputs[idx], elem);
1081        }
1082        // sut.len(); // error: ownership of sut was moved
1083    }
1084
1085    #[test]
1086    fn test_into_iterator_edge_case() {
1087        // iterate to the end (of the last data block)
1088        let inputs = [
1089            "one", "two", "three", "four", "five", "six", "seven", "eight",
1090        ];
1091        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
1092        for item in inputs {
1093            sut.push(item.to_owned());
1094        }
1095        for (idx, elem) in sut.into_iter().enumerate() {
1096            assert_eq!(inputs[idx], elem);
1097        }
1098        // sut.len(); // error: ownership of sut was moved
1099    }
1100
1101    #[test]
1102    fn test_into_iterator_drop_empty() {
1103        let sut: ExtensibleArray<String> = ExtensibleArray::new();
1104        assert_eq!(sut.into_iter().count(), 0);
1105    }
1106
1107    #[test]
1108    fn test_into_iterator_drop_tiny() {
1109        // an array that only requires a single segment and only some need to be
1110        // dropped after partially iterating the values
1111        let inputs = [
1112            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
1113        ];
1114        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
1115        for item in inputs {
1116            sut.push(item.to_owned());
1117        }
1118        for (idx, _) in sut.into_iter().enumerate() {
1119            if idx > 2 {
1120                break;
1121            }
1122        }
1123        // implicitly drop()
1124    }
1125
1126    #[test]
1127    fn test_into_iterator_drop_large() {
1128        // by adding 512 values and iterating less than 64 times, there will be
1129        // values in the first segment and some in the last segment, and two
1130        // segments inbetween that all need to be dropped
1131        let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
1132        for _ in 0..512 {
1133            let value = ulid::Ulid::new().to_string();
1134            sut.push(value);
1135        }
1136        for (idx, _) in sut.into_iter().enumerate() {
1137            if idx >= 30 {
1138                break;
1139            }
1140        }
1141        // implicitly drop()
1142    }
1143
1144    #[test]
1145    fn test_push_get_many_instances_ints() {
1146        // test allocating, filling, and then dropping many instances
1147        for _ in 0..1_000 {
1148            let mut sut: ExtensibleArray<usize> = ExtensibleArray::new();
1149            for value in 0..10_000 {
1150                sut.push(value);
1151            }
1152            assert_eq!(sut.len(), 10_000);
1153        }
1154    }
1155
1156    #[test]
1157    fn test_push_get_many_instances_strings() {
1158        // test allocating, filling, and then dropping many instances
1159        for _ in 0..1_000 {
1160            let mut sut: ExtensibleArray<String> = ExtensibleArray::new();
1161            for _ in 0..1_000 {
1162                let value = ulid::Ulid::new().to_string();
1163                sut.push(value);
1164            }
1165            assert_eq!(sut.len(), 1_000);
1166        }
1167    }
1168
1169    #[test]
1170    fn test_last_segment() {
1171        // l cannot be zero, but segment numbers are 0-based
1172        assert_eq!(last_segment_for_l(1), 1);
1173        assert_eq!(last_segment_for_l(2), 4);
1174        assert_eq!(last_segment_for_l(3), 10);
1175        assert_eq!(last_segment_for_l(4), 22);
1176        assert_eq!(last_segment_for_l(5), 46);
1177        assert_eq!(last_segment_for_l(6), 94);
1178        assert_eq!(last_segment_for_l(7), 190);
1179        assert_eq!(last_segment_for_l(8), 382);
1180        assert_eq!(last_segment_for_l(9), 766);
1181        assert_eq!(last_segment_for_l(10), 1534);
1182        assert_eq!(last_segment_for_l(11), 3070);
1183        assert_eq!(last_segment_for_l(12), 6142);
1184        assert_eq!(last_segment_for_l(13), 12286);
1185        assert_eq!(last_segment_for_l(14), 24574);
1186        assert_eq!(last_segment_for_l(15), 49150);
1187        assert_eq!(last_segment_for_l(16), 98302);
1188    }
1189
1190    #[test]
1191    fn test_segment_len() {
1192        // l cannot be zero, but segment numbers are 0-based
1193        assert_eq!(datablock_capacity(1), 2);
1194        assert_eq!(datablock_capacity(2), 4);
1195        assert_eq!(datablock_capacity(3), 8);
1196        assert_eq!(datablock_capacity(4), 16);
1197        assert_eq!(datablock_capacity(5), 32);
1198        assert_eq!(datablock_capacity(6), 64);
1199        assert_eq!(datablock_capacity(7), 128);
1200        assert_eq!(datablock_capacity(8), 256);
1201        assert_eq!(datablock_capacity(9), 512);
1202        assert_eq!(datablock_capacity(10), 1024);
1203        assert_eq!(datablock_capacity(11), 2048);
1204        assert_eq!(datablock_capacity(12), 4096);
1205        assert_eq!(datablock_capacity(13), 8192);
1206        assert_eq!(datablock_capacity(14), 16384);
1207        assert_eq!(datablock_capacity(15), 32768);
1208        assert_eq!(datablock_capacity(16), 65536);
1209    }
1210
1211    #[test]
1212    fn test_mapping() {
1213        assert_eq!(mapping(0), (0, 0));
1214        assert_eq!(mapping(1), (0, 1));
1215        assert_eq!(mapping(2), (1, 0));
1216        assert_eq!(mapping(3), (1, 1));
1217        assert_eq!(mapping(4), (2, 0));
1218        assert_eq!(mapping(5), (2, 1));
1219        assert_eq!(mapping(6), (2, 2));
1220        assert_eq!(mapping(7), (2, 3));
1221        assert_eq!(mapping(8), (3, 0));
1222        assert_eq!(mapping(9), (3, 1));
1223        assert_eq!(mapping(10), (3, 2));
1224        assert_eq!(mapping(11), (3, 3));
1225        assert_eq!(mapping(12), (4, 0));
1226        assert_eq!(mapping(72), (11, 8));
1227        assert_eq!(mapping(248), (22, 8));
1228        assert_eq!(mapping(4567), (98, 87)); // from the Moffat 2022 paper
1229        assert_eq!(mapping(1_048_576), (1535, 0));
1230        assert_eq!(mapping(2_000_000), (1999, 1152));
1231        assert_eq!(mapping(4_194_304), (3071, 0));
1232        assert_eq!(mapping(16_777_216), (6143, 0));
1233        assert_eq!(mapping(67_108_864), (12287, 0));
1234        assert_eq!(mapping(268_435_456), (24575, 0));
1235        assert_eq!(mapping(1_073_741_824), (49151, 0));
1236        assert_eq!(mapping(2_000_000_000), (63284, 37888));
1237        assert_eq!(mapping(2_147_483_647), (65534, 65535));
1238        assert_eq!(mapping(2_147_483_648), (65535, 0));
1239        assert_eq!(mapping(4_294_967_295), (98302, 65535));
1240    }
1241
1242    #[test]
1243    fn test_capacity() {
1244        assert_eq!(array_capacity(0), 0);
1245        assert_eq!(array_capacity(1), 2);
1246        assert_eq!(array_capacity(2), 4);
1247        assert_eq!(array_capacity(3), 8);
1248        assert_eq!(array_capacity(4), 12);
1249        assert_eq!(array_capacity(5), 16);
1250        assert_eq!(array_capacity(8), 40);
1251        assert_eq!(array_capacity(12), 80);
1252    }
1253
1254    #[test]
1255    fn test_l_for_segment() {
1256        assert_eq!(l_for_segment(0), 1);
1257        assert_eq!(l_for_segment(1), 1);
1258        assert_eq!(l_for_segment(2), 2);
1259        assert_eq!(l_for_segment(3), 2);
1260        assert_eq!(l_for_segment(4), 2);
1261        assert_eq!(l_for_segment(5), 3);
1262        assert_eq!(l_for_segment(6), 3);
1263        assert_eq!(l_for_segment(7), 3);
1264        assert_eq!(l_for_segment(8), 3);
1265        assert_eq!(l_for_segment(9), 3);
1266        assert_eq!(l_for_segment(10), 3);
1267        assert_eq!(l_for_segment(11), 4);
1268        assert_eq!(l_for_segment(12), 4);
1269        assert_eq!(l_for_segment(13), 4);
1270        assert_eq!(l_for_segment(14), 4);
1271        assert_eq!(l_for_segment(15), 4);
1272        assert_eq!(l_for_segment(16), 4);
1273        assert_eq!(l_for_segment(17), 4);
1274        assert_eq!(l_for_segment(18), 4);
1275        assert_eq!(l_for_segment(19), 4);
1276        assert_eq!(l_for_segment(20), 4);
1277        assert_eq!(l_for_segment(21), 4);
1278        assert_eq!(l_for_segment(22), 4);
1279        assert_eq!(l_for_segment(23), 5);
1280        assert_eq!(l_for_segment(47), 6);
1281        assert_eq!(l_for_segment(94), 6);
1282        assert_eq!(l_for_segment(95), 7);
1283        assert_eq!(l_for_segment(190), 7);
1284        assert_eq!(l_for_segment(191), 8);
1285        assert_eq!(l_for_segment(382), 8);
1286        assert_eq!(l_for_segment(383), 9);
1287        assert_eq!(l_for_segment(767), 10);
1288        assert_eq!(l_for_segment(1534), 10);
1289        assert_eq!(l_for_segment(1535), 11);
1290        assert_eq!(l_for_segment(3070), 11);
1291        assert_eq!(l_for_segment(3071), 12);
1292        assert_eq!(l_for_segment(6142), 12);
1293        assert_eq!(l_for_segment(6143), 13);
1294        assert_eq!(l_for_segment(12286), 13);
1295        assert_eq!(l_for_segment(12287), 14);
1296        assert_eq!(l_for_segment(24574), 14);
1297        assert_eq!(l_for_segment(24575), 15);
1298        assert_eq!(l_for_segment(49150), 15);
1299        assert_eq!(l_for_segment(49151), 16);
1300        assert_eq!(l_for_segment(98303), 17);
1301        assert_eq!(l_for_segment(196607), 18);
1302        assert_eq!(l_for_segment(393215), 19);
1303        assert_eq!(l_for_segment(786431), 20);
1304        assert_eq!(l_for_segment(1572863), 21);
1305    }
1306
1307    #[test]
1308    fn test_slots_in_segment() {
1309        assert_eq!(slots_in_segment(0), 2);
1310        assert_eq!(slots_in_segment(1), 2);
1311        assert_eq!(slots_in_segment(2), 4);
1312        assert_eq!(slots_in_segment(3), 4);
1313        assert_eq!(slots_in_segment(4), 4);
1314        assert_eq!(slots_in_segment(5), 8);
1315        assert_eq!(slots_in_segment(6), 8);
1316        assert_eq!(slots_in_segment(7), 8);
1317        assert_eq!(slots_in_segment(8), 8);
1318        assert_eq!(slots_in_segment(9), 8);
1319        assert_eq!(slots_in_segment(10), 8);
1320        assert_eq!(slots_in_segment(11), 16);
1321        assert_eq!(slots_in_segment(30), 32);
1322        assert_eq!(slots_in_segment(80), 64);
1323        assert_eq!(slots_in_segment(170), 128);
1324        assert_eq!(slots_in_segment(350), 256);
1325        assert_eq!(slots_in_segment(707), 512);
1326        assert_eq!(slots_in_segment(1400), 1024);
1327        assert_eq!(slots_in_segment(3000), 2048);
1328        assert_eq!(slots_in_segment(6000), 4096);
1329        assert_eq!(slots_in_segment(12000), 8192);
1330        assert_eq!(slots_in_segment(24000), 16384);
1331        assert_eq!(slots_in_segment(49000), 32768);
1332        assert_eq!(slots_in_segment(98000), 65536);
1333    }
1334
1335    #[test]
1336    #[should_panic(expected = "attempt to add with overflow")]
1337    fn test_slots_in_segment_bounds() {
1338        // not a practical test, but eventually the function will overflow
1339        slots_in_segment(usize::MAX);
1340    }
1341
1342    #[test]
1343    fn test_capacity_for_l() {
1344        assert_eq!(capacity_for_l(1), 4);
1345        assert_eq!(capacity_for_l(2), 16);
1346        assert_eq!(capacity_for_l(3), 64);
1347        assert_eq!(capacity_for_l(4), 256);
1348        assert_eq!(capacity_for_l(5), 1024);
1349        assert_eq!(capacity_for_l(6), 4096);
1350        assert_eq!(capacity_for_l(7), 16384);
1351        assert_eq!(capacity_for_l(8), 65536);
1352        assert_eq!(capacity_for_l(9), 262_144);
1353        assert_eq!(capacity_for_l(10), 1_048_576);
1354        assert_eq!(capacity_for_l(11), 4_194_304);
1355        assert_eq!(capacity_for_l(12), 16_777_216);
1356        assert_eq!(capacity_for_l(13), 67_108_864);
1357        assert_eq!(capacity_for_l(14), 268_435_456);
1358        assert_eq!(capacity_for_l(15), 1_073_741_824);
1359        assert_eq!(capacity_for_l(16), 4_294_967_296);
1360    }
1361
1362    #[test]
1363    fn test_superblock_capacity() {
1364        // l cannot be zero, but segment numbers are 0-based
1365        assert_eq!(superblock_capacity(1), 2);
1366        assert_eq!(superblock_capacity(2), 3);
1367        assert_eq!(superblock_capacity(3), 6);
1368        assert_eq!(superblock_capacity(4), 12);
1369        assert_eq!(superblock_capacity(5), 24);
1370        assert_eq!(superblock_capacity(6), 48);
1371        assert_eq!(superblock_capacity(7), 96);
1372        assert_eq!(superblock_capacity(8), 192);
1373        assert_eq!(superblock_capacity(9), 384);
1374        assert_eq!(superblock_capacity(10), 768);
1375        assert_eq!(superblock_capacity(11), 1536);
1376        assert_eq!(superblock_capacity(12), 3072);
1377        assert_eq!(superblock_capacity(13), 6144);
1378        assert_eq!(superblock_capacity(14), 12288);
1379        assert_eq!(superblock_capacity(15), 24576);
1380        assert_eq!(superblock_capacity(16), 49152);
1381    }
1382}