osom_lib_arrays/immutable_array/
immutable_array_builder.rs

1#![allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss)]
2
3use osom_lib_alloc::Allocator;
4use osom_lib_primitives::Length;
5
6#[cfg(feature = "std_alloc")]
7use osom_lib_alloc::StdAllocator;
8
9use crate::errors::ArrayConstructionError;
10
11use super::internal_array::{InternalArray, MAX_LENGTH};
12use super::{ImmutableArray, ImmutableWeakArray};
13
14const INITIAL_CAPACITY: Length = unsafe { Length::new_unchecked(16) };
15
16/// A builder for [`ImmutableArray`][`super::ImmutableArray`].
17///
18/// # Notes
19///
20/// This struct is very similar to a mutable vec. It is used to construct
21/// [`ImmutableArray`][`super::ImmutableArray`] incrementally, in place.
22pub struct ImmutableArrayBuilder<
23    T: Sized,
24    #[cfg(feature = "std_alloc")] TAllocator = StdAllocator,
25    #[cfg(not(feature = "std_alloc"))] TAllocator,
26> where
27    TAllocator: Allocator,
28{
29    internal: InternalArray<T, TAllocator>,
30}
31
32impl<T: Sized, TAllocator: Allocator> ImmutableArrayBuilder<T, TAllocator> {
33    #[inline(always)]
34    const fn grow_formula(current: usize) -> Length {
35        unsafe { Length::new_unchecked((3 * (current / 2)) as i32) }
36    }
37
38    /// Shrinks current capacity to the length.
39    ///
40    /// # Notes
41    ///
42    /// This method reallocates entire buffer if the current capacity is greater than the length.
43    ///
44    /// # Errors
45    ///
46    /// For details see [`ArrayConstructionError`].
47    pub fn shrink_to_fit(&mut self) -> Result<(), ArrayConstructionError> {
48        let internal = &mut self.internal;
49        let internal_len = internal.len();
50        let internal_capacity = internal.capacity();
51        if internal_len == internal_capacity {
52            return Ok(());
53        }
54
55        let mut new_internal: InternalArray<T, TAllocator> =
56            InternalArray::allocate(internal_len, internal_len, internal.allocator().clone())?;
57        let heap_data = new_internal.heap_data_mut();
58        let data_ptr = heap_data.data().as_ptr();
59        unsafe {
60            data_ptr.copy_from_nonoverlapping(internal.heap_data().data().as_ptr(), internal_len.value() as usize);
61        }
62        self.internal = new_internal;
63        Ok(())
64    }
65
66    /// Builds the [`ImmutableArray`] from the builder.
67    ///
68    /// # Notes
69    ///
70    /// This method is basically free. It is here only to move
71    /// ownership from mutable builder to immutable array.
72    #[inline(always)]
73    pub fn build(self) -> ImmutableArray<T, TAllocator> {
74        let internal = unsafe { core::ptr::read(&self.internal) };
75        core::mem::forget(self);
76        ImmutableArray::from(internal)
77    }
78
79    /// Creates a new builder with the default allocator.
80    ///
81    /// # Errors
82    ///
83    /// For details see [`ArrayConstructionError`].
84    #[inline(always)]
85    pub fn new() -> Result<Self, ArrayConstructionError> {
86        Self::with_allocator(TAllocator::default())
87    }
88
89    /// Creates a new builder with the specified allocator.
90    ///
91    /// # Errors
92    ///
93    /// For details see [`ArrayConstructionError`].
94    #[inline(always)]
95    pub fn with_allocator(allocator: TAllocator) -> Result<Self, ArrayConstructionError> {
96        let internal = InternalArray::allocate(Length::ZERO, INITIAL_CAPACITY, allocator)?;
97        Ok(Self { internal })
98    }
99
100    /// Pushes a new value to the end of the builder.
101    ///
102    /// # Errors
103    ///
104    /// For details see [`ArrayConstructionError`].
105    #[inline(always)]
106    pub fn push(&mut self, value: T) -> Result<(), ArrayConstructionError> {
107        self.extend_from_array([value])
108    }
109
110    /// Extends the builder with the specified values.
111    ///
112    /// # Errors
113    ///
114    /// For details see [`ArrayConstructionError`].
115    pub fn extend_from_array<const N: usize>(&mut self, values: [T; N]) -> Result<(), ArrayConstructionError> {
116        if N == 0 {
117            return Ok(());
118        }
119
120        if N > MAX_LENGTH {
121            return Err(ArrayConstructionError::ArrayTooLong);
122        }
123
124        let internal = &mut self.internal;
125        let internal_len = internal.len().value() as usize;
126        let internal_capacity = internal.capacity().value() as usize;
127        if internal_len + N > internal_capacity {
128            let missing = internal_len + N - internal_capacity;
129            let new_capacity = Self::grow_formula(internal_capacity + missing);
130            internal.grow(new_capacity)?;
131        }
132
133        let heap_data = internal.heap_data_mut();
134        let data_ptr = heap_data.data().as_ptr();
135        unsafe {
136            let end_ptr = data_ptr.add(internal_len);
137            end_ptr.copy_from_nonoverlapping(values.as_ptr(), N);
138        }
139        core::mem::forget(values);
140        *internal.len_mut() += N as i32;
141        Ok(())
142    }
143
144    #[inline(always)]
145    pub const fn len(&self) -> Length {
146        self.internal.len()
147    }
148
149    #[inline(always)]
150    pub const fn capacity(&self) -> Length {
151        self.internal.capacity()
152    }
153}
154
155impl<T: Sized, TAllocator: Allocator> Drop for ImmutableArrayBuilder<T, TAllocator> {
156    fn drop(&mut self) {
157        // We still need drop, in case someone crates builder but does not actually
158        // call `build` method. Note that the `build` method disables drop.
159        let internal = unsafe { core::ptr::read(&self.internal) };
160        let _ = ImmutableWeakArray::from(internal);
161    }
162}
163
164impl<T: Sized + Clone, TAllocator: Allocator> ImmutableArrayBuilder<T, TAllocator> {
165    /// Extends the builder with the specified slice.
166    ///
167    /// # Notes
168    ///
169    /// This method will clone each element one by one.
170    ///
171    /// # Errors
172    ///
173    /// For details see [`ArrayConstructionError`].
174    #[inline(always)]
175    pub fn extend_from_slice(&mut self, slice: &[T]) -> Result<(), ArrayConstructionError> {
176        let slice_len = slice.len();
177        if slice_len == 0 {
178            return Ok(());
179        }
180
181        if slice_len > MAX_LENGTH {
182            return Err(ArrayConstructionError::ArrayTooLong);
183        }
184
185        let internal = &mut self.internal;
186        let internal_len = internal.len().value() as usize;
187        let internal_capacity = internal.capacity().value() as usize;
188        if internal_len + slice_len > internal_capacity {
189            let missing = internal_len + slice_len - internal_capacity;
190            let new_capacity = Self::grow_formula(internal_capacity + missing);
191            internal.grow(new_capacity)?;
192        }
193
194        let heap_data = internal.heap_data_mut();
195        let data_ptr = heap_data.data().as_ptr();
196        unsafe {
197            let mut end_ptr = data_ptr.add(internal_len);
198            for item in slice {
199                end_ptr.write(item.clone());
200                end_ptr = end_ptr.add(1);
201            }
202        }
203        *internal.len_mut() += slice_len as i32;
204        Ok(())
205    }
206}