Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions benchmarks/compare/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,19 @@ fn read_chunks_btreemap(mem_id: u8, n: usize) {
// StableVec benchmarks

fn write_chunks_vec<const CHUNK_SIZE: usize>(mem_id: u8, n: usize) {
let vec: StableVec<BoundedVecN<CHUNK_SIZE>, _> =
StableVec::new(init_memory(mem_id)).expect("Vec::new failed");
let vec: StableVec<BoundedVecN<CHUNK_SIZE>, _> = 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<const CHUNK_SIZE: usize>(mem_id: u8, n: usize) {
write_chunks_vec::<CHUNK_SIZE>(mem_id, n);
let vec: StableVec<BoundedVecN<CHUNK_SIZE>, _> =
StableVec::init(init_memory(mem_id)).expect("Vec::init failed");
let vec: StableVec<BoundedVecN<CHUNK_SIZE>, _> = StableVec::init(init_memory(mem_id));

bench_fn(|| {
for i in 0..n as u64 {
Expand Down
8 changes: 4 additions & 4 deletions benchmarks/vec/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fn vec_insert_blob<const N: usize>() -> BenchResult {

fn vec_insert<T: Storable + Random>() -> BenchResult {
let num_items = 10_000;
let svec: StableVec<T, _> = StableVec::new(DefaultMemoryImpl::default()).unwrap();
let svec: StableVec<T, _> = StableVec::new(DefaultMemoryImpl::default());

let mut rng = Rng::from_seed(0);
let mut random_items = Vec::with_capacity(num_items);
Expand All @@ -102,7 +102,7 @@ fn vec_insert<T: Storable + Random>() -> BenchResult {

bench_fn(|| {
for item in random_items.iter() {
svec.push(item).unwrap();
svec.push(item);
}
})
}
Expand All @@ -118,12 +118,12 @@ fn vec_get_blob_mem_manager<const N: usize>() -> BenchResult {

fn vec_get<T: Storable + Random>(memory: impl Memory) -> BenchResult {
let num_items = 10_000;
let svec: StableVec<T, _> = StableVec::new(memory).unwrap();
let svec: StableVec<T, _> = 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(|| {
Expand Down
11 changes: 4 additions & 7 deletions examples/src/quick_start/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ fn stable_get(key: u128) -> Option<u128> {
// if it exists.
#[update]
fn stable_insert(key: u128, value: u128) -> Option<u128> {
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.
Expand All @@ -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
Expand Down Expand Up @@ -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<u128, u128, Memory> {
Expand All @@ -106,4 +104,3 @@ impl Default for State {
}
}
}

8 changes: 2 additions & 6 deletions examples/src/task_timer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
)
);
}

Expand All @@ -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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
33 changes: 18 additions & 15 deletions src/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,36 +96,39 @@ pub struct Cell<T: Storable, M: Memory> {

impl<T: Storable, M: Memory> Cell<T, M> {
/// Creates a new cell in the specified memory, overwriting the previous contents of the memory.
pub fn new(memory: M, value: T) -> Result<Self, ValueError> {
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<Self, InitError> {
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 {
last_supported_version: LAYOUT_VERSION,
decoded_version: header.version,
});
panic!(
"Failed to initialize cell: {}",
InitError::IncompatibleVersion {
last_supported_version: LAYOUT_VERSION,
decoded_version: header.version,
}
);
}

Ok(Self {
Self {
value: Self::read_value(&memory, header.value_length),
memory,
})
}
}

/// Reads and decodes the value of specified length.
Expand Down Expand Up @@ -169,9 +172,9 @@ impl<T: Storable, M: Memory> Cell<T, M> {
/// 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<T, ValueError> {
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");
Comment thread
maksymar marked this conversation as resolved.
std::mem::replace(&mut self.value, value)
}

/// Writes the value to the memory, growing the memory size if needed.
Expand Down
32 changes: 13 additions & 19 deletions src/cell/tests.rs
Original file line number Diff line number Diff line change
@@ -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<T: Default + Storable, M: Memory>(c: Cell<T, M>) -> Cell<T, M> {
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::<u8>::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()[..]);
}
34 changes: 20 additions & 14 deletions src/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,45 +183,51 @@ impl<T: Storable, INDEX: Memory, DATA: Memory> Log<T, INDEX, DATA> {
/// 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<Self, InitError> {
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 {
Comment thread
maksymar marked this conversation as resolved.
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);
Comment thread
maksymar marked this conversation as resolved.
}

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)]
{
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.
Expand Down
Loading