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}