diff --git a/src/allocator.rs b/src/allocator.rs index b90a0b7..34f28d8 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -48,33 +48,34 @@ pub struct FreeListBlock { size: u8, } +impl FilePointer { + pub fn next_ptr(self) -> FilePointer> { + FilePointer::new(self.into_raw()) + } + + pub fn size_start_ptr(self) -> FilePointer { + FilePointer::new(self.into_raw() + size_of::>() as u64) + } + + pub fn size_end_ptr(self) -> FilePointer { + FilePointer::new(self.into_raw() + size_of::() as u64) + } +} + impl GeneralPurposeAllocator { const SIZE_MASK: u8 = 0b1000_0000; - const MIN_ALLOCATION_SIZE: u64 = size_of::() as u64 + 1; - - pub(crate) fn next_ptr( - ptr: FilePointer, - ) -> FilePointer> { - FilePointer::new(ptr.into_raw()) - } - - fn first_byte_ptr(ptr: FilePointer) -> FilePointer { - FilePointer::new(ptr.into_raw() + size_of::() as u64) - } - - fn size_ptr(ptr: FilePointer) -> FilePointer { - FilePointer::new(ptr.into_raw() + size_of::() as u64) - } + const MIN_ALLOCATION_SIZE: u64 = size_of::() as u64; pub fn size(db: &Db, head: FilePointer) -> u64 { - let first_byte: u8 = unsafe { db.read(Self::first_byte_ptr(head)) }; + // println!("get size({head:?})"); + let first_byte: u8 = unsafe { db.read(head.size_start_ptr()) }; let size = if first_byte & Self::SIZE_MASK == 0 { // small size (can fit in 7bits) first_byte as u64 } else { // large size - unsafe { db.read::(Self::size_ptr(head)) }.get() + unsafe { db.read::(head.size_end_ptr()) }.get() }; Self::MIN_ALLOCATION_SIZE + size @@ -86,25 +87,25 @@ impl GeneralPurposeAllocator { if size <= (u8::MAX & !Self::SIZE_MASK) as u64 { // small size (can fit in 7bits) debug_assert_eq!(size as u8 & Self::SIZE_MASK, 0); - unsafe { db.write(Self::first_byte_ptr(head), size as u8) }; + unsafe { db.write(head.size_start_ptr(), size as u8) }; } else { unsafe { - db.write(Self::first_byte_ptr(head), Self::SIZE_MASK); - db.write::(Self::size_ptr(head), size.into()); + db.write(head.size_start_ptr(), Self::SIZE_MASK); + db.write::(head.size_end_ptr(), size.into()); } } } fn clear(db: &mut Db, ptr: FilePointer) -> RawFilePointer { unsafe { - db.write(Self::next_ptr(ptr), FilePointer::null()); - let first_byte: u8 = db.read(Self::first_byte_ptr(ptr)); + db.write(ptr.next_ptr(), FilePointer::null()); + let first_byte: u8 = db.read(ptr.size_start_ptr()); // clear first size byte - db.write(Self::first_byte_ptr(ptr), 0u8); + db.write(ptr.size_start_ptr(), 0); if first_byte & Self::SIZE_MASK != 0 { // larger block. clear full size field - db.write(Self::size_ptr(ptr), U64::from(0)); + db.write(ptr.size_end_ptr(), 0.into()); } } @@ -144,7 +145,7 @@ impl GeneralPurposeAllocator { // if the first element is replaced update the head pointer let mut prevprev = FilePointer::::null(); let mut prev = head; - let mut next: FilePointer = unsafe { db.read(Self::next_ptr(head)) }; + let mut next: FilePointer = unsafe { db.read(head.next_ptr()) }; let empty_list = next.is_null(); @@ -201,16 +202,16 @@ impl GeneralPurposeAllocator { Self::set_size(db, remainder, extra_space); // prev must be the current tail of the free list and the newly allocated space, being at the end of the file // must be the last element of the free list to keep it sorted. - unsafe { db.write(Self::next_ptr(prev), remainder) }; + unsafe { db.write(prev.next_ptr(), remainder) }; } else { - unsafe { db.write(Self::next_ptr(prev), FilePointer::::null()) }; + unsafe { db.write(prev.next_ptr(), FilePointer::::null()) }; } start } else { let start = next; - let nextnext = unsafe { db.read(Self::next_ptr(start)) }; + let nextnext = unsafe { db.read(start.next_ptr()) }; let extra_space = Self::size(db, start) - needed_size; @@ -224,13 +225,13 @@ impl GeneralPurposeAllocator { Self::set_size(db, remainder, extra_space); unsafe { - db.write(Self::next_ptr(prev), remainder); - db.write(Self::next_ptr(remainder), nextnext); + db.write(prev.next_ptr(), remainder); + db.write(remainder.next_ptr(), nextnext); } // println!("{:x?}", unsafe { db.read::<[u8; 9 + 8]>(remainder) }); } else { - unsafe { db.write(Self::next_ptr(prev), nextnext) }; + unsafe { db.write(prev.next_ptr(), nextnext) }; } start @@ -247,25 +248,23 @@ impl GeneralPurposeAllocator { let mut size = range.len().max(Self::MIN_ALLOCATION_SIZE); let mut start = FilePointer::::new(range.start); - let range = (); - let head = self.head_ptr; let mut prevprev = FilePointer::null(); let mut prev = head; - let mut next = unsafe { db.read(Self::next_ptr(head)) }; + let mut next = unsafe { db.read(head.next_ptr()) }; while !next.is_null() && next < start { prevprev = prev; prev = next; - next = unsafe { db.read(Self::next_ptr(next)) }; + next = unsafe { db.read(next.next_ptr()) }; } if start.into_raw() + size == next.into_raw() { // we can merge with the next range let nextlen = Self::size(db, next); - let nextnext = unsafe { db.read(Self::next_ptr(next)) }; + let nextnext = unsafe { db.read(next.next_ptr()) }; // println!("merging with next range {:?}", next.range(nextlen)); @@ -290,8 +289,8 @@ impl GeneralPurposeAllocator { } unsafe { - db.write(Self::next_ptr(prev), start); - db.write(Self::next_ptr(start), next); + db.write(prev.next_ptr(), start); + db.write(start.next_ptr(), next); Self::set_size(db, start, size) } } diff --git a/src/lib.rs b/src/lib.rs index 6f24c3d..612eada 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use std::{ collections::{BTreeMap, VecDeque}, fmt::Debug, fs::File, + hash::Hash, marker::PhantomData, mem::size_of, ops::Range, @@ -26,7 +27,7 @@ type U64 = zerocopy::byteorder::U64; type U32 = zerocopy::byteorder::U32; type U16 = zerocopy::byteorder::U16; -#[derive(FromBytes, FromZeroes, AsBytes, Unaligned, Hash)] +#[derive(FromBytes, FromZeroes, AsBytes, Unaligned)] #[repr(transparent)] pub struct FilePointer { inner: RawFilePointer, @@ -70,6 +71,12 @@ impl PartialEq for FilePointer { impl Eq for FilePointer {} +impl Hash for FilePointer { + fn hash(&self, state: &mut H) { + self.inner.hash(state); + } +} + impl FilePointer { fn from_range(range: FileRange) -> Self { assert_eq!(range.len(), size_of::() as u64); @@ -335,11 +342,11 @@ impl Db { } fn root_ptr() -> FilePointer> { - FilePointer::new(RawFilePointer((size_of::
() as u64).into())) + FilePointer::new(RawFilePointer(16.into())) } fn allocator_state_ptr() -> RawFilePointer { - RawFilePointer((size_of::
() as u64 + size_of::() as u64).into()) + RawFilePointer((size_of::
() as u64 - size_of::() as u64).into()) } fn general_purpose_allocator() -> GeneralPurposeAllocator { @@ -556,6 +563,10 @@ impl Db { // TODO: scrap the PAGE-wise allocation and make slab allocations allocations of the general allocator. pub fn allocate(&mut self, size: u64) -> FileRange { + // println!("allocate({size})"); + if size == 0 { + return RawFilePointer::null().range(0); + } if let Some(slab) = self.get_slab(size) { slab.alloc(self) } else { @@ -564,6 +575,9 @@ impl Db { } pub fn free(&mut self, range: FileRange) { + if range.len() == 0 { + return; + } if let Some(slab) = self.get_slab(range.len()) { slab.free(self, range) } else { @@ -612,7 +626,7 @@ mod tests { fn fragmentation(db: &mut Db, print: bool) -> usize { let allocator = Db::<()>::general_purpose_allocator(); - let mut next = unsafe { db.read(GeneralPurposeAllocator::next_ptr(allocator.head_ptr)) }; + let mut next = unsafe { db.read(allocator.head_ptr.next_ptr()) }; let mut n = 0; while !next.is_null() { @@ -620,7 +634,7 @@ mod tests { if print { println!("\x1b[34m[{n}]\x1b[m {:?}", next.into_raw().range(size)); } - next = unsafe { db.read(GeneralPurposeAllocator::next_ptr(next)) }; + next = unsafe { db.read(next.next_ptr()) }; n += 1; } @@ -739,7 +753,7 @@ mod tests { } #[test] - fn it_works() { + fn allocator() { let mut db = Db::<()>::create(tempfile::tempfile().unwrap(), &[4, 7, 16]); let mut ranges = Vec::new();