diff --git a/src/btreemap.rs b/src/btreemap.rs index e2a38413..f4ca151c 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -107,6 +107,7 @@ where _phantom: PhantomData<(K, V)>, } +#[derive(PartialEq, Debug)] /// The packed header size must be <= ALLOCATOR_OFFSET. struct BTreeHeader { version: Version, @@ -556,11 +557,20 @@ where } /// Removes all elements from the map. + #[deprecated(since = "0.6.3", note = "please use `clear_new` instead")] pub fn clear(self) -> Self { let mem = self.allocator.into_memory(); Self::new(mem) } + /// Removes all elements from the map. + pub fn clear_new(&mut self) { + self.root_addr = NULL; + self.length = 0; + self.allocator.clear(); + self.save(); + } + /// Returns the first key-value pair in the map. The key in this /// pair is the minimum key in the map. pub fn first_key_value(&self) -> Option<(K, V)> { @@ -3029,4 +3039,50 @@ mod test { let btree: BTreeMap = BTreeMap::init(btree.into_memory()); assert_eq!(btree.get(&T), Some(T)); } + + #[test] + fn test_clear_new_bounded_type() { + let mem = make_memory(); + let mut btree: BTreeMap, Blob<4>, _> = BTreeMap::new(mem.clone()); + + btree.insert( + [1u8; 4].as_slice().try_into().unwrap(), + [1u8; 4].as_slice().try_into().unwrap(), + ); + + assert_ne!(btree.len(), 0); + assert_ne!(btree.allocator.num_allocated_chunks(), 0); + assert_ne!(btree.root_addr, NULL); + + btree.clear_new(); + + let header_actual = BTreeMap::, Blob<4>, _>::read_header(&mem); + + BTreeMap::, Blob<4>, _>::new(mem.clone()); + + let header_expected = BTreeMap::, Blob<4>, _>::read_header(&mem); + + assert_eq!(header_actual, header_expected); + } + + #[test] + fn test_clear_new_unbounded_type() { + let mem = make_memory(); + let mut btree: BTreeMap = BTreeMap::new(mem.clone()); + btree.insert("asd".into(), "bce".into()); + + assert_ne!(btree.len(), 0); + assert_ne!(btree.allocator.num_allocated_chunks(), 0); + assert_ne!(btree.root_addr, NULL); + + btree.clear_new(); + + let header_actual = BTreeMap::::read_header(&mem); + + BTreeMap::::new(mem.clone()); + + let header_expected = BTreeMap::::read_header(&mem); + + assert_eq!(header_actual, header_expected); + } } diff --git a/src/btreemap/allocator.rs b/src/btreemap/allocator.rs index 957441b0..68544599 100644 --- a/src/btreemap/allocator.rs +++ b/src/btreemap/allocator.rs @@ -46,6 +46,7 @@ pub struct Allocator { } #[repr(C, packed)] +#[derive(PartialEq, Debug)] struct AllocatorHeader { magic: [u8; 3], version: u8, @@ -76,24 +77,30 @@ impl Allocator { /// |__________| |____ NULL /// pub fn new(memory: M, addr: Address, allocation_size: Bytes) -> Self { - let free_list_head = addr + AllocatorHeader::size(); - - // Create the initial memory chunk and save it directly after the allocator's header. - let chunk = ChunkHeader::null(); - chunk.save(free_list_head, &memory); - - let allocator = Self { + let mut allocator = Self { header_addr: addr, allocation_size, num_allocated_chunks: 0, - free_list_head, + free_list_head: NULL, // Will be set in `allocator.clear()` below. memory, }; - allocator.save(); + allocator.clear(); allocator } + /// Deallocate all allocated chunks. + pub fn clear(&mut self) { + // Create the initial memory chunk and save it directly after the allocator's header. + self.free_list_head = self.header_addr + AllocatorHeader::size(); + let chunk = ChunkHeader::null(); + chunk.save(self.free_list_head, &self.memory); + + self.num_allocated_chunks = 0; + + self.save() + } + /// Load an allocator from memory at the given `addr`. pub fn load(memory: M, addr: Address) -> Self { let header: AllocatorHeader = read_struct(addr, &memory); @@ -401,6 +408,35 @@ mod test { assert_eq!(allocator.num_allocated_chunks, 0); } + #[test] + fn clear_deallocates_all_allocated_chunks() { + let mem = make_memory(); + let allocation_size = Bytes::from(16u64); + let allocator_addr = Address::from(0); + + let mut allocator = Allocator::new(mem.clone(), allocator_addr, allocation_size); + + allocator.allocate(); + allocator.allocate(); + + assert_eq!( + allocator.free_list_head, + allocator_addr + + AllocatorHeader::size() + + allocator.chunk_size() + + allocator.chunk_size() + ); + allocator.clear(); + + let header_actual: AllocatorHeader = read_struct(allocator_addr, &mem); + + Allocator::new(mem.clone(), allocator_addr, allocation_size); + + let header_expected: AllocatorHeader = read_struct(allocator_addr, &mem); + + assert_eq!(header_actual, header_expected); + } + #[test] fn allocate_deallocate_2() { let mem = make_memory();