From b5b80a6edd03728fed6be3b638ce9201dd2e9e7a Mon Sep 17 00:00:00 2001 From: Gabriel Rondon Date: Mon, 23 Mar 2026 23:45:07 +0000 Subject: [PATCH 1/2] feat(vec): add extend() method for batch element insertion Add `BaseVec::extend()` and `Vec::extend()` for appending multiple items in a single operation. Writes all items sequentially and updates the length once, reducing stable memory writes compared to repeated push(). Closes #349 --- src/base_vec.rs | 16 ++++++++++++++++ src/vec.rs | 9 +++++++++ src/vec/tests.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/src/base_vec.rs b/src/base_vec.rs index 78cdb4b6..634c280a 100644 --- a/src/base_vec.rs +++ b/src/base_vec.rs @@ -227,6 +227,22 @@ impl BaseVec { Ok(()) } + /// Adds multiple items at the end of the vector. + /// + /// Complexity: O(n * max_size(T)) + pub fn extend(&self, items: &[T]) -> Result<(), GrowFailed> { + let start_index = self.len(); + let slot = slot_size::() as u64; + for (i, item) in items.iter().enumerate() { + let offset = DATA_OFFSET + slot * (start_index + i as u64); + let bytes = item.to_bytes_checked(); + let data_offset = self.write_entry_size(offset, bytes.len() as u32)?; + safe_write(&self.memory, data_offset, bytes.borrow())?; + } + self.set_len(start_index + items.len() as u64); + Ok(()) + } + /// Removes the item at the end of the vector. /// /// Complexity: O(max_size(T)) diff --git a/src/vec.rs b/src/vec.rs index d2cb984f..cf0b27b4 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -89,6 +89,15 @@ impl Vec { .expect("Failed to push item to the vector"); } + /// Adds multiple items at the end of the vector. + /// + /// Complexity: O(n * max_size(T)) + pub fn extend(&self, items: &[T]) { + self.0 + .extend(items) + .expect("Failed to extend the vector"); + } + /// Removes the item at the end of the vector. /// /// Complexity: O(max_size(T)) diff --git a/src/vec/tests.rs b/src/vec/tests.rs index cb6e676f..c53d5f63 100644 --- a/src/vec/tests.rs +++ b/src/vec/tests.rs @@ -350,3 +350,45 @@ fn set_last_element_to_large_blob() { // Store a large blob that would require growing the memory. sv.set(0, &Blob::try_from(vec![1; 65536].as_slice()).unwrap()); } + +#[test] +fn extend_empty_slice() { + let sv = StableVec::::new(M::default()); + sv.extend(&[]); + assert_eq!(sv.len(), 0); + assert!(sv.is_empty()); +} + +#[test] +fn extend_basic() { + let sv = StableVec::::new(M::default()); + sv.extend(&[1, 2, 3]); + assert_eq!(sv.len(), 3); + assert_eq!(sv.to_vec(), vec![1, 2, 3]); +} + +#[test] +fn extend_after_push() { + let sv = StableVec::::new(M::default()); + sv.push(&0); + sv.extend(&[1, 2, 3]); + assert_eq!(sv.len(), 4); + assert_eq!(sv.to_vec(), vec![0, 1, 2, 3]); +} + +#[test] +fn extend_multiple_calls() { + let sv = StableVec::::new(M::default()); + sv.extend(&[1, 2]); + sv.extend(&[3, 4]); + assert_eq!(sv.to_vec(), vec![1, 2, 3, 4]); +} + +#[test] +fn extend_persistence() { + let sv = StableVec::::new(M::default()); + sv.extend(&[10, 20, 30]); + let mem = sv.into_memory(); + let sv = StableVec::::init(mem); + assert_eq!(sv.to_vec(), vec![10, 20, 30]); +} From d0d32dc6d8f713e2e9e40ba7f775471b00d7727a Mon Sep 17 00:00:00 2001 From: Gabriel Rondon Date: Tue, 24 Mar 2026 10:16:47 +0000 Subject: [PATCH 2/2] chore: retrigger CI after CLA signing