dsa_rust/sequences/
dyn_array.rs

1/*!
2
3# About
4
5# Design
6
7# Example
8*/
9
10use std::alloc::{self, Layout};
11use std::ops::{Index, IndexMut};
12use std::ptr::{self, NonNull};
13
14#[derive(Debug)]
15/// A dynamically sized, contiguous storage buffer with positive
16/// (forward) indexing, where elements are stored sequentially in memory.
17pub struct DynArray<T> {
18    ptr: NonNull<T>, // Pointer to the start of the contiguous region
19    len: usize,      // Number of elements currently initialized
20    cap: usize,      // Total number of elements the region can hold
21}
22#[allow(clippy::new_without_default)]
23#[allow(unused)] // Temporary shhhhhh
24impl<T> DynArray<T> {
25    // Utilities
26    ////////////
27
28    pub fn new() -> Self {
29        // Start with zero capacity and a "dangling" pointer to avoid
30        // an immediate syscall for an empty array
31        Self {
32            ptr: NonNull::dangling(),
33            len: 0,
34            cap: 0,
35        }
36    }
37
38    /// Pre-allocate for quicker operations
39    pub fn new_with_capacity(cap: usize) -> Self {
40        // Will panic on attempts to allocate beyond isize::MAX bytes
41        let new_layout = Layout::array::<T>(cap).unwrap();
42        // SAFETY:
43        let ptr = unsafe { alloc::alloc(new_layout) };
44        Self {
45            ptr: NonNull::new(ptr as *mut T).expect("Out of memory"),
46            len: 0,
47            cap,
48        }
49    }
50
51    fn layouts() {
52        let layout = Layout::new::<usize>();
53    }
54
55    /// Warning: Unimplemented
56    /// Returns the current number of elements in the list.
57    pub fn size(&mut self, val: T) {}
58
59    /// Warning: Unimplemented
60    /// Returns the current capacity of the list.
61    pub fn cap(&mut self, val: T) {}
62
63    /// Warning: Unimplemented
64    /// Returns a Boolean indicating whether the list is empty.
65    pub fn is_empty(&mut self, val: T) -> bool {
66        false
67    }
68
69    /// Internal capacity growth utility.
70    fn grow(&mut self) {
71        // Double the capacity (or start at 4)
72        let new_cap = if self.cap == 0 { 4 } else { self.cap * 2 };
73        let new_layout = Layout::array::<T>(new_cap).unwrap();
74
75        // SAFETY:
76        unsafe {
77            let new_ptr = if self.cap == 0 {
78                // First time allocating: ask the retailer for a fresh block
79                alloc::alloc(new_layout)
80            } else {
81                // Already have memory: ask to resize (might move to a new page)
82                let old_layout = Layout::array::<T>(self.cap).unwrap();
83                alloc::realloc(self.ptr.as_ptr() as *mut u8, old_layout, new_layout.size())
84            };
85
86            self.ptr = NonNull::new(new_ptr as *mut T).expect("Out of memory");
87            self.cap = new_cap;
88        }
89    }
90
91    // Mutators
92    ///////////
93
94    /// Appends the list with an given element in O(1) time.
95    /// If the list is at capacity, this operation automatically
96    /// grows the list in O(n) time.
97    pub fn push(&mut self, val: T) {
98        if self.len == self.cap {
99            self.grow();
100        }
101
102        // SAFETY:
103        unsafe {
104            // Calculate the offset and write the value into the uninitialized slot
105            let offset_ptr = self.ptr.as_ptr().add(self.len);
106            ptr::write(offset_ptr, val);
107            self.len += 1;
108        }
109    }
110
111    /// Warning: Unimplemented
112    /// Removes the last inserted element from the list.
113    pub fn pop(&mut self) {}
114
115    /// Warning: Unimplemented
116    /// Removes an element at the ith index in the list.
117    pub fn remove(&mut self, index: usize) {}
118
119    /// Warning: Unimplemented
120    /// Inserts an element at a given index, forcing any remaining
121    /// elements between i - list.len() to shift right. If the list
122    /// is at capacity, the list automatically grows in O(n) time.
123    pub fn insert(&mut self, val: T, index: usize) {}
124
125    /// Warning: Unimplemented
126    /// Returns
127    pub fn get(&mut self, index: usize) -> Option<T> {
128        None
129    }
130}
131impl<T> Index<usize> for DynArray<T> {
132    type Output = T;
133
134    fn index(&self, index: usize) -> &Self::Output {
135        if index >= self.len {
136            panic!(
137                "Index out of bounds: the len is {} but the index is {}",
138                self.len, index
139            );
140        }
141        // Uses pointer arithmetic to retireve the [index]th element
142        // add takes usize, for negative indexing like in Python's
143        // list type, use offset
144        // SAFETY:
145        unsafe { &*self.ptr.as_ptr().add(index) }
146    }
147}
148impl<T> IndexMut<usize> for DynArray<T> {
149    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
150        if index >= self.len {
151            panic!(
152                "Index out of bounds: the len is {} but the index is {}",
153                self.len, index
154            );
155        }
156        // SAFETY:
157        unsafe { &mut *self.ptr.as_ptr().add(index) }
158    }
159}
160impl<T> Drop for DynArray<T> {
161    fn drop(&mut self) {
162        if self.cap != 0 {
163            // SAFETY:
164            unsafe {
165                // 1. Drop the actual items in the array
166                ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), self.len));
167
168                // 2. Tell the allocator to release the virtual memory region
169                let layout = Layout::array::<T>(self.cap).unwrap();
170                alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
171            }
172        }
173    }
174}
175
176#[test]
177fn test_0() {
178    let mut a = DynArray::<&str>::new();
179    a.push("Hello");
180    a.push("world");
181
182    assert_eq!(a.len, 2);
183    assert_eq!(a.cap, 4);
184
185    // Accesses the array with index operator
186    assert_eq!(a[0], "Hello");
187    assert_eq!(a[1], "world");
188
189    a.push("Alpha");
190    a.push("Bravo");
191    assert_eq!(a.len, 4);
192    assert_eq!(a.cap, 4);
193
194    // Grows the array, doubling its capacity
195    a.push("Charlie");
196    assert_eq!(a.len, 5);
197    assert_eq!(a.cap, 8);
198
199    // Illustrates the same logic with a pre-allocated slab
200    // which avoids O(n) reallocation with the internal grow()
201    let mut a = DynArray::<String>::new_with_capacity(4);
202    a.push("Papa".to_string());
203    a.push("Echo".to_string());
204    a.push("Tango".to_string());
205    a.push("Echo".to_string());
206    a.push("Romeo".to_string());
207    assert_eq!(a.cap, 8);
208    assert_eq!(a.len, 5);
209}
210
211#[test]
212#[should_panic]
213fn test_1() {
214    let mut a = DynArray::<&str>::new();
215    a.push("Hello");
216    a.push("world");
217
218    assert_ne!(a[5], "Hello"); // Illegal index
219}
220
221#[test]
222fn test_2() {
223    let mut a = DynArray::<String>::new_with_capacity(4);
224    a.push("Papa".to_string());
225    a.push("Echo".to_string());
226    a.push("Tango".to_string());
227    a.push("Echo".to_string());
228    a.push("Romeo".to_string());
229    assert_eq!(a.cap, 8);
230    assert_eq!(a.len, 5);
231
232    //assert!(balance("([][]{([]{()})})")); // Balanced
233    //assert!(balance("([][]{[]{()}}()")); // Missing closing symbol
234    //assert!(balance("[][]{[]{()}}())")); // Missing opening symbol
235}
236
237#[test]
238#[should_panic]
239fn test_panic() {
240    // Cannot allocate beyond isize::MAX, which is aproximately
241    // 9,223,372,036,854,775,807, or ~9.22 quintillion
242    let _: DynArray<String> = DynArray::new_with_capacity(9_223_372_036_854_775_809);
243    // Panic!!
244}
245
246mod testing {
247
248    #![allow(
249        unused,
250        clippy::disallowed_names,
251        clippy::needless_lifetimes,
252        clippy::extra_unused_lifetimes
253    )]
254
255    struct MutStr {}
256
257    fn two<'a, 'long, 'short, T>() {
258        // Invariance vs covariance
259        ///////////////////////////
260        //
261        // foo is invariant, and can only take *mut T
262        let foo: *mut T;
263        // bar is covariant and can take any sub-type over T
264        let bar: std::ptr::NonNull<T>;
265
266        // Sub-typing
267        let foo: &'static str = "foo"; // Lives longest, most useful
268        let bar: &'long str = "bar"; // Lives longer, more useful
269        let baz: &'short str = "baz"; // Lives shortest, least useful
270
271        // Allowed because foo lives exactly as long
272        // as the specified lifetime
273        three(foo);
274        // Allowed because is a sub-type of the
275        // parameter and lives much longer so it
276        // can be coerced into a shorter lifetime
277        four(foo);
278        // bar is illegal because bar is a supertype
279        // and does not live as long as the parameter spec
280        //three(bar);
281
282        five(foo, foo);
283        five(bar, baz);
284    }
285
286    // Example of contravariance
287    ////////////////////////////
288    //
289    // Least useful because a caller must supply a value
290    // that lives for the entire program, instead of some
291    // shorter-lived binding.
292    fn three(foo: &'static str) {} // Stricter, so less useful
293                                   // More useful because a caller can supply a slice
294                                   // with any lifetime.
295    fn four<'a>(foo: &'a str) {} // Less strict, more useful
296
297    fn five<'a, 'b>(foo: &'a str, bar: &'b str) {
298        let a: Box<&str> = Box::from(foo);
299        println!("{foo} {bar}");
300    }
301
302    // Invariance
303    //
304    // Raw pointer type *mut T is invariant in T which
305    // means that there is no sub-typing possible through
306    // type constructor parameter, preventing any type coercion.
307    fn invariant<'a>(foo: *mut &'a str) {}
308    fn demo_invariance<'a, 'b>(bar: *mut &'a str) {
309        // Illegal because *mut T is invariant in its pointee type
310        // so it is not possible to coerce the lifetime
311        //let mut baz: *mut &'b str = bar; // Error!
312
313        // Allowed because the types are invariant,
314        // e.g. foo and bar are both *mut &'a T
315        invariant(bar);
316    }
317
318    // Covariance
319    //
320    // Shared references (&T) are covariant over their
321    // lifetime parameter, which allows coercion from
322    // longer-lived references to shorter-lived ones
323    //
324    // &'static str <: &'a str, so a &'static str can be used
325    // where &'a str is expected
326    fn covariant<'a>(foo: &'a str) {}
327    fn demo_covariance(bar: &'static str) {
328        // Allowed via covariant coerction because
329        // &'static str is a sub-type of &'a str
330        covariant(bar);
331    }
332
333    // contravariance
334    //
335    fn accepts_static(_: fn(&'static str)) {}
336    fn takes_any<'a>(_: fn(&'a str)) {}
337    fn demo_contravariance<'a>(foo: fn(&'a str)) {
338        accepts_static(foo); // OK via contravariance
339    }
340}