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