nstd_sys/
vec.rs

1//! A dynamically sized contiguous sequence of values.
2extern crate alloc;
3use crate::{
4    alloc::{GLOBAL_ALLOCATOR, NSTD_ALLOCATOR},
5    core::{
6        alloc::{
7            nstd_core_alloc_layout_array, nstd_core_alloc_layout_array_unchecked,
8            NSTDAllocError::{self, NSTD_ALLOC_ERROR_NONE},
9            NSTDAllocator,
10        },
11        def::{NSTDByte, NSTDErrorCode},
12        mem::{nstd_core_mem_copy, nstd_core_mem_copy_overlapping, nstd_core_mem_dangling_mut},
13        optional::NSTDOptional,
14        slice::{
15            nstd_core_slice_align, nstd_core_slice_as_ptr, nstd_core_slice_len,
16            nstd_core_slice_mut_new_unchecked, nstd_core_slice_new_unchecked,
17            nstd_core_slice_stride, NSTDSlice, NSTDSliceMut,
18        },
19    },
20    NSTDAny, NSTDAnyMut, NSTDBool, NSTDUInt, NSTD_NULL,
21};
22use alloc::vec::Vec;
23use core::ptr::addr_of;
24use nstdapi::nstdapi;
25
26/// A dynamically sized contiguous sequence of values.
27#[nstdapi]
28pub struct NSTDVec<'a> {
29    /// The memory allocator.
30    allocator: &'a NSTDAllocator,
31    /// A raw pointer to the vector's memory buffer.
32    ptr: NSTDAnyMut,
33    /// The number of bytes each value in the vector takes up.
34    stride: NSTDUInt,
35    /// The memory alignment for the buffer.
36    align: NSTDUInt,
37    /// The number of active elements in the vector.
38    len: NSTDUInt,
39    /// The number of values allocated in the memory buffer.
40    cap: NSTDUInt,
41}
42impl<'a> NSTDVec<'a> {
43    /// Creates a new [`NSTDVec`] from a Rust [Vec].
44    #[allow(dead_code)]
45    pub(crate) fn from_vec<T>(vec: Vec<T>) -> NSTDVec<'a> {
46        let cap = vec.capacity();
47        let data = vec.leak();
48        NSTDVec {
49            allocator: &GLOBAL_ALLOCATOR,
50            ptr: data.as_mut_ptr().cast(),
51            stride: core::mem::size_of::<T>(),
52            align: core::mem::align_of::<T>(),
53            len: data.len(),
54            cap,
55        }
56    }
57
58    /// Checks if the vector's capacity is greater than 0.
59    #[inline]
60    const fn has_allocated(&self) -> NSTDBool {
61        self.cap > 0
62    }
63
64    /// Returns the number of active bytes in the vector.
65    #[inline]
66    #[allow(clippy::arithmetic_side_effects)]
67    const fn byte_len(&self) -> usize {
68        self.len * self.stride
69    }
70
71    /// Returns the number of bytes in the vector's memory buffer.
72    #[inline]
73    #[allow(clippy::arithmetic_side_effects)]
74    const fn buffer_byte_len(&self) -> usize {
75        self.cap * self.stride
76    }
77
78    /// Creates a Rust slice containing all the *active* elements from this `NSTDVec`.
79    ///
80    /// # Panics
81    ///
82    /// This operation will panic if `size_of::<T>()` does not match the vector's stride.
83    ///
84    /// # Safety
85    ///
86    /// - The vector's data must remain valid while the returned slice is in use.
87    ///
88    /// - The vector's data must be properly aligned.
89    #[inline]
90    #[allow(dead_code)]
91    pub(crate) unsafe fn as_slice<T>(&self) -> &[T] {
92        assert!(self.stride == core::mem::size_of::<T>());
93        core::slice::from_raw_parts(self.ptr as _, self.len)
94    }
95
96    /// Returns a pointer to one byte past the end of the vector.
97    #[inline]
98    fn end(&mut self) -> NSTDAnyMut {
99        // SAFETY: `self.ptr` is never null.
100        unsafe { self.ptr.add(self.byte_len()) }
101    }
102
103    /// Attempts to reserve some memory for the vector if needed.
104    #[inline]
105    fn try_reserve(&mut self) -> NSTDAllocError {
106        if self.len == self.cap {
107            #[allow(clippy::arithmetic_side_effects)]
108            let additional = 1 + self.cap / 2;
109            #[allow(unused_unsafe)]
110            // SAFETY: This operation is safe.
111            return unsafe { nstd_vec_reserve(self, additional) };
112        }
113        NSTD_ALLOC_ERROR_NONE
114    }
115}
116impl Drop for NSTDVec<'_> {
117    /// [`NSTDVec`]'s destructor.
118    #[inline]
119    fn drop(&mut self) {
120        let buffer_len = self.buffer_byte_len();
121        if buffer_len > 0 {
122            // SAFETY:
123            // - `self.align` is always a nonzero power of two.
124            // - `self.stride` is always a multiple of `self.align`.
125            // - The buffer's capacity in bytes never exceeds `NSTDInt`'s max value.
126            let layout = unsafe {
127                nstd_core_alloc_layout_array_unchecked(self.stride, self.align, self.cap)
128            };
129            // SAFETY: The vector has allocated.
130            unsafe { (self.allocator.deallocate)(self.allocator.state, self.ptr, layout) };
131        }
132    }
133}
134impl<A> FromIterator<A> for NSTDVec<'_> {
135    /// Creates a new [`NSTDVec`] from an iterator.
136    ///
137    /// # Note
138    ///
139    /// Each value will need to be dropped manually, as [`NSTDVec`] does not automatically drop it's
140    /// contents.
141    ///
142    /// # Panics
143    ///
144    /// This operation will panic if allocating fails.
145    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
146        let size = core::mem::size_of::<A>();
147        let align = core::mem::align_of::<A>();
148        #[allow(unused_unsafe)]
149        // SAFETY: This operation is safe.
150        let mut s = unsafe { nstd_vec_new(&NSTD_ALLOCATOR, size, align) };
151        let mut errc;
152        for v in iter {
153            // SAFETY: `v` is stored on the stack.
154            errc = unsafe { nstd_vec_push(&mut s, addr_of!(v).cast()) };
155            assert!(errc == NSTD_ALLOC_ERROR_NONE);
156            // Be sure to forget `v` so it doesn't get dropped.
157            core::mem::forget(v);
158        }
159        s
160    }
161}
162/// # Safety
163///
164/// The data that the vector holds must be able to be safely sent between threads.
165// SAFETY: The user guarantees that the data is thread-safe.
166unsafe impl Send for NSTDVec<'_> {}
167/// # Safety
168///
169/// The data that the vector holds must be able to be safely shared between threads.
170// SAFETY: The user guarantees that the data is thread-safe.
171unsafe impl Sync for NSTDVec<'_> {}
172
173/// Represents an optional value of type `NSTDVec`.
174pub type NSTDOptionalVec<'a> = NSTDOptional<NSTDVec<'a>>;
175
176/// Creates a new vector without allocating any resources.
177///
178/// # Parameters:
179///
180/// - `const NSTDAllocator *allocator` - The memory allocator.
181///
182/// - `NSTDUInt stride` - The size in bytes of each value in the vector.
183///
184/// - `NSTDUInt align` - The alignment of each value in the vector.
185///
186/// # Returns
187///
188/// `NSTDVec vec` - The new vector.
189///
190/// # Panics
191///
192/// This operation will panic if either `align` is not a power of two or `stride` is not a multiple
193/// of `align`.
194///
195/// # Example
196///
197/// ```
198/// use nstd_sys::{alloc::NSTD_ALLOCATOR, vec::nstd_vec_new};
199///
200/// const SIZE: usize = core::mem::size_of::<u32>();
201/// const ALIGN: usize = core::mem::align_of::<u32>();
202///
203/// let vec = unsafe { nstd_vec_new(&NSTD_ALLOCATOR, SIZE, ALIGN) };
204/// ```
205#[inline]
206#[nstdapi]
207#[allow(clippy::arithmetic_side_effects)]
208pub const fn nstd_vec_new(
209    allocator: &NSTDAllocator,
210    stride: NSTDUInt,
211    align: NSTDUInt,
212) -> NSTDVec<'_> {
213    assert!(crate::core::mem::is_power_of_two(align) && stride % align == 0);
214    NSTDVec {
215        allocator,
216        ptr: nstd_core_mem_dangling_mut(),
217        stride,
218        align,
219        cap: 0,
220        len: 0,
221    }
222}
223
224/// Creates a new vector initialized with the given capacity.
225///
226/// # Parameters:
227///
228/// - `const NSTDAllocator *allocator` - The memory allocator.
229///
230/// - `NSTDUInt stride` - The size in bytes of each value in the vector.
231///
232/// - `NSTDUInt align` - The alignment of each value in the vector.
233///
234/// - `NSTDUInt cap` - The initial capacity for the vector.
235///
236/// # Returns
237///
238/// `NSTDOptionalVec vec` - The new vector on success, or an uninitialized "none" variant if
239/// allocation fails.
240///
241/// # Example
242///
243/// ```
244/// use nstd_sys::{
245///     alloc::NSTD_ALLOCATOR,
246///     core::{
247///         alloc::NSTDAllocError::NSTD_ALLOC_ERROR_NONE,
248///         slice::{nstd_core_slice_get, nstd_core_slice_new},
249///     },
250///     vec::{nstd_vec_extend, nstd_vec_get, nstd_vec_len, nstd_vec_new_with_cap},
251/// };
252///
253/// const SIZE: usize = core::mem::size_of::<i16>();
254/// const ALIGN: usize = core::mem::align_of::<i16>();
255///
256/// unsafe {
257///     let numbers = [642i16, 324i16, 190i16];
258///     let numbers = nstd_core_slice_new(numbers.as_ptr().cast(), SIZE, ALIGN, 3).unwrap();
259///     let mut vec = nstd_vec_new_with_cap(&NSTD_ALLOCATOR, SIZE, ALIGN, 3).unwrap();
260///     assert!(nstd_vec_extend(&mut vec, &numbers) == NSTD_ALLOC_ERROR_NONE);
261///     for i in 0..nstd_vec_len(&vec) {
262///         let sv = nstd_core_slice_get(&numbers, i).cast::<i16>();
263///         let vv = nstd_vec_get(&vec, i).cast::<i16>();
264///         assert!(!sv.is_null() && !vv.is_null());
265///         assert!(*sv == *vv);
266///     }
267/// }
268/// ```
269#[nstdapi]
270pub fn nstd_vec_new_with_cap(
271    allocator: &NSTDAllocator,
272    stride: NSTDUInt,
273    align: NSTDUInt,
274    cap: NSTDUInt,
275) -> NSTDOptionalVec<'_> {
276    // Check if either `stride` or `cap` are zero.
277    if stride == 0 || cap == 0 {
278        #[allow(clippy::arithmetic_side_effects)]
279        if crate::core::mem::is_power_of_two(align) && stride % align == 0 {
280            return NSTDOptional::Some(NSTDVec {
281                allocator,
282                ptr: nstd_core_mem_dangling_mut(),
283                stride,
284                align,
285                cap,
286                len: 0,
287            });
288        }
289    }
290    // Attempt to allocate the memory buffer.
291    else if let NSTDOptional::Some(layout) = nstd_core_alloc_layout_array(stride, align, cap) {
292        // SAFETY: Both `stride` & `cap` are above 0.
293        let ptr = unsafe { (allocator.allocate)(allocator.state, layout) };
294        if !ptr.is_null() {
295            return NSTDOptional::Some(NSTDVec {
296                allocator,
297                ptr,
298                stride,
299                align,
300                cap,
301                len: 0,
302            });
303        }
304    }
305    NSTDOptional::None
306}
307
308/// Creates a new vector from a slice.
309///
310/// # Parameters:
311///
312/// - `const NSTDAllocator *allocator` - The memory allocator.
313///
314/// - `const NSTDSlice *slice` - The slice to copy data from.
315///
316/// # Returns
317///
318/// `NSTDOptionalVec vec` - The new vector with a copy of `slice`'s contents on success, or an
319/// uninitialized "none" variant if allocating fails.
320///
321/// # Safety
322///
323/// The caller of this function must ensure that `slice`'s data is valid for reads.
324///
325/// # Example
326///
327/// ```
328/// use nstd_sys::{
329///     alloc::NSTD_ALLOCATOR,
330///     core::slice::{nstd_core_slice_get, nstd_core_slice_new},
331///     vec::{nstd_vec_from_slice, nstd_vec_get, nstd_vec_len},
332/// };
333///
334/// const SIZE: usize = core::mem::size_of::<u128>();
335/// const ALIGN: usize = core::mem::align_of::<u128>();
336///
337/// unsafe {
338///     let numbers = [59237u128, 13953u128, 50285u128];
339///     let numbers = nstd_core_slice_new(numbers.as_ptr().cast(), SIZE, ALIGN, 3).unwrap();
340///     let mut vec = nstd_vec_from_slice(&NSTD_ALLOCATOR, &numbers).unwrap();
341///     for i in 0..nstd_vec_len(&vec) {
342///         let sv = nstd_core_slice_get(&numbers, i).cast::<u128>();
343///         let vv = nstd_vec_get(&vec, i).cast::<u128>();
344///         assert!(!sv.is_null() && !vv.is_null());
345///         assert!(*sv == *vv);
346///     }
347/// }
348/// ```
349#[nstdapi]
350pub unsafe fn nstd_vec_from_slice<'a>(
351    allocator: &'a NSTDAllocator,
352    slice: &NSTDSlice,
353) -> NSTDOptionalVec<'a> {
354    let stride = nstd_core_slice_stride(slice);
355    let align = nstd_core_slice_align(slice);
356    let len = nstd_core_slice_len(slice);
357    #[allow(clippy::arithmetic_side_effects)]
358    if len > 0 {
359        // Allocate the new vector.
360        if let NSTDOptional::Some(mut vec) = nstd_vec_new_with_cap(allocator, stride, align, len) {
361            let bytes = len * stride;
362            nstd_core_mem_copy(vec.ptr.cast(), nstd_core_slice_as_ptr(slice).cast(), bytes);
363            vec.len = len;
364            return NSTDOptional::Some(vec);
365        }
366    }
367    NSTDOptional::Some(NSTDVec {
368        allocator,
369        ptr: nstd_core_mem_dangling_mut(),
370        stride,
371        align,
372        cap: 0,
373        len: 0,
374    })
375}
376
377/// Creates a new deep copy of `vec`.
378///
379/// # Parameters:
380///
381/// - `const NSTDVec *vec` - The vector to create a new deep copy of.
382///
383/// # Returns
384///
385/// `NSTDOptionalVec cloned` - The new deep copy of `vec` on success, or an uninitialized "none"
386/// variant if allocating fails.
387#[nstdapi]
388pub fn nstd_vec_clone<'a>(vec: &NSTDVec<'a>) -> NSTDOptionalVec<'a> {
389    if vec.len > 0 {
390        let NSTDOptional::Some(mut cloned) =
391            nstd_vec_new_with_cap(vec.allocator, vec.stride, vec.align, vec.len)
392        else {
393            return NSTDOptional::None;
394        };
395        // SAFETY: Both vectors are non-null.
396        unsafe { nstd_core_mem_copy(cloned.ptr.cast(), vec.ptr.cast(), vec.byte_len()) };
397        cloned.len = vec.len;
398        NSTDOptional::Some(cloned)
399    } else {
400        NSTDOptional::Some(nstd_vec_new(vec.allocator, vec.stride, vec.align))
401    }
402}
403
404/// Returns an immutable reference to a vector's allocator.
405///
406/// # Parameters:
407///
408/// - `const NSTDVec *vec` - The vector.
409///
410/// # Returns
411///
412/// `const NSTDAllocator *allocator` - The vector's allocator.
413#[inline]
414#[nstdapi]
415pub const fn nstd_vec_allocator<'a>(vec: &NSTDVec<'a>) -> &'a NSTDAllocator {
416    vec.allocator
417}
418
419/// Returns the length of a vector.
420///
421/// # Parameters:
422///
423/// - `const NSTDVec *vec` - The vector.
424///
425/// # Returns
426///
427/// `NSTDUInt len` - The length of the vector.
428#[inline]
429#[nstdapi]
430pub const fn nstd_vec_len(vec: &NSTDVec<'_>) -> NSTDUInt {
431    vec.len
432}
433
434/// Returns a vector's capacity.
435///
436/// This is the max number of values the vector can contain without reallocating.
437///
438/// # Parameters:
439///
440/// - `const NSTDVec *vec` - The vector.
441///
442/// # Returns
443///
444/// `NSTDUInt cap` - The vector's capacity.
445#[inline]
446#[nstdapi]
447pub const fn nstd_vec_cap(vec: &NSTDVec<'_>) -> NSTDUInt {
448    vec.cap
449}
450
451/// Returns the amount of bytes each value in a vector occupies.
452///
453/// # Parameters:
454///
455/// - `const NSTDVec *vec` - The vector.
456///
457/// # Returns
458///
459/// `NSTDUInt stride` - The size of each value in the vector.
460#[inline]
461#[nstdapi]
462pub const fn nstd_vec_stride(vec: &NSTDVec<'_>) -> NSTDUInt {
463    vec.stride
464}
465
466/// Returns the number of reserved elements within a vector's inactive buffer.
467///
468/// # Parameters:
469///
470/// - `const NSTDVec *vec` - The vector.
471///
472/// # Returns
473///
474/// `NSTDUInt reserved` - The number of uninitialized elements within `vec`'s inactive buffer.
475///
476/// # Example
477///
478/// ```
479/// use nstd_sys::{
480///     alloc::NSTD_ALLOCATOR,
481///     vec::{nstd_vec_new_with_cap, nstd_vec_reserved},
482/// };
483///
484/// unsafe {
485///     let vec = nstd_vec_new_with_cap(&NSTD_ALLOCATOR, 8, 2, 16).unwrap();
486///     assert!(nstd_vec_reserved(&vec) == 16);
487/// }
488/// ```
489#[inline]
490#[nstdapi]
491#[allow(clippy::arithmetic_side_effects)]
492pub const fn nstd_vec_reserved(vec: &NSTDVec<'_>) -> NSTDUInt {
493    vec.cap - vec.len
494}
495
496/// Returns an immutable slice containing all of a vector's active elements.
497///
498/// # Parameters:
499///
500/// - `const NSTDVec *vec` - The vector.
501///
502/// # Returns
503///
504/// `NSTDSlice slice` - An *immutable* view into the vector.
505#[inline]
506#[nstdapi]
507pub const fn nstd_vec_as_slice(vec: &NSTDVec<'_>) -> NSTDSlice {
508    // SAFETY: `vec.ptr` is checked, vector lengths are never greater than `NSTDInt`'s max value.
509    unsafe { nstd_core_slice_new_unchecked(vec.ptr, vec.stride, vec.align, vec.len) }
510}
511
512/// Returns a slice containing all of a vector's active elements.
513///
514/// # Parameters:
515///
516/// - `NSTDVec *vec` - The vector.
517///
518/// # Returns
519///
520/// `NSTDSliceMut slice` - A *mutable* view into the vector.
521#[inline]
522#[nstdapi]
523pub fn nstd_vec_as_slice_mut(vec: &mut NSTDVec<'_>) -> NSTDSliceMut {
524    // SAFETY: `vec.ptr` is checked, vector lengths are never greater than `NSTDInt`'s max value.
525    unsafe { nstd_core_slice_mut_new_unchecked(vec.ptr, vec.stride, vec.align, vec.len) }
526}
527
528/// Returns a pointer to a vector's raw data.
529///
530/// # Parameters:
531///
532/// - `const NSTDVec *vec` - The vector.
533///
534/// # Returns
535///
536/// `NSTDAny ptr` - A pointer to the vector's raw data.
537#[inline]
538#[nstdapi]
539pub const fn nstd_vec_as_ptr(vec: &NSTDVec<'_>) -> NSTDAny {
540    vec.ptr
541}
542
543/// Returns a pointer to a vector's raw data.
544///
545/// # Parameters:
546///
547/// - `NSTDVec *vec` - The vector.
548///
549/// # Returns
550///
551/// `NSTDAnyMut ptr` - A pointer to the vector's raw data.
552#[inline]
553#[nstdapi]
554pub fn nstd_vec_as_ptr_mut(vec: &mut NSTDVec<'_>) -> NSTDAnyMut {
555    vec.ptr
556}
557
558/// Returns a pointer to the end of a vector.
559///
560/// Note that this does not return a pointer to the last element or the last byte in the vector, but
561/// a pointer to *one byte past* the end of the vector's active buffer.
562///
563/// # Parameters:
564///
565/// - `const NSTDVec *vec` - The vector.
566///
567/// # Returns
568///
569/// `NSTDAny end` - A pointer to the end of the vector.
570#[inline]
571#[nstdapi]
572pub const fn nstd_vec_end(vec: &NSTDVec<'_>) -> NSTDAny {
573    // SAFETY: `len` is within the bounds of the vector and does not overflow `isize`.
574    unsafe { vec.ptr.add(vec.byte_len()) }
575}
576
577/// Returns a mutable pointer to the end of a vector.
578///
579/// Note that this does not return a pointer to the last element or the last byte in the vector, but
580/// a pointer to *one byte past* the end of the vector's active buffer.
581///
582/// # Parameters:
583///
584/// - `NSTDVec *vec` - The vector.
585///
586/// # Returns
587///
588/// `NSTDAnyMut end` - A mutable pointer to the end of the vector.
589#[inline]
590#[nstdapi]
591pub fn nstd_vec_end_mut(vec: &mut NSTDVec<'_>) -> NSTDAnyMut {
592    // SAFETY: `len` is within the bounds of the vector and does not overflow `isize`.
593    unsafe { vec.ptr.add(vec.byte_len()) }
594}
595
596/// Returns an immutable pointer to the element at index `pos` in `vec`.
597///
598/// # Note
599///
600/// It is highly advised to copy the return value onto the stack because the pointer can easily
601/// become invalid if the vector is mutated.
602///
603/// # Parameters:
604///
605/// - `const NSTDVec *vec` - The vector to read an element from.
606///
607/// - `NSTDUInt pos` - The position of the element to get, starting at 0.
608///
609/// # Returns
610///
611/// `NSTDAny element` - A pointer to the element at `pos` or `NSTD_NULL` if `pos` is out
612/// of the vector's boundaries.
613///
614/// # Example
615///
616/// ```
617/// use nstd_sys::{
618///     alloc::NSTD_ALLOCATOR,
619///     core::slice::{nstd_core_slice_get, nstd_core_slice_new},
620///     vec::{nstd_vec_from_slice, nstd_vec_get, nstd_vec_len},
621/// };
622///
623/// const SIZE: usize = core::mem::size_of::<i64>();
624/// const ALIGN: usize = core::mem::size_of::<i64>();
625///
626/// unsafe {
627///     let numbers = [-639i64, 429i64, -440i64];
628///     let numbers = nstd_core_slice_new(numbers.as_ptr().cast(), SIZE, ALIGN, 3).unwrap();
629///     let mut vec = nstd_vec_from_slice(&NSTD_ALLOCATOR, &numbers).unwrap();
630///     for i in 0..nstd_vec_len(&vec) {
631///         let sv = nstd_core_slice_get(&numbers, i).cast::<i64>();
632///         let vv = nstd_vec_get(&vec, i).cast::<i64>();
633///         assert!(!sv.is_null() && !vv.is_null());
634///         assert!(*sv == *vv);
635///     }
636/// }
637/// ```
638#[inline]
639#[nstdapi]
640pub const fn nstd_vec_get(vec: &NSTDVec<'_>, mut pos: NSTDUInt) -> NSTDAny {
641    #[allow(clippy::arithmetic_side_effects)]
642    if pos < vec.len {
643        pos *= vec.stride;
644        // SAFETY: `pos` is a valid index.
645        return unsafe { vec.ptr.add(pos) };
646    }
647    NSTD_NULL
648}
649
650/// Returns a pointer to the element at index `pos` in `vec`.
651///
652/// # Note
653///
654/// It is highly advised to copy the return value onto the stack because the pointer can easily
655/// become invalid if the vector is mutated.
656///
657/// # Parameters:
658///
659/// - `NSTDVec *vec` - The vector to read an element from.
660///
661/// - `NSTDUInt pos` - The position of the element to get, starting at 0.
662///
663/// # Returns
664///
665/// `NSTDAnyMut element` - A pointer to the element at `pos` or `NSTD_NULL` if `pos` is out of
666/// the vector's boundaries.
667///
668/// # Example
669///
670/// ```
671/// use nstd_sys::{
672///     alloc::NSTD_ALLOCATOR,
673///     core::slice::{nstd_core_slice_get, nstd_core_slice_new},
674///     vec::{nstd_vec_from_slice, nstd_vec_get, nstd_vec_get_mut, nstd_vec_len},
675/// };
676///
677/// const SIZE: usize = core::mem::size_of::<i64>();
678/// const ALIGN: usize = core::mem::size_of::<i64>();
679///
680/// unsafe {
681///     let numbers = [639i64, -429i64, 440i64];
682///     let numbers = nstd_core_slice_new(numbers.as_ptr().cast(), SIZE, ALIGN, 3).unwrap();
683///     let mut vec = nstd_vec_from_slice(&NSTD_ALLOCATOR, &numbers).unwrap();
684///     for i in 0..nstd_vec_len(&vec) {
685///         let vv = nstd_vec_get_mut(&mut vec, i).cast::<i64>();
686///         assert!(!vv.is_null());
687///         *vv = -*vv;
688///     }
689///     for i in 0..nstd_vec_len(&vec) {
690///         let sv = nstd_core_slice_get(&numbers, i).cast::<i64>();
691///         let vv = nstd_vec_get(&vec, i).cast::<i64>();
692///         assert!(!sv.is_null() && !vv.is_null());
693///         assert!(-*sv == *vv);
694///     }
695/// }
696/// ```
697#[inline]
698#[nstdapi]
699pub fn nstd_vec_get_mut(vec: &mut NSTDVec<'_>, pos: NSTDUInt) -> NSTDAnyMut {
700    nstd_vec_get(vec, pos).cast_mut()
701}
702
703/// Pushes a value onto a vector by copying bytes to the end of the vector's buffer. The number of
704/// bytes to push is determined by `vec`'s stride.
705///
706/// # Parameters:
707///
708/// - `NSTDVec *vec` - The vector.
709///
710/// - `NSTDAny value` - A pointer to the value to push onto the vector.
711///
712/// # Returns
713///
714/// `NSTDAllocError errc` - The allocation operation error code.
715///
716/// # Safety
717///
718/// This operation is unsafe because undefined behavior can occur if the size of the value being
719/// pushed onto the vector is not equal to `vec`'s stride.
720///
721/// # Example
722///
723/// ```
724/// use core::ptr::addr_of;
725/// use nstd_sys::{
726///     alloc::NSTD_ALLOCATOR,
727///     vec::{nstd_vec_new, nstd_vec_push},
728/// };
729///
730/// const SIZE: usize = core::mem::size_of::<f64>();
731/// const ALIGN: usize = core::mem::size_of::<f64>();
732///
733/// unsafe {
734///     let mut vec = nstd_vec_new(&NSTD_ALLOCATOR, SIZE, ALIGN);
735///     let values: [f64; 3] = [6.0, 3.1, 9.4];
736///     for value in values {
737///         nstd_vec_push(&mut vec, addr_of!(value).cast());
738///     }
739/// }
740/// ```
741#[inline]
742#[nstdapi]
743pub unsafe fn nstd_vec_push(vec: &mut NSTDVec<'_>, value: NSTDAny) -> NSTDAllocError {
744    // Attempt to reserve space for the push.
745    let errc = vec.try_reserve();
746    // On success: copy bytes to the end of the vector.
747    if errc == NSTD_ALLOC_ERROR_NONE {
748        nstd_core_mem_copy(vec.end().cast(), value.cast(), vec.stride);
749        vec.len = match vec.len.checked_add(1) {
750            Some(len) => len,
751            _ => return NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT,
752        };
753    }
754    errc
755}
756
757/// Removes the last value of a vector and returns a pointer to it.
758///
759/// # Note
760///
761/// It is highly advised to copy the return value onto the stack because the pointer can easily
762/// become invalid if the vector is mutated.
763///
764/// # Parameters:
765///
766/// - `NSTDVec *vec` - The vector.
767///
768/// # Returns
769///
770/// - `NSTDAny value` - A pointer to the value that was popped off the stack, or null if the
771/// vector is empty.
772///
773/// # Example
774///
775/// ```
776/// use core::ptr::addr_of;
777/// use nstd_sys::{
778///     alloc::NSTD_ALLOCATOR,
779///     core::slice::nstd_core_slice_new,
780///     vec::{nstd_vec_extend, nstd_vec_new, nstd_vec_pop},
781/// };
782///
783/// const SIZE: usize = core::mem::size_of::<f64>();
784/// const ALIGN: usize = core::mem::size_of::<f64>();
785///
786/// unsafe {
787///     let mut vec = nstd_vec_new(&NSTD_ALLOCATOR, SIZE, ALIGN);
788///     let values: [f64; 3] = [9.4, 3.1, 6.0];
789///     let values_slice = nstd_core_slice_new(values.as_ptr().cast(), SIZE, ALIGN, 3).unwrap();
790///     nstd_vec_extend(&mut vec, &values_slice);
791///     for value in values.iter().rev() {
792///         assert!(*value == *nstd_vec_pop(&mut vec).cast::<f64>());
793///     }
794/// }
795/// ```
796#[inline]
797#[nstdapi]
798pub fn nstd_vec_pop(vec: &mut NSTDVec<'_>) -> NSTDAny {
799    #[allow(clippy::arithmetic_side_effects)]
800    if vec.len > 0 {
801        vec.len -= 1;
802        return vec.end();
803    }
804    NSTD_NULL
805}
806
807/// Attempts to insert a value into a vector at `index`.
808///
809/// # Parameters:
810///
811/// - `NSTDVec *vec` - The vector.
812///
813/// - `NSTDAny value` - A pointer to the value to insert into the vector.
814///
815/// - `NSTDUInt index` - The index at which to insert the value.
816///
817/// # Returns
818///
819/// `NSTDErrorCode errc` - Nonzero on error.
820///
821/// # Errors
822///
823/// - `1` - `index` is greater than the vector's length.
824///
825/// - `2` - Reserving space for the vector failed.
826///
827/// # Safety
828///
829/// This operation is unsafe because undefined behavior can occur if the size of the value being
830/// inserted into the vector is not equal to `vec`'s stride.
831///
832/// # Example
833///
834/// ```
835/// use core::ptr::addr_of;
836/// use nstd_sys::{
837///     alloc::NSTD_ALLOCATOR,
838///     core::slice::nstd_core_slice_new,
839///     vec::{nstd_vec_from_slice, nstd_vec_get, nstd_vec_insert},
840/// };
841///
842/// const SIZE: usize = core::mem::size_of::<u32>();
843/// const ALIGN: usize = core::mem::size_of::<u32>();
844///
845/// unsafe {
846///     let slice: [u32; 4] = [1, 2, 3, 5];
847///     let slice = nstd_core_slice_new(slice.as_ptr().cast(), SIZE, ALIGN, 4).unwrap();
848///     let mut vec = nstd_vec_from_slice(&NSTD_ALLOCATOR, &slice).unwrap();
849///     let four = 4u32;
850///     assert!(nstd_vec_insert(&mut vec, addr_of!(four).cast(), 3) == 0);
851///     for i in 1..=5 {
852///         let v = nstd_vec_get(&vec, i - 1);
853///         assert!(!v.is_null());
854///         assert!(*v.cast::<u32>() == i as u32);
855///     }
856/// }
857/// ```
858#[nstdapi]
859pub unsafe fn nstd_vec_insert(
860    vec: &mut NSTDVec<'_>,
861    value: NSTDAny,
862    mut index: NSTDUInt,
863) -> NSTDErrorCode {
864    // Make sure `index` is valid.
865    if index > vec.len {
866        1
867    }
868    // Attempt to reserve space for the insert.
869    else if vec.try_reserve() != NSTD_ALLOC_ERROR_NONE {
870        2
871    }
872    // Insert the value.
873    else {
874        // Move elements at/after `index` over by one element.
875        #[allow(clippy::arithmetic_side_effects)]
876        if vec.stride > 0 {
877            let stride = vec.stride;
878            let bytes_to_copy = (vec.len - index) * stride;
879            index *= stride;
880            let idxptr = vec.ptr.add(index).cast::<NSTDByte>();
881            let dest = idxptr.add(stride);
882            nstd_core_mem_copy_overlapping(dest, idxptr, bytes_to_copy);
883            // Write `value` over the old value at `index`.
884            nstd_core_mem_copy(idxptr, value.cast(), stride);
885            vec.len += 1;
886        } else {
887            vec.len = match vec.len.checked_add(1) {
888                Some(len) => len,
889                _ => return 2,
890            };
891        }
892        0
893    }
894}
895
896/// Removes the element at `index` in a vector.
897///
898/// # Parameters:
899///
900/// - `NSTDVec *vec` - The vector.
901///
902/// - `NSTDUInt index` - The index of the element to remove.
903///
904/// # Returns
905///
906/// `NSTDErrorCode errc` - Nonzero if `index` is invalid.
907///
908/// # Example
909///
910/// ```
911/// use nstd_sys::{
912///     alloc::NSTD_ALLOCATOR,
913///     core::slice::nstd_core_slice_new,
914///     vec::{nstd_vec_from_slice, nstd_vec_get, nstd_vec_remove},
915/// };
916///
917/// const SIZE: usize = core::mem::size_of::<u32>();
918/// const ALIGN: usize = core::mem::align_of::<u32>();
919///
920/// unsafe {
921///     let slice: [u32; 5] = [1, 2, 3, 4, 5];
922///     let slice = nstd_core_slice_new(slice.as_ptr().cast(), SIZE, ALIGN, 5).unwrap();
923///     let mut vec = nstd_vec_from_slice(&NSTD_ALLOCATOR, &slice).unwrap();
924///     assert!(nstd_vec_remove(&mut vec, 0) == 0);
925///     assert!(nstd_vec_remove(&mut vec, 3) == 0);
926///     for i in 0..3 {
927///         let v = nstd_vec_get(&vec, i);
928///         assert!(!v.is_null());
929///         assert!(*v.cast::<u32>() == (i + 2) as u32);
930///     }
931/// }
932/// ```
933#[nstdapi]
934pub fn nstd_vec_remove(vec: &mut NSTDVec<'_>, mut index: NSTDUInt) -> NSTDErrorCode {
935    // Make sure `index` is valid. This also ensures that `vec.len` is at least 1.
936    #[allow(clippy::arithmetic_side_effects)]
937    if index < vec.len {
938        // Move bytes after `index` to the left by one element.
939        if vec.stride > 0 {
940            let stride = vec.stride;
941            let bytes_to_copy = (vec.len - index - 1) * stride;
942            index *= stride;
943            // SAFETY: The vector's data is valid for the shift.
944            unsafe {
945                let idxptr = vec.ptr.add(index).cast::<NSTDByte>();
946                let src = idxptr.add(stride);
947                nstd_core_mem_copy_overlapping(idxptr, src, bytes_to_copy);
948            }
949        }
950        // Decrement the vector's length AFTER shifting the bytes.
951        vec.len -= 1;
952        0
953    } else {
954        1
955    }
956}
957
958/// Pushes a series of values onto a vector.
959///
960/// # Parameters:
961///
962/// - `NSTDVec *vec` - The vector to extend.
963///
964/// - `const NSTDSlice *values` - A slice of values to push onto the vector.
965///
966/// # Returns
967///
968/// `NSTDAllocError errc` - The allocation operation error code.
969///
970/// # Panics
971///
972/// This operation will panic if `vec` and `values` strides do not match.
973///
974/// # Safety
975///
976/// This operation can cause undefined behavior if `values`'s data is invalid.
977///
978/// # Example
979///
980/// ```
981/// use nstd_sys::{
982///     alloc::NSTD_ALLOCATOR,
983///     core::{alloc::NSTDAllocError::NSTD_ALLOC_ERROR_NONE, slice::nstd_core_slice_new},
984///     vec::{nstd_vec_extend, nstd_vec_get, nstd_vec_new},
985/// };
986///
987/// const SIZE: usize = core::mem::size_of::<i128>();
988/// const ALIGN: usize = core::mem::align_of::<i128>();
989///
990/// unsafe {
991///     let values: [i128; 5] = [1, 2, 3, 4, 5];
992///     let slice = nstd_core_slice_new(values.as_ptr().cast(), SIZE, ALIGN, 5).unwrap();
993///     let mut vec = nstd_vec_new(&NSTD_ALLOCATOR, SIZE, ALIGN);
994///     assert!(nstd_vec_extend(&mut vec, &slice) == NSTD_ALLOC_ERROR_NONE);
995///     for i in 0..5 {
996///         let v = nstd_vec_get(&vec, i);
997///         assert!(!v.is_null());
998///         assert!(*v.cast::<i128>() == values[i]);
999///     }
1000/// }
1001/// ```
1002#[nstdapi]
1003pub unsafe fn nstd_vec_extend(vec: &mut NSTDVec<'_>, values: &NSTDSlice) -> NSTDAllocError {
1004    // Ensure value sizes are the same for both the vector and the slice.
1005    assert!(vec.stride == nstd_core_slice_stride(values));
1006    let len = nstd_core_slice_len(values);
1007    // Making sure there's enough space for the extension.
1008    let mut errc = NSTD_ALLOC_ERROR_NONE;
1009    let reserved = nstd_vec_reserved(vec);
1010    if reserved < len {
1011        #[allow(clippy::arithmetic_side_effects)]
1012        let additional = len - reserved;
1013        errc = nstd_vec_reserve(vec, additional);
1014    }
1015    // On success copy bytes to the end of the vector.
1016    if errc == NSTD_ALLOC_ERROR_NONE {
1017        let ptr = nstd_core_slice_as_ptr(values).cast();
1018        nstd_core_mem_copy(vec.end().cast(), ptr, values.byte_len());
1019        vec.len = match vec.len.checked_add(len) {
1020            Some(len) => len,
1021            _ => return NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT,
1022        };
1023    }
1024    errc
1025}
1026
1027/// Shortens a vector, keeping the first `len` elements.
1028///
1029/// # Note
1030///
1031/// This function does nothing if `vec.len` is less than or equal to `len`.
1032///
1033/// # Parameters:
1034///
1035/// - `NSTDVec *vec` - The vector to truncate.
1036///
1037/// - `NSTDUInt len` - The number of elements to keep.
1038#[inline]
1039#[nstdapi]
1040pub fn nstd_vec_truncate(vec: &mut NSTDVec<'_>, len: NSTDUInt) {
1041    if vec.len > len {
1042        vec.len = len;
1043    }
1044}
1045
1046/// Sets a vectors length.
1047///
1048/// # Parameters:
1049///
1050/// - `NSTDVec *vec` - The vector.
1051///
1052/// - `NSTDUInt len` - The new length for the vector.
1053///
1054/// # Safety
1055///
1056/// - If `len` is greater than the vector's current length, care must be taken to ensure that the
1057/// new elements are properly initialized.
1058///
1059/// - `len`'s value must not be greater than the vector's capacity.
1060#[inline]
1061#[nstdapi]
1062pub unsafe fn nstd_vec_set_len(vec: &mut NSTDVec<'_>, len: NSTDUInt) {
1063    vec.len = len;
1064}
1065
1066/// Reserves some space on the heap for at least `size` more elements to be pushed onto a vector
1067/// without making more allocations.
1068///
1069/// # Parameters:
1070///
1071/// - `NSTDVec *vec` - The vector to reserve space for.
1072///
1073/// - `NSTDUInt size` - The number of additional elements to allocate for.
1074///
1075/// # Returns
1076///
1077/// `NSTDAllocError errc` - The allocation operation error code.
1078#[nstdapi]
1079pub fn nstd_vec_reserve(vec: &mut NSTDVec<'_>, size: NSTDUInt) -> NSTDAllocError {
1080    // Calculate the number of bytes to allocate.
1081    let Some(bytes_to_alloc) = size.checked_mul(vec.stride) else {
1082        return NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT;
1083    };
1084    if bytes_to_alloc == 0 {
1085        vec.cap = match vec.cap.checked_add(size) {
1086            Some(cap) => cap,
1087            _ => return NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT,
1088        };
1089        return NSTD_ALLOC_ERROR_NONE;
1090    }
1091    // Check if the vector has allocated.
1092    if vec.has_allocated() {
1093        // This can't be 0 because the vector is non-null.
1094        // After an nstd vector has allocated it will always have at least one value allocated.
1095        // An example of this behavior can be seen in `nstd_vec_shrink`.
1096        let Some(new_cap) = vec.cap.checked_add(size) else {
1097            return NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT;
1098        };
1099        let new_layout = match nstd_core_alloc_layout_array(vec.stride, vec.align, new_cap) {
1100            NSTDOptional::Some(new_layout) => new_layout,
1101            NSTDOptional::None => return NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT,
1102        };
1103        // SAFETY: `byte_len` is never greater than `NSTDInt`'s max value, `vec.align` is valid.
1104        let old_layout =
1105            unsafe { nstd_core_alloc_layout_array_unchecked(vec.stride, vec.align, vec.cap) };
1106        // SAFETY: The vector is non-null & the lengths are above 0.
1107        let errc = unsafe {
1108            (vec.allocator.reallocate)(vec.allocator.state, &mut vec.ptr, old_layout, new_layout)
1109        };
1110        // On success increase the buffer length.
1111        if errc == NSTD_ALLOC_ERROR_NONE {
1112            vec.cap = new_cap;
1113        }
1114        errc
1115    } else {
1116        let layout = match nstd_core_alloc_layout_array(vec.stride, vec.align, size) {
1117            NSTDOptional::Some(layout) => layout,
1118            NSTDOptional::None => return NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT,
1119        };
1120        // SAFETY: `bytes_to_alloc` is above 0.
1121        let mem = unsafe { (vec.allocator.allocate)(vec.allocator.state, layout) };
1122        if !mem.is_null() {
1123            vec.ptr = mem;
1124            vec.cap = size;
1125            return NSTD_ALLOC_ERROR_NONE;
1126        }
1127        NSTDAllocError::NSTD_ALLOC_ERROR_OUT_OF_MEMORY
1128    }
1129}
1130
1131/// Decreases a vector's capacity to match it's length.
1132///
1133/// # Parameters:
1134///
1135/// - `NSTDVec *vec` - The vector.
1136///
1137/// # Returns
1138///
1139/// `NSTDAllocError errc` - The allocation operation error code.
1140#[nstdapi]
1141pub fn nstd_vec_shrink(vec: &mut NSTDVec<'_>) -> NSTDAllocError {
1142    // Make sure the vector's capacity is greater than it's length.
1143    if vec.cap > vec.len {
1144        let new_cap = vec.len.max(1);
1145        // Make sure the vector's stride is greater than 0 before reallocating.
1146        if vec.stride > 0 {
1147            // Make sure to allocate at least one element to avoid undefined behavior.
1148            let new_layout = match nstd_core_alloc_layout_array(vec.stride, vec.align, new_cap) {
1149                NSTDOptional::Some(new_layout) => new_layout,
1150                NSTDOptional::None => return NSTDAllocError::NSTD_ALLOC_ERROR_INVALID_LAYOUT,
1151            };
1152            // SAFETY: `byte_len` is never greater than `NSTDInt`'s max value, `vec.align` is valid.
1153            let old_layout =
1154                unsafe { nstd_core_alloc_layout_array_unchecked(vec.stride, vec.align, vec.cap) };
1155            // SAFETY: The vector is non-null & the lengths are above 0.
1156            let errc = unsafe {
1157                (vec.allocator.reallocate)(
1158                    vec.allocator.state,
1159                    &mut vec.ptr,
1160                    old_layout,
1161                    new_layout,
1162                )
1163            };
1164            if errc == NSTD_ALLOC_ERROR_NONE {
1165                // The buffer's new length is at least 1.
1166                vec.cap = new_cap;
1167            }
1168            return errc;
1169        }
1170        vec.cap = new_cap;
1171    }
1172    NSTD_ALLOC_ERROR_NONE
1173}
1174
1175/// Sets a vector's length to zero.
1176///
1177/// # Parameters:
1178///
1179/// - `NSTDVec *vec` - The vector to clear.
1180#[inline]
1181#[nstdapi]
1182pub fn nstd_vec_clear(vec: &mut NSTDVec<'_>) {
1183    vec.len = 0;
1184}
1185
1186/// Frees an instance of `NSTDVec`.
1187///
1188/// # Parameters:
1189///
1190/// - `NSTDVec vec` - The vector to free.
1191#[inline]
1192#[nstdapi]
1193#[allow(
1194    unused_variables,
1195    clippy::missing_const_for_fn,
1196    clippy::needless_pass_by_value
1197)]
1198pub fn nstd_vec_free(vec: NSTDVec<'_>) {}
1199
1200/// Frees an instance of `NSTDVec` after invoking `callback` with each of the vector's elements.
1201///
1202/// # Parameters:
1203///
1204/// - `NSTDVec vec` - The vector to free.
1205///
1206/// - `void (*callback)(NSTDAnyMut)` - The vector data's destructor.
1207///
1208/// # Safety
1209///
1210/// This operation makes a direct call on a C function pointer (`callback`).
1211#[nstdapi]
1212pub unsafe fn nstd_vec_drop(mut vec: NSTDVec<'_>, callback: unsafe extern "C" fn(NSTDAnyMut)) {
1213    let mut ptr = nstd_vec_as_ptr_mut(&mut vec);
1214    let end = nstd_vec_end_mut(&mut vec);
1215    while ptr < end {
1216        callback(ptr);
1217        ptr = ptr.add(vec.stride);
1218    }
1219}