From 966a0bc60409fe698d9aa23d814ab66308f6f4f6 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 23 Jun 2025 09:53:03 +0200 Subject: [PATCH 01/26] use write instead of safe_write --- src/base_vec.rs | 26 ++++++++++++-------------- src/memory_manager.rs | 13 ++++++------- src/reader/tests.rs | 8 ++++---- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/base_vec.rs b/src/base_vec.rs index 707cb303..6a633c90 100644 --- a/src/base_vec.rs +++ b/src/base_vec.rs @@ -201,16 +201,15 @@ impl BaseVec { /// Adds a new item at the end of the vector. /// /// Complexity: O(max_size(T)) - pub fn push(&self, item: &T) -> Result<(), GrowFailed> { + pub fn push(&self, item: &T) { let index = self.len(); let offset = DATA_OFFSET + slot_size::() as u64 * index; 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())?; + write(&self.memory, data_offset, bytes.borrow()); // NB. We update the size only after we ensure that the data // write succeeded. self.set_len(index + 1); - Ok(()) } /// Removes the item at the end of the vector. @@ -257,21 +256,21 @@ impl BaseVec { } /// Writes the size of the item at the specified offset. - fn write_entry_size(&self, offset: u64, size: u32) -> Result { + fn write_entry_size(&self, offset: u64, size: u32) -> u64 { let t_bounds = bounds::(); debug_assert!(size <= t_bounds.max_size); if t_bounds.is_fixed_size { - Ok(offset) + offset } else if t_bounds.max_size <= u8::MAX as u32 { - safe_write(&self.memory, offset, &[size as u8; 1])?; - Ok(offset + 1) + write(&self.memory, offset, &[size as u8; 1]); + offset + 1 } else if t_bounds.max_size <= u16::MAX as u32 { - safe_write(&self.memory, offset, &(size as u16).to_le_bytes())?; - Ok(offset + 2) + write(&self.memory, offset, &(size as u16).to_le_bytes()); + offset + 2 } else { - safe_write(&self.memory, offset, &size.to_le_bytes())?; - Ok(offset + 4) + write(&self.memory, offset, &size.to_le_bytes()); + offset + 4 } } @@ -295,13 +294,12 @@ impl BaseVec { } /// Write the layout header to the memory. - fn write_header(header: &HeaderV1, memory: &M) -> Result<(), GrowFailed> { - safe_write(memory, 0, &header.magic)?; + fn write_header(header: &HeaderV1, memory: &M) { + write(memory, 0, &header.magic); memory.write(3, &[header.version; 1]); write_u64(memory, Address::from(4), header.len); write_u32(memory, Address::from(12), header.max_size); memory.write(16, &[if header.is_fixed_size { 1u8 } else { 0u8 }; 1]); - Ok(()) } /// Reads the header from the specified memory. diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 07a2ecbf..829457c3 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -1010,16 +1010,15 @@ mod test { // There are 5 operations for _ in 0..MEMORIES * 5 { - safe_write(&next_memory(), 0, data.as_slice()).unwrap(); - safe_write(&next_memory(), WASM_PAGE_SIZE / 2, data.as_slice()).unwrap(); - safe_write(&next_memory(), WASM_PAGE_SIZE - 1, data.as_slice()).unwrap(); - safe_write(&next_memory(), WASM_PAGE_SIZE + 1, data.as_slice()).unwrap(); - safe_write( + write(&next_memory(), 0, data.as_slice()); + write(&next_memory(), WASM_PAGE_SIZE / 2, data.as_slice()); + write(&next_memory(), WASM_PAGE_SIZE - 1, data.as_slice()); + write(&next_memory(), WASM_PAGE_SIZE + 1, data.as_slice()); + write( &next_memory(), 2 * WASM_PAGE_SIZE + WASM_PAGE_SIZE / 2, data.as_slice(), - ) - .unwrap(); + ); } let expected_write = include_bytes!("memory_manager/stability_write.golden"); diff --git a/src/reader/tests.rs b/src/reader/tests.rs index 61a63f81..62f9eab2 100644 --- a/src/reader/tests.rs +++ b/src/reader/tests.rs @@ -1,5 +1,5 @@ use crate::reader::{BufferedReader, Reader}; -use crate::{safe_write, Memory, VectorMemory, WASM_PAGE_SIZE}; +use crate::{write, Memory, VectorMemory, WASM_PAGE_SIZE}; use proptest::proptest; use std::io::Read; @@ -12,7 +12,7 @@ proptest! { len in 0..1024usize ) { let memory = VectorMemory::default(); - safe_write(&memory, 0, &bytes).unwrap(); + write(&memory, 0, &bytes); let mut reader = build_reader(&memory, buffer_size, offset); let mut output = vec![0u8; len]; @@ -30,7 +30,7 @@ proptest! { repetitions in 2..8 ) { let memory = VectorMemory::default(); - safe_write(&memory, 0, &bytes).unwrap(); + write(&memory, 0, &bytes); // use unbuffered reader, because buffered read might return fewer bytes depending on buffer state let mut reader = Reader::new(&memory, offset); @@ -53,7 +53,7 @@ proptest! { offset in 0..512u64, ) { let memory = VectorMemory::default(); - safe_write(&memory, 0, &bytes).unwrap(); + write(&memory, 0, &bytes); let mut reader = build_reader(&memory, buffer_size, offset); let mut output = vec![]; From 36eced2f4d6922baa57bf6995348e499bb88cc2c Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 23 Jun 2025 10:01:51 +0200 Subject: [PATCH 02/26] . --- src/base_vec.rs | 17 +++++++---------- src/memory_manager.rs | 1 - src/min_heap.rs | 5 ++--- src/min_heap/tests.rs | 12 ++++++------ src/vec.rs | 8 ++++---- src/vec/tests.rs | 42 +++++++++++++++++++++--------------------- 6 files changed, 40 insertions(+), 45 deletions(-) diff --git a/src/base_vec.rs b/src/base_vec.rs index 6a633c90..1c5be5ed 100644 --- a/src/base_vec.rs +++ b/src/base_vec.rs @@ -33,8 +33,7 @@ //! bytes required to represent integers up to that max size. use crate::storable::{bounds, bytes_to_store_size_bounded}; use crate::{ - read_to_vec, read_u32, read_u64, safe_write, write, write_u32, write_u64, Address, GrowFailed, - Memory, Storable, + read_to_vec, read_u32, read_u64, write, write_u32, write_u64, Address, Memory, Storable, }; use std::borrow::{Borrow, Cow}; use std::cmp::min; @@ -104,7 +103,7 @@ impl BaseVec { /// contained previously. /// /// Complexity: O(1) - pub fn new(memory: M, magic: [u8; 3]) -> Result { + pub fn new(memory: M, magic: [u8; 3]) -> Self { let t_bounds = bounds::(); let header = HeaderV1 { @@ -114,11 +113,11 @@ impl BaseVec { max_size: t_bounds.max_size, is_fixed_size: t_bounds.is_fixed_size, }; - Self::write_header(&header, &memory)?; - Ok(Self { + Self::write_header(&header, &memory); + Self { memory, _marker: PhantomData, - }) + } } /// Initializes a vector in the specified memory. @@ -181,9 +180,7 @@ impl BaseVec { let offset = DATA_OFFSET + slot_size::() as u64 * index; let bytes = item.to_bytes_checked(); - let data_offset = self - .write_entry_size(offset, bytes.len() as u32) - .expect("unreachable: cannot fail to write to pre-allocated area"); + let data_offset = self.write_entry_size(offset, bytes.len() as u32); write(&self.memory, data_offset, bytes.borrow()); } @@ -205,7 +202,7 @@ impl BaseVec { let index = self.len(); let offset = DATA_OFFSET + slot_size::() as u64 * index; let bytes = item.to_bytes_checked(); - let data_offset = self.write_entry_size(offset, bytes.len() as u32)?; + let data_offset = self.write_entry_size(offset, bytes.len() as u32); write(&self.memory, data_offset, bytes.borrow()); // NB. We update the size only after we ensure that the data // write succeeded. diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 829457c3..c7a212f8 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -644,7 +644,6 @@ impl BucketCache { #[cfg(test)] mod test { use super::*; - use crate::safe_write; use proptest::prelude::*; const MAX_MEMORY_IN_PAGES: u64 = MAX_NUM_BUCKETS * BUCKET_SIZE_IN_PAGES; diff --git a/src/min_heap.rs b/src/min_heap.rs index ecd50d15..a9bff53d 100644 --- a/src/min_heap.rs +++ b/src/min_heap.rs @@ -61,11 +61,10 @@ where /// Pushes an item onto the heap. /// /// Complexity: O(log(self.len())) - pub fn push(&mut self, item: &T) -> Result<(), GrowFailed> { - self.0.push(item)?; + pub fn push(&mut self, item: &T) { + self.0.push(item); self.bubble_up(self.0.len() - 1, item); debug_assert_eq!(Ok(()), self.check_invariant()); - Ok(()) } /// Removes the smallest item from the heap and returns it. diff --git a/src/min_heap/tests.rs b/src/min_heap/tests.rs index a366d519..53721386 100644 --- a/src/min_heap/tests.rs +++ b/src/min_heap/tests.rs @@ -29,7 +29,7 @@ proptest! { for op in ops { match op { Operation::Push(x) => { - sh.push(&x).unwrap(); + sh.push(&x); h.push(Reverse(x)); } Operation::Pop => { @@ -43,7 +43,7 @@ proptest! { fn pop_sorted(mut items in pvec(any::(), 0..50)) { let mut sh = StableMinHeap::::new(M::default()).unwrap(); for x in &items { - sh.push(x).unwrap(); + sh.push(x); } items.sort(); for x in items { @@ -56,10 +56,10 @@ proptest! { fn test_simple_case() { let mut h = StableMinHeap::::new(M::default()).unwrap(); assert_eq!(h.pop(), None); - h.push(&0).unwrap(); - h.push(&3).unwrap(); - h.push(&0).unwrap(); - h.push(&1).unwrap(); + h.push(&0); + h.push(&3); + h.push(&0); + h.push(&1); assert_eq!(h.pop(), Some(0)); assert_eq!(h.pop(), Some(0)); assert_eq!(h.pop(), Some(1)); diff --git a/src/vec.rs b/src/vec.rs index 7e353a7c..757a9435 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -3,7 +3,7 @@ use crate::base_vec::BaseVec; pub use crate::base_vec::InitError; use crate::storable::Storable; -use crate::{GrowFailed, Memory}; +use crate::Memory; use std::fmt; #[cfg(test)] @@ -20,8 +20,8 @@ impl Vec { /// contained previously. /// /// Complexity: O(1) - pub fn new(memory: M) -> Result { - BaseVec::::new(memory, MAGIC).map(Self) + pub fn new(memory: M) -> Self { + BaseVec::::new(memory, MAGIC) } /// Initializes a vector in the specified memory. @@ -72,7 +72,7 @@ impl Vec { /// Adds a new item at the end of the vector. /// /// Complexity: O(max_size(T)) - pub fn push(&self, item: &T) -> Result<(), GrowFailed> { + pub fn push(&self, item: &T) { self.0.push(item) } diff --git a/src/vec/tests.rs b/src/vec/tests.rs index b4c1bd2d..76ec7a05 100644 --- a/src/vec/tests.rs +++ b/src/vec/tests.rs @@ -72,9 +72,9 @@ proptest! { #[test] fn init_after_new(vals in pvec(any::(), 0..20)) { - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); for v in vals { - sv.push(&v).unwrap(); + sv.push(&v); } let vec = sv.to_vec(); prop_assert_eq!(StableVec::::init(sv.into_memory()).unwrap().to_vec(), vec); @@ -85,12 +85,12 @@ fn check_push_pop_model( ops: Vec>, ) -> Result<(), TestCaseError> { let mut v = Vec::new(); - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); for op in ops { match op { Operation::Push(x) => { - sv.push(&x).unwrap(); + sv.push(&x); v.push(x); prop_assert_eq!(&sv.to_vec(), &v); } @@ -105,14 +105,14 @@ fn check_push_pop_model( #[test] fn test_init_type_compatibility() { - let v = StableVec::::new(M::default()).unwrap(); + let v = StableVec::::new(M::default()); assert_eq!( StableVec::::init(v.into_memory()).unwrap_err(), InitError::IncompatibleElementType ); - let v = StableVec::::new(M::default()).unwrap(); + let v = StableVec::::new(M::default()); assert_eq!( StableVec::, M>::init(v.into_memory()).unwrap_err(), InitError::IncompatibleElementType @@ -173,11 +173,11 @@ fn test_init_failures() { #[allow(clippy::iter_nth_zero)] #[test] fn test_iter() { - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); assert_eq!(sv.iter().next(), None); - sv.push(&1).unwrap(); - sv.push(&2).unwrap(); - sv.push(&3).unwrap(); + sv.push(&1); + sv.push(&2); + sv.push(&3); let mut iter = sv.iter(); assert_eq!(iter.size_hint(), (3, None)); @@ -235,11 +235,11 @@ fn test_iter() { #[test] fn test_iter_count() { - let sv = StableVec::::new(M::default()).unwrap(); - sv.push(&1).unwrap(); - sv.push(&2).unwrap(); - sv.push(&3).unwrap(); - sv.push(&4).unwrap(); + let sv = StableVec::::new(M::default()); + sv.push(&1); + sv.push(&2); + sv.push(&3); + sv.push(&4); { let mut iter = sv.iter(); iter.next_back(); @@ -280,17 +280,17 @@ impl crate::Storable for BuggyStruct { #[test] #[should_panic(expected = "expected an element with length <= 1 bytes, but found 4")] fn push_element_bigger_than_max_size_panics() { - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); // Insert a struct where the serialized size is > `MAX_SIZE`. Should panic. - sv.push(&BuggyStruct(vec![1, 2, 3, 4])).unwrap(); + sv.push(&BuggyStruct(vec![1, 2, 3, 4])); } #[test] #[should_panic(expected = "expected an element with length <= 1 bytes, but found 5")] fn set_element_bigger_than_max_size_panics() { - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); // Insert a struct where the serialized size is <= `MAX_SIZE`. This should succeed. - sv.push(&BuggyStruct(vec![1])).unwrap(); + sv.push(&BuggyStruct(vec![1])); // Insert a struct where the serialized size is > `MAX_SIZE`. Should panic. sv.set(0, &BuggyStruct(vec![1, 2, 3, 4, 5])); @@ -299,10 +299,10 @@ fn set_element_bigger_than_max_size_panics() { #[test] fn set_last_element_to_large_blob() { use crate::storable::Blob; - let sv = StableVec::, M>::new(M::default()).unwrap(); + let sv = StableVec::, M>::new(M::default()); // Store a small blob. - sv.push(&Blob::default()).unwrap(); + sv.push(&Blob::default()); // Store a large blob that would require growing the memory. sv.set(0, &Blob::try_from(vec![1; 65536].as_slice()).unwrap()); From e9abb8380a32f625c457e72c53ce23c968b09ee2 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 23 Jun 2025 11:14:11 +0200 Subject: [PATCH 03/26] . --- src/base_vec.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/base_vec.rs b/src/base_vec.rs index 1c5be5ed..27f9cf10 100644 --- a/src/base_vec.rs +++ b/src/base_vec.rs @@ -33,7 +33,8 @@ //! bytes required to represent integers up to that max size. use crate::storable::{bounds, bytes_to_store_size_bounded}; use crate::{ - read_to_vec, read_u32, read_u64, write, write_u32, write_u64, Address, Memory, Storable, + read_to_vec, read_u32, read_u64, safe_write, write, write_u32, write_u64, Address, GrowFailed, + Memory, Storable, }; use std::borrow::{Borrow, Cow}; use std::cmp::min; @@ -104,6 +105,12 @@ impl BaseVec { /// /// Complexity: O(1) pub fn new(memory: M, magic: [u8; 3]) -> Self { + Self::safe_new(memory, magic) + .map_err(|e| panic!("Failed to create a new base stable vector: {}", e)) + .unwrap() + } + + fn safe_new(memory: M, magic: [u8; 3]) -> Result { let t_bounds = bounds::(); let header = HeaderV1 { @@ -114,10 +121,10 @@ impl BaseVec { is_fixed_size: t_bounds.is_fixed_size, }; Self::write_header(&header, &memory); - Self { + Ok(Self { memory, _marker: PhantomData, - } + }) } /// Initializes a vector in the specified memory. @@ -128,7 +135,7 @@ impl BaseVec { /// stable vector. pub fn init(memory: M, magic: [u8; 3]) -> Result { if memory.size() == 0 { - return Self::new(memory, magic).map_err(|_| InitError::OutOfMemory); + return Self::safe_new(memory, magic).map_err(|_| InitError::OutOfMemory); } let header = Self::read_header(&memory); if header.magic != magic { @@ -291,8 +298,8 @@ impl BaseVec { } /// Write the layout header to the memory. - fn write_header(header: &HeaderV1, memory: &M) { - write(memory, 0, &header.magic); + fn write_header(header: &HeaderV1, memory: &M) -> Result<(), GrowFailed> { + safe_write(memory, 0, &header.magic)?; memory.write(3, &[header.version; 1]); write_u64(memory, Address::from(4), header.len); write_u32(memory, Address::from(12), header.max_size); From 28cca8d7adfc44fa369d5a0ecff9c9fa5f196c10 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Mon, 23 Jun 2025 11:14:44 +0200 Subject: [PATCH 04/26] . --- src/base_vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base_vec.rs b/src/base_vec.rs index 27f9cf10..ded3a191 100644 --- a/src/base_vec.rs +++ b/src/base_vec.rs @@ -120,7 +120,7 @@ impl BaseVec { max_size: t_bounds.max_size, is_fixed_size: t_bounds.is_fixed_size, }; - Self::write_header(&header, &memory); + Self::write_header(&header, &memory)?; Ok(Self { memory, _marker: PhantomData, From 24f1a3710437e1f4c087cb4db23bf9eaca3736ed Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 24 Jun 2025 11:40:24 +0200 Subject: [PATCH 05/26] . --- src/base_vec.rs | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/base_vec.rs b/src/base_vec.rs index ded3a191..b3d68b2b 100644 --- a/src/base_vec.rs +++ b/src/base_vec.rs @@ -187,7 +187,12 @@ impl BaseVec { let offset = DATA_OFFSET + slot_size::() as u64 * index; let bytes = item.to_bytes_checked(); - let data_offset = self.write_entry_size(offset, bytes.len() as u32); + let data_offset = self + .write_entry_size(offset, bytes.len() as u32) + .map_err(|e| { + panic!("Failed to write to the stable vector: {}", e); + }) + .unwrap(); write(&self.memory, data_offset, bytes.borrow()); } @@ -209,8 +214,15 @@ impl BaseVec { let index = self.len(); let offset = DATA_OFFSET + slot_size::() as u64 * index; let bytes = item.to_bytes_checked(); - let data_offset = self.write_entry_size(offset, bytes.len() as u32); - write(&self.memory, data_offset, bytes.borrow()); + let data_offset = self + .write_entry_size(offset, bytes.len() as u32) + .map_err(|e| { + panic!("Failed to write to the stable vector: {}", e); + }) + .unwrap(); + safe_write(&self.memory, data_offset, bytes.borrow()).map_err(|e| { + panic!("Failed to write to the stable vector: {}", e); + }); // NB. We update the size only after we ensure that the data // write succeeded. self.set_len(index + 1); @@ -260,21 +272,21 @@ impl BaseVec { } /// Writes the size of the item at the specified offset. - fn write_entry_size(&self, offset: u64, size: u32) -> u64 { + fn write_entry_size(&self, offset: u64, size: u32) -> Result { let t_bounds = bounds::(); debug_assert!(size <= t_bounds.max_size); if t_bounds.is_fixed_size { - offset + Ok(offset) } else if t_bounds.max_size <= u8::MAX as u32 { - write(&self.memory, offset, &[size as u8; 1]); - offset + 1 + safe_write(&self.memory, offset, &[size as u8; 1])?; + Ok(offset + 1) } else if t_bounds.max_size <= u16::MAX as u32 { - write(&self.memory, offset, &(size as u16).to_le_bytes()); - offset + 2 + safe_write(&self.memory, offset, &(size as u16).to_le_bytes())?; + Ok(offset + 2) } else { - write(&self.memory, offset, &size.to_le_bytes()); - offset + 4 + safe_write(&self.memory, offset, &size.to_le_bytes())?; + Ok(offset + 4) } } From 760e03e66c553bad6832a6ba633fbb272c8e149a Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 24 Jun 2025 11:41:12 +0200 Subject: [PATCH 06/26] . --- src/base_vec.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/base_vec.rs b/src/base_vec.rs index b3d68b2b..eb307ce7 100644 --- a/src/base_vec.rs +++ b/src/base_vec.rs @@ -316,6 +316,7 @@ impl BaseVec { write_u64(memory, Address::from(4), header.len); write_u32(memory, Address::from(12), header.max_size); memory.write(16, &[if header.is_fixed_size { 1u8 } else { 0u8 }; 1]); + Ok(()) } /// Reads the header from the specified memory. From a0c589a52051762a4d93c62df5f2ff17e64ad98b Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 24 Jun 2025 11:47:19 +0200 Subject: [PATCH 07/26] . --- src/base_vec.rs | 29 +++++++---------------------- src/vec/tests.rs | 2 +- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/base_vec.rs b/src/base_vec.rs index eb307ce7..707cb303 100644 --- a/src/base_vec.rs +++ b/src/base_vec.rs @@ -104,13 +104,7 @@ impl BaseVec { /// contained previously. /// /// Complexity: O(1) - pub fn new(memory: M, magic: [u8; 3]) -> Self { - Self::safe_new(memory, magic) - .map_err(|e| panic!("Failed to create a new base stable vector: {}", e)) - .unwrap() - } - - fn safe_new(memory: M, magic: [u8; 3]) -> Result { + pub fn new(memory: M, magic: [u8; 3]) -> Result { let t_bounds = bounds::(); let header = HeaderV1 { @@ -135,7 +129,7 @@ impl BaseVec { /// stable vector. pub fn init(memory: M, magic: [u8; 3]) -> Result { if memory.size() == 0 { - return Self::safe_new(memory, magic).map_err(|_| InitError::OutOfMemory); + return Self::new(memory, magic).map_err(|_| InitError::OutOfMemory); } let header = Self::read_header(&memory); if header.magic != magic { @@ -189,10 +183,7 @@ impl BaseVec { let bytes = item.to_bytes_checked(); let data_offset = self .write_entry_size(offset, bytes.len() as u32) - .map_err(|e| { - panic!("Failed to write to the stable vector: {}", e); - }) - .unwrap(); + .expect("unreachable: cannot fail to write to pre-allocated area"); write(&self.memory, data_offset, bytes.borrow()); } @@ -210,22 +201,16 @@ impl BaseVec { /// Adds a new item at the end of the vector. /// /// Complexity: O(max_size(T)) - pub fn push(&self, item: &T) { + pub fn push(&self, item: &T) -> Result<(), GrowFailed> { let index = self.len(); let offset = DATA_OFFSET + slot_size::() as u64 * index; let bytes = item.to_bytes_checked(); - let data_offset = self - .write_entry_size(offset, bytes.len() as u32) - .map_err(|e| { - panic!("Failed to write to the stable vector: {}", e); - }) - .unwrap(); - safe_write(&self.memory, data_offset, bytes.borrow()).map_err(|e| { - panic!("Failed to write to the stable vector: {}", e); - }); + let data_offset = self.write_entry_size(offset, bytes.len() as u32)?; + safe_write(&self.memory, data_offset, bytes.borrow())?; // NB. We update the size only after we ensure that the data // write succeeded. self.set_len(index + 1); + Ok(()) } /// Removes the item at the end of the vector. diff --git a/src/vec/tests.rs b/src/vec/tests.rs index fee2f282..09382f7f 100644 --- a/src/vec/tests.rs +++ b/src/vec/tests.rs @@ -138,7 +138,7 @@ fn test_init_failures() { } assert_eq!( - StableVec::::new(EmptyMem).unwrap_err(), + StableVec::::new(EmptyMem), GrowFailed { current_size: 0, delta: 1 From 9046736823ea3e7b54703a8f69dc02f4a4973d7d Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 24 Jun 2025 13:20:27 +0200 Subject: [PATCH 08/26] . --- src/memory_manager.rs | 14 ++++++++------ src/vec.rs | 4 ++++ src/vec/tests.rs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/memory_manager.rs b/src/memory_manager.rs index c7a212f8..07a2ecbf 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -644,6 +644,7 @@ impl BucketCache { #[cfg(test)] mod test { use super::*; + use crate::safe_write; use proptest::prelude::*; const MAX_MEMORY_IN_PAGES: u64 = MAX_NUM_BUCKETS * BUCKET_SIZE_IN_PAGES; @@ -1009,15 +1010,16 @@ mod test { // There are 5 operations for _ in 0..MEMORIES * 5 { - write(&next_memory(), 0, data.as_slice()); - write(&next_memory(), WASM_PAGE_SIZE / 2, data.as_slice()); - write(&next_memory(), WASM_PAGE_SIZE - 1, data.as_slice()); - write(&next_memory(), WASM_PAGE_SIZE + 1, data.as_slice()); - write( + safe_write(&next_memory(), 0, data.as_slice()).unwrap(); + safe_write(&next_memory(), WASM_PAGE_SIZE / 2, data.as_slice()).unwrap(); + safe_write(&next_memory(), WASM_PAGE_SIZE - 1, data.as_slice()).unwrap(); + safe_write(&next_memory(), WASM_PAGE_SIZE + 1, data.as_slice()).unwrap(); + safe_write( &next_memory(), 2 * WASM_PAGE_SIZE + WASM_PAGE_SIZE / 2, data.as_slice(), - ); + ) + .unwrap(); } let expected_write = include_bytes!("memory_manager/stability_write.golden"); diff --git a/src/vec.rs b/src/vec.rs index 757a9435..0f164284 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -4,6 +4,7 @@ use crate::base_vec::BaseVec; pub use crate::base_vec::InitError; use crate::storable::Storable; use crate::Memory; +use core::panic; use std::fmt; #[cfg(test)] @@ -22,6 +23,9 @@ impl Vec { /// Complexity: O(1) pub fn new(memory: M) -> Self { BaseVec::::new(memory, MAGIC) + .map(Self) + .map_err(|e| panic!("{:?}", e)) + .unwrap() } /// Initializes a vector in the specified memory. diff --git a/src/vec/tests.rs b/src/vec/tests.rs index 09382f7f..fee2f282 100644 --- a/src/vec/tests.rs +++ b/src/vec/tests.rs @@ -138,7 +138,7 @@ fn test_init_failures() { } assert_eq!( - StableVec::::new(EmptyMem), + StableVec::::new(EmptyMem).unwrap_err(), GrowFailed { current_size: 0, delta: 1 From 3bb1947640b7b6bf20f663d64de4be72c595c4fa Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 24 Jun 2025 13:22:43 +0200 Subject: [PATCH 09/26] . --- src/min_heap.rs | 2 +- src/vec.rs | 2 +- tests/api_confomrance.rs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/min_heap.rs b/src/min_heap.rs index a9bff53d..6000b91a 100644 --- a/src/min_heap.rs +++ b/src/min_heap.rs @@ -62,7 +62,7 @@ where /// /// Complexity: O(log(self.len())) pub fn push(&mut self, item: &T) { - self.0.push(item); + self.0.push(item).map_err(|e| panic!("{:?}", e)).unwrap(); self.bubble_up(self.0.len() - 1, item); debug_assert_eq!(Ok(()), self.check_invariant()); } diff --git a/src/vec.rs b/src/vec.rs index 0f164284..41c82c4a 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -77,7 +77,7 @@ impl Vec { /// /// Complexity: O(max_size(T)) pub fn push(&self, item: &T) { - self.0.push(item) + self.0.push(item).map_err(|e| panic!("{:?}", e)).unwrap(); } /// Removes the item at the end of the vector. diff --git a/tests/api_confomrance.rs b/tests/api_confomrance.rs index 7b5cb568..78ffdc3d 100644 --- a/tests/api_confomrance.rs +++ b/tests/api_confomrance.rs @@ -246,7 +246,7 @@ fn api_conformance_min_heap() { // Push elements. for i in 0..n { - stable.push(&i).expect("push failed"); + stable.push(&i); std.push(Reverse(i)); } @@ -279,13 +279,13 @@ fn api_conformance_min_heap() { #[test] fn api_conformance_vec() { let mem = make_memory(); - let stable = StableVec::new(mem).unwrap(); + let stable = StableVec::new(mem); let mut std = Vec::new(); let n = 10_u32; // Push elements. for i in 0..n { - stable.push(&i).expect("push failed"); + stable.push(&i); std.push(i); } From db04abaffe2c156f434598cac51c183befc6a314 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 24 Jun 2025 13:23:46 +0200 Subject: [PATCH 10/26] . --- src/reader/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/reader/tests.rs b/src/reader/tests.rs index 62f9eab2..61a63f81 100644 --- a/src/reader/tests.rs +++ b/src/reader/tests.rs @@ -1,5 +1,5 @@ use crate::reader::{BufferedReader, Reader}; -use crate::{write, Memory, VectorMemory, WASM_PAGE_SIZE}; +use crate::{safe_write, Memory, VectorMemory, WASM_PAGE_SIZE}; use proptest::proptest; use std::io::Read; @@ -12,7 +12,7 @@ proptest! { len in 0..1024usize ) { let memory = VectorMemory::default(); - write(&memory, 0, &bytes); + safe_write(&memory, 0, &bytes).unwrap(); let mut reader = build_reader(&memory, buffer_size, offset); let mut output = vec![0u8; len]; @@ -30,7 +30,7 @@ proptest! { repetitions in 2..8 ) { let memory = VectorMemory::default(); - write(&memory, 0, &bytes); + safe_write(&memory, 0, &bytes).unwrap(); // use unbuffered reader, because buffered read might return fewer bytes depending on buffer state let mut reader = Reader::new(&memory, offset); @@ -53,7 +53,7 @@ proptest! { offset in 0..512u64, ) { let memory = VectorMemory::default(); - write(&memory, 0, &bytes); + safe_write(&memory, 0, &bytes).unwrap(); let mut reader = build_reader(&memory, buffer_size, offset); let mut output = vec![]; From 482bd507a5da72c0323407335b07ff901446bfc3 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Tue, 24 Jun 2025 13:27:31 +0200 Subject: [PATCH 11/26] . --- src/vec.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vec.rs b/src/vec.rs index 41c82c4a..432836d6 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -34,8 +34,11 @@ impl Vec { /// /// PRECONDITION: the memory is either empty or contains a valid /// stable vector. - pub fn init(memory: M) -> Result { - BaseVec::::init(memory, MAGIC).map(Self) + pub fn init(memory: M) -> Self { + BaseVec::::init(memory, MAGIC) + .map(Self) + .map_err(|e| panic!("{:?}", e)) + .unwrap() } /// Returns the underlying memory instance. From 2b10343131612a827fb72fef9859959c377523e3 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 16:46:14 +0200 Subject: [PATCH 12/26] fix tests --- src/vec/tests.rs | 97 +++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/src/vec/tests.rs b/src/vec/tests.rs index fee2f282..ef422889 100644 --- a/src/vec/tests.rs +++ b/src/vec/tests.rs @@ -1,7 +1,7 @@ -use super::{InitError, Vec as StableVec}; +use super::Vec as StableVec; use crate::storable::{Bound, Storable}; use crate::vec_mem::VectorMemory as M; -use crate::{GrowFailed, Memory}; +use crate::Memory; use proptest::collection::vec as pvec; use proptest::prelude::*; use std::borrow::Cow; @@ -77,7 +77,7 @@ proptest! { sv.push(&v); } let vec = sv.to_vec(); - prop_assert_eq!(StableVec::::init(sv.into_memory()).unwrap().to_vec(), vec); + prop_assert_eq!(StableVec::::init(sv.into_memory()).to_vec(), vec); } } @@ -104,70 +104,65 @@ fn check_push_pop_model( } #[test] -fn test_init_type_compatibility() { +#[should_panic(expected = "IncompatibleElementType")] +fn test_failure_incompatible_element_type() { let v = StableVec::::new(M::default()); + StableVec::::init(v.into_memory()); +} - assert_eq!( - StableVec::::init(v.into_memory()).unwrap_err(), - InitError::IncompatibleElementType - ); - +#[test] +#[should_panic(expected = "IncompatibleElementType")] +fn test_failure_incompatible_element_type_2() { let v = StableVec::::new(M::default()); - assert_eq!( - StableVec::, M>::init(v.into_memory()).unwrap_err(), - InitError::IncompatibleElementType - ); + StableVec::, M>::init(v.into_memory()); } -#[test] -fn test_init_failures() { - struct EmptyMem; - impl Memory for EmptyMem { - fn size(&self) -> u64 { - 0 - } - fn grow(&self, _: u64) -> i64 { - -1 - } - fn read(&self, _: u64, _: &mut [u8]) { - panic!("out of bounds") - } - fn write(&self, _: u64, _: &[u8]) { - panic!("out of bounds") - } +struct EmptyMem; + +impl Memory for EmptyMem { + fn size(&self) -> u64 { + 0 + } + fn grow(&self, _: u64) -> i64 { + -1 + } + fn read(&self, _: u64, _: &mut [u8]) { + panic!("out of bounds") } + fn write(&self, _: u64, _: &[u8]) { + panic!("out of bounds") + } +} - assert_eq!( - StableVec::::new(EmptyMem).unwrap_err(), - GrowFailed { - current_size: 0, - delta: 1 - } - ); +#[test] +#[should_panic(expected = "GrowFailed { current_size: 0, delta: 1 }")] +fn test_failure_new() { + StableVec::::new(EmptyMem); +} - assert_eq!( - StableVec::::init(EmptyMem).unwrap_err(), - InitError::OutOfMemory, - ); +#[test] +#[should_panic(expected = "OutOfMemory")] +fn test_failure_init() { + StableVec::::init(EmptyMem); +} +#[test] +#[should_panic(expected = "BadMagic { actual: [83, 73, 67], expected: [83, 86, 67] }")] +fn test_failure_bad_magic() { + // InitError::BadMagic { actual: *b"SIC", expected: *b"SVC" } let mem = M::default(); mem.grow(1); mem.write(0, b"SIC\x01\x08\x00\x00\x00\x00\x00\x00\x00\x01"); - assert_eq!( - StableVec::::init(mem).unwrap_err(), - InitError::BadMagic { - actual: *b"SIC", - expected: *b"SVC" - }, - ); + StableVec::::init(mem); +} +#[test] +#[should_panic(expected = "IncompatibleVersion(15)")] +fn test_failure_incompatible_version() { let mem = M::default(); mem.grow(1); mem.write(0, b"SVC\x0f\x08\x00\x00\x00\x00\x00\x00\x00\x01"); - assert_eq!( - StableVec::::init(mem).unwrap_err(), - InitError::IncompatibleVersion(15), - ); + StableVec::::init(mem); } #[allow(clippy::iter_nth_zero)] From 63ad4271ec2a8916f083494ac6a322aebbf63f29 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 16:52:21 +0200 Subject: [PATCH 13/26] expect --- examples/src/task_timer/src/lib.rs | 6 +----- src/min_heap.rs | 2 +- src/vec.rs | 10 +++++----- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/examples/src/task_timer/src/lib.rs b/examples/src/task_timer/src/lib.rs index 6d84bb5c..b2b9af57 100644 --- a/examples/src/task_timer/src/lib.rs +++ b/examples/src/task_timer/src/lib.rs @@ -26,11 +26,7 @@ fn post_upgrade() { #[update] fn schedule_task(after_sec: u64) { let task_time = ic_cdk::api::time() + after_sec * 1_000_000_000; - TASKS.with(|t| { - t.borrow_mut() - .push(&task_time) - .expect("failed to schedule a task") - }); + TASKS.with(|t| t.borrow_mut().push(&task_time)); reschedule(); } diff --git a/src/min_heap.rs b/src/min_heap.rs index 6000b91a..07461427 100644 --- a/src/min_heap.rs +++ b/src/min_heap.rs @@ -62,7 +62,7 @@ where /// /// Complexity: O(log(self.len())) pub fn push(&mut self, item: &T) { - self.0.push(item).map_err(|e| panic!("{:?}", e)).unwrap(); + self.0.push(item).expect("heap push failed"); self.bubble_up(self.0.len() - 1, item); debug_assert_eq!(Ok(()), self.check_invariant()); } diff --git a/src/vec.rs b/src/vec.rs index 432836d6..0f60f5ce 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -24,8 +24,7 @@ impl Vec { pub fn new(memory: M) -> Self { BaseVec::::new(memory, MAGIC) .map(Self) - .map_err(|e| panic!("{:?}", e)) - .unwrap() + .expect("Failed to create a new vector") } /// Initializes a vector in the specified memory. @@ -37,8 +36,7 @@ impl Vec { pub fn init(memory: M) -> Self { BaseVec::::init(memory, MAGIC) .map(Self) - .map_err(|e| panic!("{:?}", e)) - .unwrap() + .expect("Failed to initialize a vector") } /// Returns the underlying memory instance. @@ -80,7 +78,9 @@ impl Vec { /// /// Complexity: O(max_size(T)) pub fn push(&self, item: &T) { - self.0.push(item).map_err(|e| panic!("{:?}", e)).unwrap(); + self.0 + .push(item) + .expect("Failed to push item to the vector"); } /// Removes the item at the end of the vector. From afa39a468f27356340553c17cee08d2e6d6e8d23 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 16:52:45 +0200 Subject: [PATCH 14/26] . --- src/vec.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vec.rs b/src/vec.rs index 0f60f5ce..5d88e02e 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -4,7 +4,6 @@ use crate::base_vec::BaseVec; pub use crate::base_vec::InitError; use crate::storable::Storable; use crate::Memory; -use core::panic; use std::fmt; #[cfg(test)] From 150b992489392a8e2694046b82d6647312a08e86 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 16:57:17 +0200 Subject: [PATCH 15/26] . --- benchmarks/compare/src/main.rs | 8 +++----- benchmarks/vec/src/main.rs | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/benchmarks/compare/src/main.rs b/benchmarks/compare/src/main.rs index 92e57d2a..f2048619 100644 --- a/benchmarks/compare/src/main.rs +++ b/benchmarks/compare/src/main.rs @@ -84,21 +84,19 @@ fn read_chunks_btreemap(mem_id: u8, n: usize) { // StableVec benchmarks fn write_chunks_vec(mem_id: u8, n: usize) { - let vec: StableVec, _> = - StableVec::new(init_memory(mem_id)).expect("Vec::new failed"); + let vec: StableVec, _> = StableVec::new(init_memory(mem_id)); let chunks: Vec<_> = chunk_data(n).iter().map(|v| BoundedVecN::from(v)).collect(); bench_fn(|| { for chunk in &chunks { - vec.push(chunk).expect("Vec::push failed"); + vec.push(chunk); } }); } fn read_chunks_vec(mem_id: u8, n: usize) { write_chunks_vec::(mem_id, n); - let vec: StableVec, _> = - StableVec::init(init_memory(mem_id)).expect("Vec::init failed"); + let vec: StableVec, _> = StableVec::init(init_memory(mem_id)); bench_fn(|| { for i in 0..n as u64 { diff --git a/benchmarks/vec/src/main.rs b/benchmarks/vec/src/main.rs index 7463ff59..02741875 100644 --- a/benchmarks/vec/src/main.rs +++ b/benchmarks/vec/src/main.rs @@ -91,7 +91,7 @@ fn vec_insert_blob() -> BenchResult { fn vec_insert() -> BenchResult { let num_items = 10_000; - let svec: StableVec = StableVec::new(DefaultMemoryImpl::default()).unwrap(); + let svec: StableVec = StableVec::new(DefaultMemoryImpl::default()); let mut rng = Rng::from_seed(0); let mut random_items = Vec::with_capacity(num_items); @@ -102,7 +102,7 @@ fn vec_insert() -> BenchResult { bench_fn(|| { for item in random_items.iter() { - svec.push(item).unwrap(); + svec.push(item); } }) } @@ -118,12 +118,12 @@ fn vec_get_blob_mem_manager() -> BenchResult { fn vec_get(memory: impl Memory) -> BenchResult { let num_items = 10_000; - let svec: StableVec = StableVec::new(memory).unwrap(); + let svec: StableVec = StableVec::new(memory); let mut rng = Rng::from_seed(0); for _ in 0..num_items { - svec.push(&T::random(&mut rng)).unwrap(); + svec.push(&T::random(&mut rng)); } bench_fn(|| { From b7b5a3ada436cab16252a55ee6c516ac4e1606fc Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 17:05:26 +0200 Subject: [PATCH 16/26] . --- src/min_heap.rs | 16 ++++--- src/min_heap/tests.rs | 94 +++++++++++++++++++--------------------- tests/api_confomrance.rs | 2 +- 3 files changed, 55 insertions(+), 57 deletions(-) diff --git a/src/min_heap.rs b/src/min_heap.rs index 07461427..e2f2c55c 100644 --- a/src/min_heap.rs +++ b/src/min_heap.rs @@ -1,6 +1,6 @@ -use crate::base_vec::{BaseVec, InitError}; +use crate::base_vec::BaseVec; use crate::storable::Storable; -use crate::{GrowFailed, Memory}; +use crate::Memory; use std::fmt; #[cfg(test)] @@ -30,8 +30,10 @@ where /// contained. /// /// Complexity: O(1) - pub fn new(memory: M) -> Result { - BaseVec::::new(memory, MAGIC).map(Self) + pub fn new(memory: M) -> Self { + BaseVec::::new(memory, MAGIC) + .map(Self) + .expect("Failed to create a new heap") } /// Initializes a heap in the specified memory. @@ -40,8 +42,10 @@ where /// /// PRECONDITION: the memory is either empty or contains a valid /// stable heap. - pub fn init(memory: M) -> Result { - BaseVec::::init(memory, MAGIC).map(Self) + pub fn init(memory: M) -> Self { + BaseVec::::init(memory, MAGIC) + .map(Self) + .expect("Failed to initialize a heap") } /// Returns the number of items in the heap. diff --git a/src/min_heap/tests.rs b/src/min_heap/tests.rs index 53721386..1a59e3b2 100644 --- a/src/min_heap/tests.rs +++ b/src/min_heap/tests.rs @@ -1,6 +1,6 @@ -use super::{InitError, MinHeap as StableMinHeap}; +use super::MinHeap as StableMinHeap; use crate::vec_mem::VectorMemory as M; -use crate::{GrowFailed, Memory}; +use crate::Memory; use proptest::collection::vec as pvec; use proptest::prelude::*; use std::cmp::Reverse; @@ -24,7 +24,7 @@ proptest! { #[test] fn push_pop_model_u64(ops in pvec(arb_op(any::()), 40)) { let mut h = BinaryHeap::new(); - let mut sh = StableMinHeap::::new(M::default()).unwrap(); + let mut sh = StableMinHeap::::new(M::default()); for op in ops { match op { @@ -41,7 +41,7 @@ proptest! { #[test] fn pop_sorted(mut items in pvec(any::(), 0..50)) { - let mut sh = StableMinHeap::::new(M::default()).unwrap(); + let mut sh = StableMinHeap::::new(M::default()); for x in &items { sh.push(x); } @@ -54,7 +54,7 @@ proptest! { #[test] fn test_simple_case() { - let mut h = StableMinHeap::::new(M::default()).unwrap(); + let mut h = StableMinHeap::::new(M::default()); assert_eq!(h.pop(), None); h.push(&0); h.push(&3); @@ -69,62 +69,56 @@ fn test_simple_case() { } #[test] -fn test_init_type_compatibility() { - let h = StableMinHeap::::new(M::default()).unwrap(); - - assert_eq!( - StableMinHeap::::init(h.into_memory()).unwrap_err(), - InitError::IncompatibleElementType - ); +#[should_panic(expected = "IncompatibleElementType")] +fn test_failure_incompatible_element_type() { + let h = StableMinHeap::::new(M::default()); + StableMinHeap::::init(h.into_memory()); } -#[test] -fn test_init_failures() { - struct EmptyMem; - impl Memory for EmptyMem { - fn size(&self) -> u64 { - 0 - } - fn grow(&self, _: u64) -> i64 { - -1 - } - fn read(&self, _: u64, _: &mut [u8]) { - panic!("out of bounds") - } - fn write(&self, _: u64, _: &[u8]) { - panic!("out of bounds") - } +struct EmptyMem; + +impl Memory for EmptyMem { + fn size(&self) -> u64 { + 0 + } + fn grow(&self, _: u64) -> i64 { + -1 + } + fn read(&self, _: u64, _: &mut [u8]) { + panic!("out of bounds") + } + fn write(&self, _: u64, _: &[u8]) { + panic!("out of bounds") } +} - assert_eq!( - StableMinHeap::::new(EmptyMem).unwrap_err(), - GrowFailed { - current_size: 0, - delta: 1 - } - ); +#[test] +#[should_panic(expected = "GrowFailed { current_size: 0, delta: 1 }")] +fn test_failure_new() { + StableMinHeap::::new(EmptyMem); +} - assert_eq!( - StableMinHeap::::init(EmptyMem).unwrap_err(), - InitError::OutOfMemory, - ); +#[test] +#[should_panic(expected = "OutOfMemory")] +fn test_failure_init() { + StableMinHeap::::init(EmptyMem); +} +#[test] +#[should_panic(expected = "BadMagic { actual: [83, 86, 67], expected: [83, 77, 72] }")] +fn test_failure_bad_magic() { + // BadMagic { actual: *b"SVC", expected: *b"SMH" } let mem = M::default(); mem.grow(1); mem.write(0, b"SVC\x01\x08\x00\x00\x00\x00\x00\x00\x00\x01"); - assert_eq!( - StableMinHeap::::init(mem).unwrap_err(), - InitError::BadMagic { - actual: *b"SVC", - expected: *b"SMH" - }, - ); + StableMinHeap::::init(mem); +} +#[test] +#[should_panic(expected = "IncompatibleVersion(15)")] +fn test_failure_incompatible_version() { let mem = M::default(); mem.grow(1); mem.write(0, b"SMH\x0f\x08\x00\x00\x00\x00\x00\x00\x00\x01"); - assert_eq!( - StableMinHeap::::init(mem).unwrap_err(), - InitError::IncompatibleVersion(15), - ); + StableMinHeap::::init(mem); } diff --git a/tests/api_confomrance.rs b/tests/api_confomrance.rs index 78ffdc3d..50b57f19 100644 --- a/tests/api_confomrance.rs +++ b/tests/api_confomrance.rs @@ -240,7 +240,7 @@ fn api_conformance_btreeset() { #[test] fn api_conformance_min_heap() { let mem = make_memory(); - let mut stable = MinHeap::new(mem).unwrap(); + let mut stable = MinHeap::new(mem); let mut std = BinaryHeap::new(); let n = 10_u32; From d08c455afb1c04273835fd5d26dac7e571cc0e45 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 17:08:10 +0200 Subject: [PATCH 17/26] . --- examples/src/task_timer/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/task_timer/src/lib.rs b/examples/src/task_timer/src/lib.rs index b2b9af57..64722d3f 100644 --- a/examples/src/task_timer/src/lib.rs +++ b/examples/src/task_timer/src/lib.rs @@ -14,7 +14,7 @@ thread_local! { MEMORY_MANAGER.with(|mm| RefCell::new( StableMinHeap::init(mm.borrow().get(MemoryId::new(1))) - .expect("failed to initialize the tasks")) + ) ); } From 536baff55cf5ddc61869660cb7fec80ec2a4fe76 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 17:08:55 +0200 Subject: [PATCH 18/26] fmt --- examples/src/quick_start/src/lib.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/src/quick_start/src/lib.rs b/examples/src/quick_start/src/lib.rs index 6b0e344b..1faef210 100644 --- a/examples/src/quick_start/src/lib.rs +++ b/examples/src/quick_start/src/lib.rs @@ -39,8 +39,7 @@ fn stable_get(key: u128) -> Option { // if it exists. #[update] fn stable_insert(key: u128, value: u128) -> Option { - STATE - .with(|s| s.borrow_mut().stable_data.insert(key, value)) + STATE.with(|s| s.borrow_mut().stable_data.insert(key, value)) } // Sets the data that lives on the heap. @@ -61,7 +60,8 @@ fn pre_upgrade() { // Serialize the state. // This example is using CBOR, but you can use any data format you like. let mut state_bytes = vec![]; - STATE.with(|s| ciborium::ser::into_writer(&*s.borrow(), &mut state_bytes)) + STATE + .with(|s| ciborium::ser::into_writer(&*s.borrow(), &mut state_bytes)) .expect("failed to encode state"); // Write the length of the serialized bytes to memory, followed by the @@ -89,9 +89,7 @@ fn post_upgrade() { // Deserialize and set the state. let state = ciborium::de::from_reader(&*state_bytes).expect("failed to decode state"); - STATE.with(|s| { - *s.borrow_mut() = state - }); + STATE.with(|s| *s.borrow_mut() = state); } fn init_stable_data() -> StableBTreeMap { @@ -106,4 +104,3 @@ impl Default for State { } } } - From ba88029dcbf5cf217acfbe15901f0c21d4c3c5a6 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 17:16:49 +0200 Subject: [PATCH 19/26] add api_conformance_cell --- tests/api_confomrance.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/api_confomrance.rs b/tests/api_confomrance.rs index 50b57f19..b123afda 100644 --- a/tests/api_confomrance.rs +++ b/tests/api_confomrance.rs @@ -1,5 +1,6 @@ use ic_stable_structures::btreemap::BTreeMap; use ic_stable_structures::btreeset::BTreeSet; +use ic_stable_structures::cell::Cell as StableCell; use ic_stable_structures::min_heap::MinHeap; use ic_stable_structures::vec::Vec as StableVec; use std::cell::RefCell; @@ -323,3 +324,30 @@ fn api_conformance_vec() { assert_eq!(stable.len(), std.len() as u64); assert_eq!(stable.is_empty(), std.is_empty()); } + +#[test] +fn api_conformance_cell() { + let mem = make_memory(); + + // use u32 for simplicity; also supported by Storable + let initial = 42u32; + let updated = 777u32; + + let mut stable = StableCell::new(mem.clone(), initial).expect("init stable cell"); + let std = RefCell::new(initial); + + // Get + assert_eq!(*stable.get(), *std.borrow()); + + // Set + let old_stable = stable.set(updated).expect("set stable cell"); + let old_std = std.replace(updated); + assert_eq!(old_stable, old_std); + + // After set + assert_eq!(*stable.get(), *std.borrow()); + + // Check that the value persists across re-init + let stable = StableCell::init(mem, 0).expect("reinit stable cell"); + assert_eq!(*stable.get(), updated); +} From 746674902854e00106bfdc39dafd2ef385961684 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 17:20:25 +0200 Subject: [PATCH 20/26] add api_conformance_log --- tests/api_confomrance.rs | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/tests/api_confomrance.rs b/tests/api_confomrance.rs index b123afda..8d657583 100644 --- a/tests/api_confomrance.rs +++ b/tests/api_confomrance.rs @@ -1,12 +1,14 @@ +use std::cell::RefCell; +use std::cmp::Reverse; +use std::collections::BinaryHeap; +use std::rc::Rc; + use ic_stable_structures::btreemap::BTreeMap; use ic_stable_structures::btreeset::BTreeSet; use ic_stable_structures::cell::Cell as StableCell; +use ic_stable_structures::log::Log as StableLog; use ic_stable_structures::min_heap::MinHeap; use ic_stable_structures::vec::Vec as StableVec; -use std::cell::RefCell; -use std::cmp::Reverse; -use std::collections::BinaryHeap; -use std::rc::Rc; pub fn make_memory() -> Rc>> { Rc::new(RefCell::new(Vec::new())) @@ -351,3 +353,33 @@ fn api_conformance_cell() { let stable = StableCell::init(mem, 0).expect("reinit stable cell"); assert_eq!(*stable.get(), updated); } + +#[test] +fn api_conformance_log() { + let index_mem = make_memory(); + let data_mem = make_memory(); + let log = StableLog::new(index_mem.clone(), data_mem.clone()); + let mut std = Vec::new(); + + let n = 10_u32; + + // Append elements and compare returned indices + for i in 0..n { + let idx = log.append(&i).expect("append should succeed"); + assert_eq!(idx, std.len() as u64); + std.push(i); + } + + // Length and is_empty + assert_eq!(log.len(), std.len() as u64); + assert_eq!(log.is_empty(), std.is_empty()); + + // Get by index + for i in 0..n { + assert_eq!(log.get(i as u64), Some(std[i as usize])); + } + + // Iteration + let log_items: Vec<_> = log.iter().collect(); + assert_eq!(log_items, std); +} From a54abb5d7a335198d9a4653845b742c7e6c7c976 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Wed, 25 Jun 2025 17:25:32 +0200 Subject: [PATCH 21/26] cell --- src/cell.rs | 27 ++++++++++++++------------- tests/api_confomrance.rs | 8 ++------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/cell.rs b/src/cell.rs index 33f672dc..e2fa47be 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -96,36 +96,37 @@ pub struct Cell { impl Cell { /// Creates a new cell in the specified memory, overwriting the previous contents of the memory. - pub fn new(memory: M, value: T) -> Result { - Self::flush_value(&memory, &value)?; - Ok(Self { memory, value }) + pub fn new(memory: M, value: T) -> Self { + Self::flush_value(&memory, &value).expect("Failed to write initial value to the memory"); + Self { memory, value } } /// Initializes the value of the cell based on the contents of the `memory`. /// If the memory already contains a cell, initializes the cell with the decoded value. /// Otherwise, sets the cell value to `default_value` and writes it to the memory. - pub fn init(memory: M, default_value: T) -> Result { + pub fn init(memory: M, default_value: T) -> Self { if memory.size() == 0 { - return Ok(Self::new(memory, default_value)?); + return Self::new(memory, default_value); } let header = Self::read_header(&memory); if &header.magic != MAGIC { - return Ok(Self::new(memory, default_value)?); + return Self::new(memory, default_value); } if header.version != LAYOUT_VERSION { - return Err(InitError::IncompatibleVersion { + let err = InitError::IncompatibleVersion { last_supported_version: LAYOUT_VERSION, decoded_version: header.version, - }); + }; + panic!("Failed to initialize cell: {}", err); } - Ok(Self { + Self { value: Self::read_value(&memory, header.value_length), memory, - }) + } } /// Reads and decodes the value of specified length. @@ -169,9 +170,9 @@ impl Cell { /// Updates the current value in the cell. /// If the new value is too large to fit into the memory, the value in the cell does not /// change. - pub fn set(&mut self, value: T) -> Result { - Self::flush_value(&self.memory, &value)?; - Ok(std::mem::replace(&mut self.value, value)) + pub fn set(&mut self, value: T) -> T { + Self::flush_value(&self.memory, &value).expect("Failed to write value to the memory"); + std::mem::replace(&mut self.value, value) } /// Writes the value to the memory, growing the memory size if needed. diff --git a/tests/api_confomrance.rs b/tests/api_confomrance.rs index 8d657583..6ea1a653 100644 --- a/tests/api_confomrance.rs +++ b/tests/api_confomrance.rs @@ -335,23 +335,19 @@ fn api_conformance_cell() { let initial = 42u32; let updated = 777u32; - let mut stable = StableCell::new(mem.clone(), initial).expect("init stable cell"); + let mut stable = StableCell::new(mem.clone(), initial); let std = RefCell::new(initial); // Get assert_eq!(*stable.get(), *std.borrow()); // Set - let old_stable = stable.set(updated).expect("set stable cell"); + let old_stable = stable.set(updated); let old_std = std.replace(updated); assert_eq!(old_stable, old_std); // After set assert_eq!(*stable.get(), *std.borrow()); - - // Check that the value persists across re-init - let stable = StableCell::init(mem, 0).expect("reinit stable cell"); - assert_eq!(*stable.get(), updated); } #[test] From 8102ee3a5ffd03735ae582f416c7b82365bdee8a Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Thu, 26 Jun 2025 10:42:10 +0200 Subject: [PATCH 22/26] Revert "cell" This reverts commit a54abb5d7a335198d9a4653845b742c7e6c7c976. --- src/cell.rs | 27 +++++++++++++-------------- tests/api_confomrance.rs | 8 ++++++-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/cell.rs b/src/cell.rs index e2fa47be..33f672dc 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -96,37 +96,36 @@ pub struct Cell { impl Cell { /// Creates a new cell in the specified memory, overwriting the previous contents of the memory. - pub fn new(memory: M, value: T) -> Self { - Self::flush_value(&memory, &value).expect("Failed to write initial value to the memory"); - Self { memory, value } + pub fn new(memory: M, value: T) -> Result { + Self::flush_value(&memory, &value)?; + Ok(Self { memory, value }) } /// Initializes the value of the cell based on the contents of the `memory`. /// If the memory already contains a cell, initializes the cell with the decoded value. /// Otherwise, sets the cell value to `default_value` and writes it to the memory. - pub fn init(memory: M, default_value: T) -> Self { + pub fn init(memory: M, default_value: T) -> Result { if memory.size() == 0 { - return Self::new(memory, default_value); + return Ok(Self::new(memory, default_value)?); } let header = Self::read_header(&memory); if &header.magic != MAGIC { - return Self::new(memory, default_value); + return Ok(Self::new(memory, default_value)?); } if header.version != LAYOUT_VERSION { - let err = InitError::IncompatibleVersion { + return Err(InitError::IncompatibleVersion { last_supported_version: LAYOUT_VERSION, decoded_version: header.version, - }; - panic!("Failed to initialize cell: {}", err); + }); } - Self { + Ok(Self { value: Self::read_value(&memory, header.value_length), memory, - } + }) } /// Reads and decodes the value of specified length. @@ -170,9 +169,9 @@ impl Cell { /// Updates the current value in the cell. /// If the new value is too large to fit into the memory, the value in the cell does not /// change. - pub fn set(&mut self, value: T) -> T { - Self::flush_value(&self.memory, &value).expect("Failed to write value to the memory"); - std::mem::replace(&mut self.value, value) + pub fn set(&mut self, value: T) -> Result { + Self::flush_value(&self.memory, &value)?; + Ok(std::mem::replace(&mut self.value, value)) } /// Writes the value to the memory, growing the memory size if needed. diff --git a/tests/api_confomrance.rs b/tests/api_confomrance.rs index 6ea1a653..8d657583 100644 --- a/tests/api_confomrance.rs +++ b/tests/api_confomrance.rs @@ -335,19 +335,23 @@ fn api_conformance_cell() { let initial = 42u32; let updated = 777u32; - let mut stable = StableCell::new(mem.clone(), initial); + let mut stable = StableCell::new(mem.clone(), initial).expect("init stable cell"); let std = RefCell::new(initial); // Get assert_eq!(*stable.get(), *std.borrow()); // Set - let old_stable = stable.set(updated); + let old_stable = stable.set(updated).expect("set stable cell"); let old_std = std.replace(updated); assert_eq!(old_stable, old_std); // After set assert_eq!(*stable.get(), *std.borrow()); + + // Check that the value persists across re-init + let stable = StableCell::init(mem, 0).expect("reinit stable cell"); + assert_eq!(*stable.get(), updated); } #[test] From 79324c56f142b70578de6f01edc414c5f230bf68 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Thu, 26 Jun 2025 10:44:11 +0200 Subject: [PATCH 23/26] fix fuzz build --- fuzz/fuzz_targets/stable_minheap_multiple_ops_persistent.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/fuzz/fuzz_targets/stable_minheap_multiple_ops_persistent.rs b/fuzz/fuzz_targets/stable_minheap_multiple_ops_persistent.rs index f6410702..269fda21 100644 --- a/fuzz/fuzz_targets/stable_minheap_multiple_ops_persistent.rs +++ b/fuzz/fuzz_targets/stable_minheap_multiple_ops_persistent.rs @@ -25,7 +25,6 @@ thread_local! { StableMinHeap::init( MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(0))), ) - .expect("Unable to init Bounded StableMinHeap") ); static DIR: TempDir = tempdir().unwrap(); From 6ab72f630b4692ccfd529924044082cb5627eced Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Thu, 26 Jun 2025 12:45:03 +0200 Subject: [PATCH 24/26] cell --- src/cell.rs | 27 ++++++++++++++------------- src/cell/tests.rs | 32 +++++++++++++------------------- tests/api_confomrance.rs | 6 +++--- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/cell.rs b/src/cell.rs index 33f672dc..e2fa47be 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -96,36 +96,37 @@ pub struct Cell { impl Cell { /// Creates a new cell in the specified memory, overwriting the previous contents of the memory. - pub fn new(memory: M, value: T) -> Result { - Self::flush_value(&memory, &value)?; - Ok(Self { memory, value }) + pub fn new(memory: M, value: T) -> Self { + Self::flush_value(&memory, &value).expect("Failed to write initial value to the memory"); + Self { memory, value } } /// Initializes the value of the cell based on the contents of the `memory`. /// If the memory already contains a cell, initializes the cell with the decoded value. /// Otherwise, sets the cell value to `default_value` and writes it to the memory. - pub fn init(memory: M, default_value: T) -> Result { + pub fn init(memory: M, default_value: T) -> Self { if memory.size() == 0 { - return Ok(Self::new(memory, default_value)?); + return Self::new(memory, default_value); } let header = Self::read_header(&memory); if &header.magic != MAGIC { - return Ok(Self::new(memory, default_value)?); + return Self::new(memory, default_value); } if header.version != LAYOUT_VERSION { - return Err(InitError::IncompatibleVersion { + let err = InitError::IncompatibleVersion { last_supported_version: LAYOUT_VERSION, decoded_version: header.version, - }); + }; + panic!("Failed to initialize cell: {}", err); } - Ok(Self { + Self { value: Self::read_value(&memory, header.value_length), memory, - }) + } } /// Reads and decodes the value of specified length. @@ -169,9 +170,9 @@ impl Cell { /// Updates the current value in the cell. /// If the new value is too large to fit into the memory, the value in the cell does not /// change. - pub fn set(&mut self, value: T) -> Result { - Self::flush_value(&self.memory, &value)?; - Ok(std::mem::replace(&mut self.value, value)) + pub fn set(&mut self, value: T) -> T { + Self::flush_value(&self.memory, &value).expect("Failed to write value to the memory"); + std::mem::replace(&mut self.value, value) } /// Writes the value to the memory, growing the memory size if needed. diff --git a/src/cell/tests.rs b/src/cell/tests.rs index b6d44812..acfe8257 100644 --- a/src/cell/tests.rs +++ b/src/cell/tests.rs @@ -1,62 +1,56 @@ -use crate::cell::{Cell, ValueError}; +use crate::cell::Cell; use crate::storable::Storable; use crate::vec_mem::VectorMemory; use crate::{Memory, RestrictedMemory, WASM_PAGE_SIZE}; fn reload(c: Cell) -> Cell { - Cell::init(c.into_memory(), T::default()).unwrap() + Cell::init(c.into_memory(), T::default()) } #[test] fn test_cell_init() { let mem = VectorMemory::default(); - let cell = Cell::init(mem, 1024u64).unwrap(); + let cell = Cell::init(mem, 1024u64); assert_eq!(*cell.get(), 1024u64); let mem = cell.into_memory(); assert_ne!(mem.size(), 0); - let cell = Cell::init(mem, 0u64).unwrap(); + let cell = Cell::init(mem, 0u64); assert_eq!(1024u64, *cell.get()); // Check that Cell::new overwrites the contents unconditionally. - let cell = Cell::new(cell.into_memory(), 2048u64).unwrap(); + let cell = Cell::new(cell.into_memory(), 2048u64); assert_eq!(2048u64, *cell.get()); } #[test] fn test_cell_init_empty() { let mem = VectorMemory::default(); - let cell = Cell::init(mem, vec![]).unwrap(); + let cell = Cell::init(mem, vec![]); assert_eq!(Vec::::new(), *cell.get()); } #[test] -fn test_out_of_space() { +#[should_panic(expected = "ValueTooLarge { value_size: 65536 }")] +fn test_failure_out_of_space() { let mem = RestrictedMemory::new(VectorMemory::default(), 0..1); let data = [1u8; 100]; - let mut cell = Cell::new(mem, data.to_vec()).unwrap(); + let mut cell = Cell::new(mem, data.to_vec()); assert_eq!(&data[..], &cell.get()[..]); - assert_eq!( - Err(ValueError::ValueTooLarge { - value_size: WASM_PAGE_SIZE, - }), - cell.set(vec![2u8; WASM_PAGE_SIZE as usize]) - ); - - assert_eq!(&data[..], &cell.get()[..]); + cell.set(vec![2u8; WASM_PAGE_SIZE as usize]); } #[test] fn test_cell_grow_and_shrink() { let mem = VectorMemory::default(); - let mut cell = Cell::init(mem, vec![1u8; 10]).unwrap(); + let mut cell = Cell::init(mem, vec![1u8; 10]); - cell.set(vec![2u8; 20]).unwrap(); + cell.set(vec![2u8; 20]); let mut cell = reload(cell); assert_eq!(&[2u8; 20][..], &cell.get()[..]); - cell.set(vec![3u8; 5]).unwrap(); + cell.set(vec![3u8; 5]); let cell = reload(cell); assert_eq!(&[3u8; 5][..], &cell.get()[..]); } diff --git a/tests/api_confomrance.rs b/tests/api_confomrance.rs index 8d657583..cbe9dd58 100644 --- a/tests/api_confomrance.rs +++ b/tests/api_confomrance.rs @@ -335,14 +335,14 @@ fn api_conformance_cell() { let initial = 42u32; let updated = 777u32; - let mut stable = StableCell::new(mem.clone(), initial).expect("init stable cell"); + let mut stable = StableCell::new(mem.clone(), initial); let std = RefCell::new(initial); // Get assert_eq!(*stable.get(), *std.borrow()); // Set - let old_stable = stable.set(updated).expect("set stable cell"); + let old_stable = stable.set(updated); let old_std = std.replace(updated); assert_eq!(old_stable, old_std); @@ -350,7 +350,7 @@ fn api_conformance_cell() { assert_eq!(*stable.get(), *std.borrow()); // Check that the value persists across re-init - let stable = StableCell::init(mem, 0).expect("reinit stable cell"); + let stable = StableCell::init(mem, 0); assert_eq!(*stable.get(), updated); } From 32b647a3f49cc393ae879d05c18df1d892c1a014 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Thu, 26 Jun 2025 12:54:08 +0200 Subject: [PATCH 25/26] log --- src/cell.rs | 12 ++++---- src/log.rs | 34 +++++++++++++---------- src/log/tests.rs | 71 ++++++++++++++++++++++++------------------------ 3 files changed, 63 insertions(+), 54 deletions(-) diff --git a/src/cell.rs b/src/cell.rs index e2fa47be..8583efa7 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -116,11 +116,13 @@ impl Cell { } if header.version != LAYOUT_VERSION { - let err = InitError::IncompatibleVersion { - last_supported_version: LAYOUT_VERSION, - decoded_version: header.version, - }; - panic!("Failed to initialize cell: {}", err); + panic!( + "Failed to initialize cell: {}", + InitError::IncompatibleVersion { + last_supported_version: LAYOUT_VERSION, + decoded_version: header.version, + } + ); } Self { diff --git a/src/log.rs b/src/log.rs index f6d1d284..015dbd36 100644 --- a/src/log.rs +++ b/src/log.rs @@ -183,33 +183,39 @@ impl Log { /// Initializes the log based on the contents of the provided memory trait objects. /// If the memory trait objects already contain a stable log, this function recovers it from the stable /// memory. Otherwise, this function allocates a new empty log. - pub fn init(index_memory: INDEX, data_memory: DATA) -> Result { + pub fn init(index_memory: INDEX, data_memory: DATA) -> Self { // if the data memory is not containing expected data, the index is useless anyway. if data_memory.size() == 0 { - return Ok(Self::new(index_memory, data_memory)); + return Self::new(index_memory, data_memory); } let data_header = Self::read_header(&data_memory); if &data_header.magic != DATA_MAGIC { - return Ok(Self::new(index_memory, data_memory)); + return Self::new(index_memory, data_memory); } if data_header.version != LAYOUT_VERSION { - return Err(InitError::IncompatibleDataVersion { - last_supported_version: LAYOUT_VERSION, - decoded_version: data_header.version, - }); + panic!( + "Failed to initialize log: {}", + InitError::IncompatibleDataVersion { + last_supported_version: LAYOUT_VERSION, + decoded_version: data_header.version, + } + ); } let index_header = Self::read_header(&index_memory); if &index_header.magic != INDEX_MAGIC { - return Err(InitError::InvalidIndex); + panic!("Failed to initialize log: {}", InitError::InvalidIndex); } if index_header.version != LAYOUT_VERSION { - return Err(InitError::IncompatibleIndexVersion { - last_supported_version: LAYOUT_VERSION, - decoded_version: index_header.version, - }); + panic!( + "Failed to initialize log: {}", + InitError::IncompatibleIndexVersion { + last_supported_version: LAYOUT_VERSION, + decoded_version: index_header.version, + } + ); } #[cfg(debug_assertions)] @@ -217,11 +223,11 @@ impl Log { assert_eq!(Ok(()), Self::validate_v1_index(&index_memory)); } - Ok(Self { + Self { index_memory, data_memory, _marker: PhantomData, - }) + } } /// Writes the stable log header to memory. diff --git a/src/log/tests.rs b/src/log/tests.rs index 43bd0558..6b45ffe0 100644 --- a/src/log/tests.rs +++ b/src/log/tests.rs @@ -1,4 +1,4 @@ -use crate::log::{iter_thread_local, InitError, Log, WriteError}; +use crate::log::{iter_thread_local, Log, WriteError}; use crate::vec_mem::VectorMemory; use crate::{Memory, RestrictedMemory, WASM_PAGE_SIZE}; use std::cell::RefCell; @@ -9,7 +9,6 @@ thread_local! { fn new_string_log() -> Log { Log::init(VectorMemory::default(), VectorMemory::default()) - .expect("failed to initialize stable log") } #[test] @@ -21,7 +20,7 @@ fn test_log_construct() { assert_eq!(log.index_size_bytes(), 40); let (index_memory, data_memory) = log.into_memories(); - let log = Log::, _, _>::init(index_memory, data_memory).expect("failed to init log"); + let log = Log::, _, _>::init(index_memory, data_memory); assert_eq!(log.len(), 0); assert_eq!(log.log_size_bytes(), 0); assert_eq!(log.index_size_bytes(), 40); @@ -42,8 +41,7 @@ fn test_new_overwrites() { #[test] fn test_log_init_empty() { - let log = Log::, _, _>::init(VectorMemory::default(), VectorMemory::default()) - .expect("failed to init log"); + let log = Log::, _, _>::init(VectorMemory::default(), VectorMemory::default()); assert_eq!(log.len(), 0); assert_eq!(log.log_size_bytes(), 0); @@ -55,28 +53,27 @@ fn test_log_init_with_different_data_magic() { let mem = VectorMemory::default(); assert_eq!(mem.grow(1), 0); mem.write(0, b"WAS"); - let log = Log::, _, _>::init(VectorMemory::default(), mem).expect("failed to init log"); + let log = Log::, _, _>::init(VectorMemory::default(), mem); assert_eq!(log.len(), 0); } #[test] -fn test_log_init_with_different_index_magic() { +#[should_panic(expected = "Invalid index")] +fn test_failure_log_init_with_different_index_magic() { let index_mem = VectorMemory::default(); assert_eq!(index_mem.grow(1), 0); index_mem.write(0, b"WAS"); let data_mem = VectorMemory::default(); assert_eq!(data_mem.grow(1), 0); data_mem.write(0, b"GLD\x01"); - assert_eq!( - Log::, _, _>::init(index_mem, data_mem) - .map(|_| ()) - .unwrap_err(), - InitError::InvalidIndex - ); + Log::, _, _>::init(index_mem, data_mem); } #[test] -fn test_log_load_bad_index_version() { +#[should_panic( + expected = "Incompatible index version: last supported version is 1, but decoded version is 2" +)] +fn test_failure_log_load_bad_index_version() { let index_memory = VectorMemory::default(); assert_eq!(index_memory.grow(1), 0); index_memory.write(0, b"GLI\x02"); @@ -84,32 +81,36 @@ fn test_log_load_bad_index_version() { let data_memory = VectorMemory::default(); assert_eq!(data_memory.grow(1), 0); data_memory.write(0, b"GLD\x01"); - assert_eq!( - Log::, _, _>::init(index_memory, data_memory) - .map(|_| ()) - .unwrap_err(), - InitError::IncompatibleIndexVersion { - last_supported_version: 1, - decoded_version: 2 - }, - ); + Log::, _, _>::init(index_memory, data_memory); + // assert_eq!( + // Log::, _, _>::init(index_memory, data_memory) + // .map(|_| ()) + // .unwrap_err(), + // InitError::IncompatibleIndexVersion { + // last_supported_version: 1, + // decoded_version: 2 + // }, + // ); } #[test] -fn test_log_load_bad_data_version() { +#[should_panic( + expected = "Incompatible data version: last supported version is 1, but decoded version is 2" +)] +fn test_failure_log_load_bad_data_version() { let mem = VectorMemory::default(); assert_eq!(mem.grow(1), 0); mem.write(0, b"GLD\x02"); - - assert_eq!( - Log::, _, _>::init(VectorMemory::default(), mem) - .map(|_| ()) - .unwrap_err(), - InitError::IncompatibleDataVersion { - last_supported_version: 1, - decoded_version: 2 - }, - ); + Log::, _, _>::init(VectorMemory::default(), mem); + // assert_eq!( + // Log::, _, _>::init(VectorMemory::default(), mem) + // .map(|_| ()) + // .unwrap_err(), + // InitError::IncompatibleDataVersion { + // last_supported_version: 1, + // decoded_version: 2 + // }, + // ); } #[test] @@ -138,7 +139,7 @@ fn test_log_append_persistence() { let (index_memory, data_memory) = log.into_memories(); - let log = Log::, _, _>::init(index_memory, data_memory).unwrap(); + let log = Log::, _, _>::init(index_memory, data_memory); assert_eq!(log.len(), 1); assert_eq!(log.get(idx).unwrap(), b"DEADBEEF".to_vec()); assert_eq!(log.log_size_bytes(), b"DEADBEEF".len() as u64); From aa60ee5d6f12a5c4fc57da36c7c29fe51ed9a0a5 Mon Sep 17 00:00:00 2001 From: Maksym Arutyunyan Date: Thu, 26 Jun 2025 12:56:13 +0200 Subject: [PATCH 26/26] . --- src/log/tests.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/log/tests.rs b/src/log/tests.rs index 6b45ffe0..545ea72a 100644 --- a/src/log/tests.rs +++ b/src/log/tests.rs @@ -82,15 +82,6 @@ fn test_failure_log_load_bad_index_version() { assert_eq!(data_memory.grow(1), 0); data_memory.write(0, b"GLD\x01"); Log::, _, _>::init(index_memory, data_memory); - // assert_eq!( - // Log::, _, _>::init(index_memory, data_memory) - // .map(|_| ()) - // .unwrap_err(), - // InitError::IncompatibleIndexVersion { - // last_supported_version: 1, - // decoded_version: 2 - // }, - // ); } #[test] @@ -102,15 +93,6 @@ fn test_failure_log_load_bad_data_version() { assert_eq!(mem.grow(1), 0); mem.write(0, b"GLD\x02"); Log::, _, _>::init(VectorMemory::default(), mem); - // assert_eq!( - // Log::, _, _>::init(VectorMemory::default(), mem) - // .map(|_| ()) - // .unwrap_err(), - // InitError::IncompatibleDataVersion { - // last_supported_version: 1, - // decoded_version: 2 - // }, - // ); } #[test]