segment_array/
lib.rs

1//
2// Copyright (c) 2025 Nathan Fiedler
3//
4
5//! An append-only (no insert or remove) growable array as described in the
6//! [blog post](https://danielchasehooper.com/posts/segment_array/) by Daniel
7//! Hooper.
8//!
9//! From the blog post:
10//!
11//! > A data structure with constant time indexing, stable pointers, and works
12//! > well with arena allocators. ... The idea is straight forward: the
13//! > structure contains a fixed sized array of pointers to segments. Each
14//! > segment is twice the size of its predecessor. New segments are allocated
15//! > as needed. ... Unlike standard arrays, pointers to a segment array’s items
16//! > are always valid because items are never moved. Leaving items in place
17//! > also means it never leaves "holes" of abandoned memory in arena
18//! > allocators. The layout also allows us to access any index in constant
19//! > time.
20//!
21//! In terms of this Rust implementation, rather than stable "pointers", the
22//! references returned from [`SegmentArray::get()`] will be stable. The
23//! behavior, memory layout, and performance of this implementation should be
24//! identical to that of the C implementation. To summarize:
25//!
26//! * Fixed number of segments (26)
27//! * First segment has a capacity of 64
28//! * Each segment is double the size of the previous one
29//! * Total capacity of 4,294,967,232 items
30//!
31//! This data structure is meant to hold an unknown, though likely large, number
32//! of elements, otherwise `Vec` would be more appropriate. An empty array will
33//! have a hefty size of around 224 bytes.
34
35use std::alloc::{Layout, alloc, dealloc, handle_alloc_error};
36use std::iter::{FromIterator, Iterator};
37use std::ops::{Index, IndexMut};
38
39//
40// An individual segment can never be larger than 9,223,372,036,854,775,807
41// bytes due to the mechanics of the Rust memory allocator.
42//
43// 26 segments with 6 skipped segments can hold 4,294,967,232 items
44//
45// 9,223,372,036,854,775,807 bytes divided by 4,294,967,232 items yields a
46// maximum item size of 2,147,483,680 bytes
47//
48const MAX_SEGMENT_COUNT: usize = 26;
49
50// Segments of size 1, 2, 4, 8, 16, and 32 are not used at all (that is, the
51// smallest (first) segment is 64 elements in size) to avoid the overhead of
52// such tiny arrays.
53const SMALL_SEGMENTS_TO_SKIP: usize = 6;
54const SMALL_SEGMENTS_CAPACITY: usize = 1 << SMALL_SEGMENTS_TO_SKIP;
55
56// Calculates the number of elements that will fit into the given segment.
57#[inline]
58fn slots_in_segment(segment: usize) -> usize {
59    SMALL_SEGMENTS_CAPACITY << segment
60}
61
62// Calculates the overall capacity for all segments up to the given segment.
63#[inline]
64fn capacity_for_segment_count(segment: usize) -> usize {
65    (SMALL_SEGMENTS_CAPACITY << segment) - SMALL_SEGMENTS_CAPACITY
66}
67
68///
69/// Append-only growable array that uses a list of progressivly larger segments
70/// to avoid the allocate-and-copy that many growable data structures typically
71/// employ.
72///
73pub struct SegmentArray<T> {
74    count: usize,
75    used_segments: usize,
76    segments: [*mut T; MAX_SEGMENT_COUNT],
77}
78
79impl<T> SegmentArray<T> {
80    /// Return an empty segment array with zero capacity.
81    ///
82    /// Note that pre-allocating capacity has no benefit with this data
83    /// structure since append operations are always constant time.
84    pub fn new() -> Self {
85        Self {
86            count: 0,
87            used_segments: 0,
88            segments: [0 as *mut T; MAX_SEGMENT_COUNT],
89        }
90    }
91
92    /// Appends an element to the back of a collection.
93    ///
94    /// # Panics
95    ///
96    /// Panics if a new segment is allocated that would exceed `isize::MAX` _bytes_.
97    ///
98    /// # Time complexity
99    ///
100    /// Constant time.
101    pub fn push(&mut self, value: T) {
102        if self.count >= capacity_for_segment_count(self.used_segments) {
103            assert!(
104                self.used_segments < MAX_SEGMENT_COUNT,
105                "maximum number of segments exceeded"
106            );
107            let segment_len = slots_in_segment(self.used_segments);
108            // overflowing the allocator is very unlikely as the item size would
109            // have to be very large
110            let layout = Layout::array::<T>(segment_len).expect("unexpected overflow");
111            unsafe {
112                let ptr = alloc(layout).cast::<T>();
113                if ptr.is_null() {
114                    handle_alloc_error(layout);
115                }
116                self.segments[self.used_segments] = ptr;
117            }
118            self.used_segments += 1;
119        }
120
121        let segment = ((self.count >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
122        let slot = (self.count - capacity_for_segment_count(segment)) as isize;
123        unsafe {
124            std::ptr::write(self.segments[segment].offset(slot), value);
125        }
126        self.count += 1;
127    }
128
129    /// Appends an element if there is sufficient spare capacity, otherwise an
130    /// error is returned with the element.
131    ///
132    /// # Time complexity
133    ///
134    /// Constant time.
135    pub fn push_within_capacity(&mut self, value: T) -> Result<(), T> {
136        if self.count >= capacity_for_segment_count(self.used_segments) {
137            Err(value)
138        } else {
139            Ok(self.push(value))
140        }
141    }
142
143    /// Removes the last element from a vector and returns it, or `None` if it
144    /// is empty.
145    ///
146    /// # Time complexity
147    ///
148    /// Constant time.
149    pub fn pop(&mut self) -> Option<T> {
150        if self.count > 0 {
151            self.count -= 1;
152            let segment = ((self.count >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
153            let slot = (self.count - capacity_for_segment_count(segment)) as isize;
154            unsafe { Some((self.segments[segment].offset(slot)).read()) }
155        } else {
156            None
157        }
158    }
159
160    /// Removes and returns the last element from a vector if the predicate
161    /// returns true, or None if the predicate returns false or the vector is
162    /// empty (the predicate will not be called in that case).
163    ///
164    /// # Time complexity
165    ///
166    /// Constant time.
167    pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T> {
168        if self.count == 0 {
169            None
170        } else {
171            if let Some(last) = self.get_mut(self.count - 1) {
172                if predicate(last) { self.pop() } else { None }
173            } else {
174                None
175            }
176        }
177    }
178
179    /// Return the number of elements in the array.
180    ///
181    /// # Time complexity
182    ///
183    /// Constant time.
184    pub fn len(&self) -> usize {
185        self.count as usize
186    }
187
188    /// Returns the total number of elements the segment array can hold
189    /// without reallocating.
190    ///
191    /// # Time complexity
192    ///
193    /// Constant time.
194    pub fn capacity(&self) -> usize {
195        capacity_for_segment_count(self.used_segments)
196    }
197
198    /// Returns true if the array has a length of 0.
199    ///
200    /// # Time complexity
201    ///
202    /// Constant time.
203    pub fn is_empty(&self) -> bool {
204        self.count == 0
205    }
206
207    /// Retrieve a reference to the element at the given offset.
208    ///
209    /// # Time complexity
210    ///
211    /// Constant time.
212    pub fn get(&self, index: usize) -> Option<&T> {
213        if index >= self.count {
214            None
215        } else {
216            let segment = ((index >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
217            let slot = (index - capacity_for_segment_count(segment)) as isize;
218            unsafe { (self.segments[segment].offset(slot)).as_ref() }
219        }
220    }
221
222    /// Returns a mutable reference to an element.
223    ///
224    /// # Time complexity
225    ///
226    /// Constant time.
227    pub fn get_mut(&self, index: usize) -> Option<&mut T> {
228        if index >= self.count {
229            None
230        } else {
231            let segment = ((index >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
232            let slot = (index - capacity_for_segment_count(segment)) as isize;
233            unsafe { (self.segments[segment].offset(slot)).as_mut() }
234        }
235    }
236
237    /// Removes an element from the vector and returns it.
238    ///
239    /// The removed element is replaced by the last element of the vector.
240    ///
241    /// This does not preserve ordering of the remaining elements.
242    ///
243    /// # Time complexity
244    ///
245    /// Constant time.
246    pub fn swap_remove(&mut self, index: usize) -> T {
247        if index >= self.count {
248            panic!(
249                "swap_remove index (is {index}) should be < len (is {})",
250                self.count
251            );
252        }
253        // retreive the value at index before overwriting
254        let segment = ((index >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
255        let slot = (index - capacity_for_segment_count(segment)) as isize;
256        unsafe {
257            let index_ptr = self.segments[segment].offset(slot);
258            let value = index_ptr.read();
259            // find the pointer of the last element and copy to index pointer
260            self.count -= 1;
261            let segment = ((self.count >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
262            let slot = (self.count - capacity_for_segment_count(segment)) as isize;
263            let last_ptr = self.segments[segment].offset(slot);
264            std::ptr::copy(last_ptr, index_ptr, 1);
265            value
266        }
267    }
268
269    /// Returns an iterator over the segment array.
270    ///
271    /// The iterator yields all items from start to end.
272    pub fn iter(&self) -> SegArrayIter<'_, T> {
273        SegArrayIter {
274            array: self,
275            index: 0,
276        }
277    }
278
279    /// Clears the segment array, removing all values.
280    ///
281    /// Note that this method has no effect on the allocated capacity of the
282    /// segment array.
283    pub fn clear(&mut self) {
284        if self.count > 0 {
285            if std::mem::needs_drop::<T>() {
286                // find the last segment that contains values
287                let last_segment = ((self.count >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
288                let last_slot = self.count - capacity_for_segment_count(last_segment);
289                unsafe {
290                    std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(
291                        self.segments[last_segment],
292                        last_slot,
293                    ));
294                }
295                // now drop the values in all of the preceding segments
296                for segment in 0..last_segment {
297                    let segment_len = slots_in_segment(segment);
298                    unsafe {
299                        std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(
300                            self.segments[segment],
301                            segment_len,
302                        ));
303                    }
304                }
305            }
306            self.count = 0;
307        }
308    }
309}
310
311impl<T> Index<usize> for SegmentArray<T> {
312    type Output = T;
313
314    fn index(&self, index: usize) -> &Self::Output {
315        let Some(item) = self.get(index) else {
316            panic!("index out ouf bounds: {}", index);
317        };
318        item
319    }
320}
321
322impl<T> IndexMut<usize> for SegmentArray<T> {
323    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
324        let Some(item) = self.get_mut(index) else {
325            panic!("index out ouf bounds: {}", index);
326        };
327        item
328    }
329}
330
331impl<A> FromIterator<A> for SegmentArray<A> {
332    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
333        let mut arr: SegmentArray<A> = SegmentArray::new();
334        for value in iter {
335            arr.push(value)
336        }
337        arr
338    }
339}
340
341/// Immutable segment array iterator.
342pub struct SegArrayIter<'a, T> {
343    array: &'a SegmentArray<T>,
344    index: usize,
345}
346
347impl<'a, T> Iterator for SegArrayIter<'a, T> {
348    type Item = &'a T;
349
350    fn next(&mut self) -> Option<Self::Item> {
351        let value = self.array.get(self.index);
352        self.index += 1;
353        value
354    }
355}
356
357/// An iterator that moves out of a segment array.
358pub struct SegArrayIntoIter<T> {
359    index: usize,
360    count: usize,
361    used_segments: usize,
362    segments: [*mut T; MAX_SEGMENT_COUNT],
363}
364
365impl<T> Iterator for SegArrayIntoIter<T> {
366    type Item = T;
367
368    fn next(&mut self) -> Option<Self::Item> {
369        if self.index < self.count {
370            let segment = ((self.index >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
371            let slot = (self.index - capacity_for_segment_count(segment)) as isize;
372            self.index += 1;
373            unsafe { Some((self.segments[segment].offset(slot)).read()) }
374        } else {
375            None
376        }
377    }
378}
379
380impl<T> Drop for SegArrayIntoIter<T> {
381    fn drop(&mut self) {
382        if std::mem::needs_drop::<T>() {
383            let first_segment = ((self.index >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
384            let last_segment = ((self.count >> SMALL_SEGMENTS_TO_SKIP) + 1).ilog2() as usize;
385            if first_segment == last_segment {
386                // special-case, remaining values are in only one segment
387                let first = self.index - capacity_for_segment_count(first_segment);
388                let last = self.count - capacity_for_segment_count(first_segment);
389                if first < last {
390                    let len = last - first;
391                    unsafe {
392                        let first: *mut T = self.segments[first_segment].offset(first as isize);
393                        std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(first, len));
394                    }
395                }
396            } else {
397                // drop values in the first segment that still has values to be
398                // visited
399                let first = self.index - capacity_for_segment_count(first_segment);
400                let segment_len = slots_in_segment(first_segment);
401                if segment_len < self.count {
402                    unsafe {
403                        let ptr: *mut T = self.segments[first_segment].offset(first as isize);
404                        let len = segment_len - first;
405                        std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(ptr, len));
406                    }
407                }
408
409                // drop the values in the last segment
410                let last_slot = self.count - capacity_for_segment_count(last_segment);
411                unsafe {
412                    std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(
413                        self.segments[last_segment],
414                        last_slot,
415                    ));
416                }
417
418                // now drop the values in all of the other segments
419                if last_segment > first_segment {
420                    for segment in first_segment + 1..last_segment {
421                        let segment_len = slots_in_segment(segment);
422                        unsafe {
423                            std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(
424                                self.segments[segment],
425                                segment_len,
426                            ));
427                        }
428                    }
429                }
430            }
431        }
432
433        // deallocate the segments themselves and clear everything
434        for segment in 0..self.used_segments {
435            if !self.segments[segment].is_null() {
436                let segment_len = slots_in_segment(segment);
437                let layout = Layout::array::<T>(segment_len).expect("unexpected overflow");
438                unsafe {
439                    dealloc(self.segments[segment] as *mut u8, layout);
440                }
441                self.segments[segment] = std::ptr::null_mut();
442            }
443        }
444        self.index = 0;
445        self.count = 0;
446        self.used_segments = 0;
447    }
448}
449
450impl<T> IntoIterator for SegmentArray<T> {
451    type Item = T;
452    type IntoIter = SegArrayIntoIter<Self::Item>;
453
454    fn into_iter(self) -> Self::IntoIter {
455        let me = std::mem::ManuallyDrop::new(self);
456        SegArrayIntoIter {
457            index: 0,
458            count: me.count,
459            used_segments: me.used_segments,
460            segments: me.segments,
461        }
462    }
463}
464
465impl<T> Drop for SegmentArray<T> {
466    fn drop(&mut self) {
467        // perform the drop_in_place() for all of the values
468        self.clear();
469        // deallocate the segments themselves and clear everything
470        for segment in 0..self.used_segments {
471            if !self.segments[segment].is_null() {
472                let segment_len = slots_in_segment(segment);
473                let layout = Layout::array::<T>(segment_len).expect("unexpected overflow");
474                unsafe {
475                    dealloc(self.segments[segment] as *mut u8, layout);
476                }
477                self.segments[segment] = std::ptr::null_mut();
478            }
479        }
480        self.used_segments = 0;
481    }
482}
483
484#[cfg(test)]
485mod tests {
486    use super::*;
487
488    #[test]
489    fn test_slots_in_segment() {
490        // values are simply capacity_for_segment_count() plus 64 but there
491        // should be a test for this function regardless of its simplicity
492        let expected_values = [
493            64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288,
494            1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456,
495            536870912, 1073741824, 2147483648, 4294967296,
496        ];
497        assert_eq!(expected_values.len(), MAX_SEGMENT_COUNT + 1);
498        for segment in 0..=MAX_SEGMENT_COUNT {
499            assert_eq!(expected_values[segment], slots_in_segment(segment));
500        }
501    }
502
503    #[test]
504    fn test_capacity_for_segment_count() {
505        //
506        // from https://danielchasehooper.com/posts/segment_array/segment_array.h:
507        //
508        // 26 segments with 6 skipped segments can hold 4,294,967,232 items, aka
509        // capacity_for_segment_count(26)
510        //
511        let expected_values = [
512            0, 64, 192, 448, 960, 1984, 4032, 8128, 16320, 32704, 65472, 131008, 262080, 524224,
513            1048512, 2097088, 4194240, 8388544, 16777152, 33554368, 67108800, 134217664, 268435392,
514            536870848, 1073741760, 2147483584, 4294967232,
515        ];
516        assert_eq!(expected_values.len(), MAX_SEGMENT_COUNT + 1);
517        for count in 0..=MAX_SEGMENT_COUNT {
518            assert_eq!(expected_values[count], capacity_for_segment_count(count));
519        }
520    }
521
522    #[test]
523    fn test_push_within_capacity() {
524        let mut sut: SegmentArray<u32> = SegmentArray::new();
525        assert_eq!(sut.push_within_capacity(101), Err(101));
526        sut.push(10);
527        assert_eq!(sut.push_within_capacity(101), Ok(()));
528    }
529
530    #[test]
531    fn test_add_get_one_item() {
532        let item = String::from("hello world");
533        let mut sut: SegmentArray<String> = SegmentArray::new();
534        assert_eq!(sut.len(), 0);
535        assert!(sut.is_empty());
536        sut.push(item);
537        assert_eq!(sut.len(), 1);
538        assert!(!sut.is_empty());
539        let maybe = sut.get(0);
540        assert!(maybe.is_some());
541        let actual = maybe.unwrap();
542        assert_eq!("hello world", actual);
543        let missing = sut.get(10);
544        assert!(missing.is_none());
545    }
546
547    #[test]
548    fn test_add_get_several_strings() {
549        let inputs = [
550            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
551        ];
552        let mut sut: SegmentArray<String> = SegmentArray::new();
553        for item in inputs {
554            sut.push(item.to_owned());
555        }
556        assert_eq!(sut.len(), 9);
557        for idx in 0..=8 {
558            let maybe = sut.get(idx);
559            assert!(maybe.is_some(), "{idx} is none");
560            let actual = maybe.unwrap();
561            assert_eq!(inputs[idx], actual);
562        }
563        let maybe = sut.get(10);
564        assert!(maybe.is_none());
565        assert_eq!(sut[3], "four");
566    }
567
568    #[test]
569    fn test_get_mut_index_mut() {
570        let mut sut: SegmentArray<String> = SegmentArray::new();
571        sut.push(String::from("first"));
572        sut.push(String::from("second"));
573        sut.push(String::from("third"));
574        if let Some(value) = sut.get_mut(1) {
575            value.push_str(" place");
576        } else {
577            panic!("get_mut() returned None")
578        }
579        assert_eq!(sut[1], "second place");
580        sut[2] = "third planet".into();
581        assert_eq!(sut[2], "third planet");
582    }
583
584    #[test]
585    #[should_panic(expected = "index out ouf bounds:")]
586    fn test_index_out_of_bounds() {
587        let mut sut: SegmentArray<i32> = SegmentArray::new();
588        sut.push(10);
589        sut.push(20);
590        let _ = sut[2];
591    }
592
593    #[test]
594    #[should_panic(expected = "index out ouf bounds:")]
595    fn test_index_mut_out_of_bounds() {
596        let mut sut: SegmentArray<i32> = SegmentArray::new();
597        sut.push(10);
598        sut.push(20);
599        sut[2] = 30;
600    }
601
602    #[test]
603    fn test_push_and_pop() {
604        let inputs = [
605            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
606        ];
607        let mut sut: SegmentArray<String> = SegmentArray::new();
608        assert!(sut.pop().is_none());
609        for item in inputs {
610            sut.push(item.to_owned());
611        }
612        assert_eq!(sut.len(), 9);
613        for (idx, elem) in sut.iter().enumerate() {
614            assert_eq!(inputs[idx], elem);
615        }
616        let maybe = sut.pop();
617        assert!(maybe.is_some());
618        let value = maybe.unwrap();
619        assert_eq!(value, "nine");
620        assert_eq!(sut.len(), 8);
621        sut.push(String::from("nine"));
622        assert_eq!(sut.len(), 9);
623        for (idx, elem) in sut.iter().enumerate() {
624            assert_eq!(inputs[idx], elem);
625        }
626
627        // pop everything and add back again
628        while sut.len() > 0 {
629            sut.pop();
630        }
631        assert_eq!(sut.len(), 0);
632        for item in inputs {
633            sut.push(item.to_owned());
634        }
635        assert_eq!(sut.len(), 9);
636        for (idx, elem) in sut.iter().enumerate() {
637            assert_eq!(inputs[idx], elem);
638        }
639    }
640
641    #[test]
642    fn test_pop_if() {
643        let mut sut: SegmentArray<u32> = SegmentArray::new();
644        assert!(sut.pop_if(|_| panic!("should not be called")).is_none());
645        for value in 0..10 {
646            sut.push(value);
647        }
648        assert!(sut.pop_if(|_| false).is_none());
649        let maybe = sut.pop_if(|v| *v == 9);
650        assert_eq!(maybe.unwrap(), 9);
651        assert!(sut.pop_if(|v| *v == 9).is_none());
652    }
653
654    #[test]
655    fn test_swap_remove_single_segment() {
656        let mut sut: SegmentArray<u32> = SegmentArray::new();
657        for value in 0..10 {
658            sut.push(value);
659        }
660        assert_eq!(sut.len(), 10);
661        let five = sut.swap_remove(5);
662        assert_eq!(five, 5);
663        assert_eq!(sut.pop(), Some(8));
664        assert_eq!(sut[5], 9);
665    }
666
667    #[test]
668    fn test_swap_remove_multiple_segments() {
669        let mut sut: SegmentArray<u32> = SegmentArray::new();
670        for value in 0..512 {
671            sut.push(value);
672        }
673        assert_eq!(sut.len(), 512);
674        let eighty = sut.swap_remove(80);
675        assert_eq!(eighty, 80);
676        assert_eq!(sut.pop(), Some(510));
677        assert_eq!(sut[80], 511);
678    }
679
680    #[test]
681    #[should_panic(expected = "swap_remove index (is 0) should be < len (is 0)")]
682    fn test_swap_remove_panic_empty() {
683        let mut sut: SegmentArray<u32> = SegmentArray::new();
684        sut.swap_remove(0);
685    }
686
687    #[test]
688    #[should_panic(expected = "swap_remove index (is 1) should be < len (is 1)")]
689    fn test_swap_remove_panic_range_edge() {
690        let mut sut: SegmentArray<u32> = SegmentArray::new();
691        sut.push(1);
692        sut.swap_remove(1);
693    }
694
695    #[test]
696    #[should_panic(expected = "swap_remove index (is 2) should be < len (is 1)")]
697    fn test_swap_remove_panic_range_exceed() {
698        let mut sut: SegmentArray<u32> = SegmentArray::new();
699        sut.push(1);
700        sut.swap_remove(2);
701    }
702
703    #[test]
704    fn test_add_get_thousands_structs() {
705        struct MyData {
706            a: u64,
707            b: i32,
708        }
709        let mut sut: SegmentArray<MyData> = SegmentArray::new();
710        for value in 0..88_888i32 {
711            sut.push(MyData {
712                a: value as u64,
713                b: value,
714            });
715        }
716        assert_eq!(sut.len(), 88_888);
717        for idx in 0..88_888i32 {
718            let maybe = sut.get(idx as usize);
719            assert!(maybe.is_some(), "{idx} is none");
720            let actual = maybe.unwrap();
721            assert_eq!(idx as u64, actual.a);
722            assert_eq!(idx, actual.b);
723        }
724    }
725
726    #[test]
727    fn test_add_get_hundred_ints() {
728        let mut sut: SegmentArray<i32> = SegmentArray::new();
729        for value in 0..100 {
730            sut.push(value);
731        }
732        assert_eq!(sut.len(), 100);
733        for idx in 0..100 {
734            let maybe = sut.get(idx);
735            assert!(maybe.is_some(), "{idx} is none");
736            let actual = maybe.unwrap();
737            assert_eq!(idx, *actual as usize);
738        }
739        assert_eq!(sut[99], 99);
740    }
741
742    #[test]
743    fn test_len_and_capacity() {
744        let mut sut: SegmentArray<i32> = SegmentArray::new();
745        assert_eq!(sut.len(), 0);
746        assert_eq!(sut.capacity(), 0);
747        for value in 0..100 {
748            sut.push(value);
749        }
750        assert_eq!(sut.len(), 100);
751        assert_eq!(sut.capacity(), 192);
752    }
753
754    #[test]
755    fn test_clear_and_reuse_tiny() {
756        // clear an array that allocated only one segment
757        let inputs = [
758            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
759        ];
760        let mut sut: SegmentArray<String> = SegmentArray::new();
761        for item in inputs {
762            sut.push(item.to_owned());
763        }
764        assert_eq!(sut.len(), 9);
765        sut.clear();
766        assert_eq!(sut.len(), 0);
767        for item in inputs {
768            sut.push(item.to_owned());
769        }
770        assert_eq!(sut.len(), 9);
771        // implicitly drop()
772    }
773
774    #[test]
775    fn test_clear_and_reuse_ints() {
776        let mut sut: SegmentArray<i32> = SegmentArray::new();
777        for value in 0..512 {
778            sut.push(value);
779        }
780        assert_eq!(sut.len(), 512);
781        sut.clear();
782        assert_eq!(sut.len(), 0);
783        for value in 0..512 {
784            sut.push(value);
785        }
786        for idx in 0..512 {
787            let maybe = sut.get(idx);
788            assert!(maybe.is_some(), "{idx} is none");
789            let actual = maybe.unwrap();
790            assert_eq!(idx, *actual as usize);
791        }
792    }
793
794    #[test]
795    fn test_clear_and_reuse_strings() {
796        let mut sut: SegmentArray<String> = SegmentArray::new();
797        for _ in 0..512 {
798            let value = ulid::Ulid::new().to_string();
799            sut.push(value);
800        }
801        assert_eq!(sut.len(), 512);
802        sut.clear();
803        assert_eq!(sut.len(), 0);
804        for _ in 0..512 {
805            let value = ulid::Ulid::new().to_string();
806            sut.push(value);
807        }
808        assert_eq!(sut.len(), 512);
809        // implicitly drop()
810    }
811
812    #[test]
813    fn test_add_get_many_ints() {
814        let mut sut: SegmentArray<i32> = SegmentArray::new();
815        for value in 0..1_000_000 {
816            sut.push(value);
817        }
818        assert_eq!(sut.len(), 1_000_000);
819        for idx in 0..1_000_000 {
820            let maybe = sut.get(idx);
821            assert!(maybe.is_some(), "{idx} is none");
822            let actual = maybe.unwrap();
823            assert_eq!(idx, *actual as usize);
824        }
825        assert_eq!(sut[99_999], 99_999);
826    }
827
828    #[test]
829    fn test_array_iterator() {
830        let inputs = [
831            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
832        ];
833        let mut sut: SegmentArray<String> = SegmentArray::new();
834        for item in inputs {
835            sut.push(item.to_owned());
836        }
837        for (idx, elem) in sut.iter().enumerate() {
838            assert_eq!(inputs[idx], elem);
839        }
840    }
841
842    #[test]
843    fn test_array_intoiterator() {
844        // an array that only requires a single segment
845        let inputs = [
846            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
847        ];
848        let mut sut: SegmentArray<String> = SegmentArray::new();
849        for item in inputs {
850            sut.push(item.to_owned());
851        }
852        for (idx, elem) in sut.into_iter().enumerate() {
853            assert_eq!(inputs[idx], elem);
854        }
855        // sut.len(); // error: ownership of sut was moved
856    }
857
858    #[test]
859    fn test_array_intoiterator_drop_tiny() {
860        // an array that only requires a single segment and only some need to be
861        // dropped after partially iterating the values
862        let inputs = [
863            "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
864        ];
865        let mut sut: SegmentArray<String> = SegmentArray::new();
866        for item in inputs {
867            sut.push(item.to_owned());
868        }
869        for (idx, _) in sut.into_iter().enumerate() {
870            if idx > 2 {
871                break;
872            }
873        }
874        // implicitly drop()
875    }
876
877    #[test]
878    fn test_array_intoiterator_drop_large() {
879        // by adding 512 values and iterating less than 64 times, there will be
880        // values in the first segment and some in the last segment, and two
881        // segments inbetween that all need to be dropped
882        let mut sut: SegmentArray<String> = SegmentArray::new();
883        for _ in 0..512 {
884            let value = ulid::Ulid::new().to_string();
885            sut.push(value);
886        }
887        for (idx, _) in sut.into_iter().enumerate() {
888            if idx >= 30 {
889                break;
890            }
891        }
892        // implicitly drop()
893    }
894
895    #[test]
896    fn test_array_fromiterator() {
897        let mut inputs: Vec<i32> = Vec::new();
898        for value in 0..10_000 {
899            inputs.push(value);
900        }
901        let sut: SegmentArray<i32> = inputs.into_iter().collect();
902        assert_eq!(sut.len(), 10_000);
903        for idx in 0..10_000i32 {
904            let maybe = sut.get(idx as usize);
905            assert!(maybe.is_some(), "{idx} is none");
906            let actual = maybe.unwrap();
907            assert_eq!(idx, *actual as i32);
908        }
909    }
910
911    #[test]
912    fn test_add_get_many_instances() {
913        // test allocating, filling, and then dropping many instances
914        for _ in 0..1_000 {
915            let mut sut: SegmentArray<usize> = SegmentArray::new();
916            for value in 0..10_000 {
917                sut.push(value);
918            }
919            assert_eq!(sut.len(), 10_000);
920        }
921    }
922}