diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9eab5cd..fd1d2db9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: - name: Build Fuzzers run: | - cargo install cargo-fuzz + cargo +nightly install cargo-fuzz cargo +nightly fuzz build examples: diff --git a/src/base_vec.rs b/src/base_vec.rs index 5e6eeea2..183b89a3 100644 --- a/src/base_vec.rs +++ b/src/base_vec.rs @@ -157,6 +157,16 @@ impl BaseVec { self.memory } + /// Removes all items from the vector. + /// + /// The next `push` will write at slot 0, overwriting old data naturally. + /// Headers are preserved. + /// + /// Complexity: O(1) + pub fn clear(&self) { + self.set_len(0); + } + /// Returns true if the vector is empty. /// /// Complexity: O(1) diff --git a/src/log.rs b/src/log.rs index 44323c55..e6485f9c 100644 --- a/src/log.rs +++ b/src/log.rs @@ -300,6 +300,16 @@ impl Log { (self.index_memory, self.data_memory) } + /// Removes all entries from the log. + /// + /// After clearing, `len()` returns 0 and the next `append` writes at + /// data offset 0, overwriting old data naturally. Headers are preserved. + /// + /// Complexity: O(1) + pub fn clear(&self) { + write_u64(&self.index_memory, Address::from(HEADER_OFFSET), 0); + } + /// Returns true iff this log does not have any entries. pub fn is_empty(&self) -> bool { self.len() == 0 diff --git a/src/log/tests.rs b/src/log/tests.rs index 3b2df595..72fd3d8e 100644 --- a/src/log/tests.rs +++ b/src/log/tests.rs @@ -26,6 +26,56 @@ fn test_log_construct() { assert_eq!(log.index_size_bytes(), 40); } +#[test] +fn test_log_clear() { + let log = Log::, _, _>::new(VectorMemory::default(), VectorMemory::default()); + log.append(&b"DEADBEEF".to_vec()) + .expect("failed to append entry"); + log.append(&b"FEEDBAD".to_vec()) + .expect("failed to append entry"); + assert_eq!(log.len(), 2); + + log.clear(); + + assert_eq!(log.len(), 0); + assert!(log.is_empty()); + assert_eq!(log.get(0), None); + assert_eq!(log.iter().count(), 0); + assert_eq!(log.log_size_bytes(), 0); +} + +#[test] +fn test_log_clear_then_append() { + let log = Log::, _, _>::new(VectorMemory::default(), VectorMemory::default()); + log.append(&b"old_entry".to_vec()).unwrap(); + log.clear(); + + let idx = log.append(&b"new_entry".to_vec()).unwrap(); + assert_eq!(idx, 0); + assert_eq!(log.len(), 1); + assert_eq!(log.get(0).unwrap(), b"new_entry".to_vec()); +} + +#[test] +fn test_log_clear_empty() { + let log = Log::, _, _>::new(VectorMemory::default(), VectorMemory::default()); + log.clear(); + assert_eq!(log.len(), 0); + assert!(log.is_empty()); +} + +#[test] +fn test_log_clear_persistence() { + let log = Log::, _, _>::new(VectorMemory::default(), VectorMemory::default()); + log.append(&b"entry".to_vec()).unwrap(); + log.clear(); + + let (index_memory, data_memory) = log.into_memories(); + let log = Log::, _, _>::init(index_memory, data_memory); + assert_eq!(log.len(), 0); + assert!(log.is_empty()); +} + #[test] fn test_new_overwrites() { let log = Log::, _, _>::new(VectorMemory::default(), VectorMemory::default()); diff --git a/src/min_heap.rs b/src/min_heap.rs index e2f2c55c..da0dccb3 100644 --- a/src/min_heap.rs +++ b/src/min_heap.rs @@ -55,6 +55,13 @@ where self.0.len() } + /// Removes all items from the heap. + /// + /// Complexity: O(1) + pub fn clear(&mut self) { + self.0.clear(); + } + /// Returns true if the heap is empty. /// /// Complexity: O(1) diff --git a/src/min_heap/tests.rs b/src/min_heap/tests.rs index 1a59e3b2..3377f11f 100644 --- a/src/min_heap/tests.rs +++ b/src/min_heap/tests.rs @@ -92,6 +92,44 @@ impl Memory for EmptyMem { } } +#[test] +fn test_clear() { + let mut heap = StableMinHeap::::new(M::default()); + heap.push(&3); + heap.push(&1); + heap.push(&2); + assert_eq!(heap.len(), 3); + + heap.clear(); + + assert_eq!(heap.len(), 0); + assert!(heap.is_empty()); + assert_eq!(heap.peek(), None); + assert_eq!(heap.iter().count(), 0); +} + +#[test] +fn test_clear_then_push() { + let mut heap = StableMinHeap::::new(M::default()); + heap.push(&10); + heap.push(&5); + heap.clear(); + + heap.push(&42); + assert_eq!(heap.len(), 1); + assert_eq!(heap.peek(), Some(42)); + assert_eq!(heap.pop(), Some(42)); + assert!(heap.is_empty()); +} + +#[test] +fn test_clear_empty() { + let mut heap = StableMinHeap::::new(M::default()); + heap.clear(); + assert_eq!(heap.len(), 0); + assert!(heap.is_empty()); +} + #[test] #[should_panic(expected = "GrowFailed { current_size: 0, delta: 1 }")] fn test_failure_new() { diff --git a/src/vec.rs b/src/vec.rs index 5d88e02e..d2cb984f 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -43,6 +43,13 @@ impl Vec { self.0.into_memory() } + /// Removes all items from the vector. + /// + /// Complexity: O(1) + pub fn clear(&self) { + self.0.clear(); + } + /// Returns true if the vector is empty. /// /// Complexity: O(1) diff --git a/src/vec/tests.rs b/src/vec/tests.rs index 5e021e6f..cb6e676f 100644 --- a/src/vec/tests.rs +++ b/src/vec/tests.rs @@ -272,6 +272,54 @@ impl crate::Storable for BuggyStruct { }; } +#[test] +fn clear() { + let sv = StableVec::::new(M::default()); + sv.push(&1); + sv.push(&2); + sv.push(&3); + assert_eq!(sv.len(), 3); + + sv.clear(); + + assert_eq!(sv.len(), 0); + assert!(sv.is_empty()); + assert_eq!(sv.get(0), None); + assert_eq!(sv.iter().count(), 0); +} + +#[test] +fn clear_then_push() { + let sv = StableVec::::new(M::default()); + sv.push(&1); + sv.push(&2); + sv.clear(); + + sv.push(&42); + assert_eq!(sv.len(), 1); + assert_eq!(sv.get(0), Some(42)); +} + +#[test] +fn clear_empty() { + let sv = StableVec::::new(M::default()); + sv.clear(); + assert_eq!(sv.len(), 0); + assert!(sv.is_empty()); +} + +#[test] +fn clear_persistence() { + let sv = StableVec::::new(M::default()); + sv.push(&1); + sv.clear(); + + let mem = sv.into_memory(); + let sv = StableVec::::init(mem); + assert_eq!(sv.len(), 0); + assert!(sv.is_empty()); +} + #[test] #[should_panic(expected = "expected an element with length <= 1 bytes, but found 4")] fn push_element_bigger_than_max_size_panics() { diff --git a/tests/api_conformance.rs b/tests/api_conformance.rs index 3f81394c..925e6d9d 100644 --- a/tests/api_conformance.rs +++ b/tests/api_conformance.rs @@ -286,6 +286,16 @@ fn api_conformance_min_heap() { assert_eq!(stable.len(), 0); assert!(stable.is_empty()); assert!(std.is_empty()); + + // Clear. + for i in 0..n { + stable.push(&i); + std.push(Reverse(i)); + } + stable.clear(); + std.clear(); + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); } #[test] @@ -334,6 +344,16 @@ fn api_conformance_vec() { // After popping everything, both should be empty. assert_eq!(stable.len(), std.len() as u64); assert_eq!(stable.is_empty(), std.is_empty()); + + // Clear. + for i in 0..n { + stable.push(&i); + std.push(i); + } + stable.clear(); + std.clear(); + assert_eq!(stable.len(), std.len() as u64); + assert_eq!(stable.is_empty(), std.is_empty()); } #[test] @@ -391,4 +411,10 @@ fn api_conformance_log() { // Iteration let log_items: Vec<_> = log.iter().collect(); assert_eq!(log_items, std); + + // Clear. + log.clear(); + std.clear(); + assert_eq!(log.len(), std.len() as u64); + assert_eq!(log.is_empty(), std.is_empty()); }