From 2dff55384097e36b02911fd393ce90e94bc5739c Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Mon, 26 May 2025 08:57:58 +0300 Subject: [PATCH 1/4] Implement extend_from_slice --- src/lib.rs | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e5cc0b8..e6790d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -585,6 +585,33 @@ impl Vec { unsafe { slice::from_raw_parts_mut(self.data[0].as_mut_ptr(), self.len()) } } + /// Adds elements of given slice to the vector. + /// + /// # Arguments + /// + /// - `slice` - The source of elements to add. + /// + /// # Errors + /// + /// Returns [`CapacityError`] if adding elements of given slice would result in vector exceeding its capacity. + #[inline] + pub fn extend_from_slice(&mut self, slice: &[T]) -> Result<(), CapacityError> + where + T: Clone, + { + if self.len() + slice.len() > CAPACITY { + return Err(CapacityError); + } + + for (index, value) in slice.iter().enumerate() { + self.data[self.len() + index].write(value.clone()); + } + + self.length += slice.len(); + + Ok(()) + } + /// Drops all elements in given range. Needed when elements are considered to be going out of scope. /// E.g.: when the vector is going out of scope, when methods such as [`Vec::clear()`] and [`Vec::set_len()`] are called. fn drop_range(&mut self, from: usize, to: usize) { @@ -988,6 +1015,95 @@ mod tests { assert_eq!(vec.as_slice().iter().sum::(), 2000); } + #[test] + fn extend_from_slice_with_empty_vector_and_empty_slice() { + let src = []; + let mut dst = Vec::::new(); + let result = dst.extend_from_slice(&src); + + assert!(result.is_ok()); + assert!(dst.is_empty()); + } + + #[test] + fn extend_from_slice_with_empty_vector_and_non_empty_slice_within_capacity() { + let src = [1, 2]; + let mut dst = Vec::::new(); + let result = dst.extend_from_slice(&src); + + assert!(result.is_ok()); + assert_eq!(dst.len(), 2); + assert_eq!(dst.as_slice(), [1, 2]); + } + + #[test] + fn extend_from_slice_with_non_empty_vector_and_empty_slice() { + let src = []; + let mut dst = Vec::::new(); + dst.push(1).unwrap(); + dst.push(2).unwrap(); + let result = dst.extend_from_slice(&src); + + assert!(result.is_ok()); + assert_eq!(dst.len(), 2); + assert_eq!(dst.as_slice(), [1, 2]); + } + + #[test] + fn extend_from_slice_with_non_empty_vector_and_slice_fits_exactly_into_capacity() { + let src = [3, 4, 5]; + let mut dst = Vec::::new(); + dst.push(1).unwrap(); + dst.push(2).unwrap(); + let result = dst.extend_from_slice(&src); + + assert!(result.is_ok()); + assert_eq!(dst.len(), 5); + assert!(dst.is_full()); + assert_eq!(dst.as_slice(), [1, 2, 3, 4, 5]); + } + + #[test] + fn extend_from_slice_with_non_empty_vector_and_slice_exceeds_capacity() { + let src = [3, 4, 5, 6]; + let mut dst = Vec::::new(); + dst.push(1).unwrap(); + dst.push(2).unwrap(); + let result = dst.extend_from_slice(&src); + + assert!(result.is_err()); + assert_eq!(dst.len(), 2); + assert_eq!(dst.as_slice(), [1, 2]); + } + + #[test] + fn extend_from_slice_with_vector_full_and_non_empty_slice() { + let src = [3, 4, 5, 6]; + let mut dst = Vec::::new(); + dst.push(1).unwrap(); + dst.push(2).unwrap(); + let result = dst.extend_from_slice(&src); + + assert!(result.is_err()); + assert_eq!(dst.len(), 2); + assert!(dst.is_full()); + assert_eq!(dst.as_slice(), [1, 2]); + } + + #[test] + fn extend_from_slice_with_non_empty_vector_and_non_empty_slice() { + let src = [3]; + let mut dst = Vec::::new(); + dst.push(1).unwrap(); + dst.push(2).unwrap(); + let result = dst.extend_from_slice(&src); + + assert!(result.is_ok()); + assert_eq!(dst.len(), 3); + assert!(!dst.is_full()); + assert_eq!(dst.as_slice(), [1, 2, 3]); + } + #[test] fn construct_should_not_create_default_elements() { let _ = Vec::::new(); From d270b04eec04c757fd898c7b885add89b4052012 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Mon, 26 May 2025 09:20:47 +0300 Subject: [PATCH 2/4] Add example --- src/lib.rs | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e6790d9..cb528cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -585,15 +585,44 @@ impl Vec { unsafe { slice::from_raw_parts_mut(self.data[0].as_mut_ptr(), self.len()) } } - /// Adds elements of given slice to the vector. - /// - /// # Arguments - /// - /// - `slice` - The source of elements to add. + /// Inserts elements of given slice at the end of the vector. /// /// # Errors /// /// Returns [`CapacityError`] if adding elements of given slice would result in vector exceeding its capacity. + /// + /// # Example + /// + /// ```rust + /// use static_vector::{CapacityError, Vec}; + /// + /// #[derive(Debug)] + /// enum AppError { + /// VectorCapacityError(CapacityError), + /// } + /// + /// fn my_fn(src: &[i32], vec: &mut Vec) -> Result<(), AppError> { + /// vec.extend_from_slice(src).map_err(AppError::VectorCapacityError)?; + /// + /// // other operations that could return errors + /// Ok(()) + /// } + /// + /// fn main() -> Result<(), AppError> { + /// let src = [1, 2, 3]; + /// let mut vec = Vec::::new(); + /// + /// if let Err(err) = my_fn(&src, &mut vec) { + /// match err { + /// AppError::VectorCapacityError(_) => { + /// // handle case + /// }, + /// } + /// } + /// + /// Ok(()) + /// } + /// ``` #[inline] pub fn extend_from_slice(&mut self, slice: &[T]) -> Result<(), CapacityError> where From d97d0fa9692d890f1a04f0c80d7979c22c769939 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Mon, 26 May 2025 09:36:05 +0300 Subject: [PATCH 3/4] Add extend_from_slice to fuzz test --- fuzz/Cargo.lock | 2 +- fuzz/fuzz_targets/static_vector.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 8262c14..ec08a46 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -83,7 +83,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "static_vector" -version = "0.2.0" +version = "0.3.0" [[package]] name = "static_vector-fuzz" diff --git a/fuzz/fuzz_targets/static_vector.rs b/fuzz/fuzz_targets/static_vector.rs index 882094a..d6a18fa 100644 --- a/fuzz/fuzz_targets/static_vector.rs +++ b/fuzz/fuzz_targets/static_vector.rs @@ -44,6 +44,7 @@ fuzz_target!(|data: &[u8]| { if is_full_after_push { assert!(vec.is_full()); + assert!(vec.extend_from_slice(&[0]).is_err()); } else { assert!(!vec.is_full()); } @@ -53,6 +54,13 @@ fuzz_target!(|data: &[u8]| { assert_eq!(vec.get_mut(i).unwrap(), &byte); prev_byte = Some(byte); + + if vec.len() + 1 > vec.capacity() { + assert!(vec.extend_from_slice(&[0]).is_err()); + } else { + assert!(vec.extend_from_slice(&[0]).is_ok()); + vec.pop().unwrap(); + } } } From 28d6b054bfad243d1232ca6ddd51edf0b07495cb Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Mon, 26 May 2025 09:45:05 +0300 Subject: [PATCH 4/4] Add extend_from_slice complexity --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1caba4a..f382836 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,11 @@ The goal is to allocate only when needed. When first constructed, the vector wil All operations are O(1) except: -| Method | Time Complexity | Space Complexity | -|-------------|----------------------------------|---------------------------------| -| `clear` | O(current length) | O(1) | -| `set_len` | O(new length - current length) | O(new length - current length) | +| Method | Time Complexity | Space Complexity | +|-----------------------|----------------------------------|---------------------------------| +| `clear` | O(current length) | O(1) | +| `set_len` | O(new length - current length) | O(new length - current length) | +| `extend_from_slice` | O(slice length) | O(slice length) | ## Add to project