Skip to content
56 changes: 56 additions & 0 deletions src/btreemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ where
_phantom: PhantomData<(K, V)>,
}

#[derive(PartialEq, Debug)]
/// The packed header size must be <= ALLOCATOR_OFFSET.
struct BTreeHeader {
version: Version,
Expand Down Expand Up @@ -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)> {
Expand Down Expand Up @@ -3029,4 +3039,50 @@ mod test {
let btree: BTreeMap<T, T, _> = 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>, 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>, Blob<4>, _>::read_header(&mem);

BTreeMap::<Blob<4>, Blob<4>, _>::new(mem.clone());

let header_expected = BTreeMap::<Blob<4>, 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<String, String, _> = 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::<String, String, _>::read_header(&mem);

BTreeMap::<String, String, _>::new(mem.clone());

let header_expected = BTreeMap::<String, String, _>::read_header(&mem);

assert_eq!(header_actual, header_expected);
}
}
54 changes: 45 additions & 9 deletions src/btreemap/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub struct Allocator<M: Memory> {
}

#[repr(C, packed)]
#[derive(PartialEq, Debug)]
struct AllocatorHeader {
magic: [u8; 3],
version: u8,
Expand Down Expand Up @@ -76,24 +77,30 @@ impl<M: Memory> Allocator<M> {
/// |__________| |____ 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);
Expand Down Expand Up @@ -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();
Expand Down