diff --git a/src/allocator.rs b/src/allocator.rs index 0c82848..582750d 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -2,7 +2,30 @@ use std::mem::size_of; use zerocopy::{AsBytes, FromBytes, Unaligned}; -use crate::{Db, FilePointer, Header, PAGE_SIZE, U32}; +use crate::{Db, FilePointer, FileRange, Header, PAGE_SIZE, U32}; + +enum SlabKind { + SplitFreeList, + RelativeFreeList, + AbsoluteFreeList, +} + +impl SlabKind { + fn for_size(size: u32) -> Self { + if size == 1 { + Self::SplitFreeList + } else if size < 8 { + Self::RelativeFreeList + } else if (size as u64) <= PAGE_SIZE { + Self::AbsoluteFreeList + } else { + panic!("invalid size") + } + } +} + +// Best bitmap sizes in bytes for a 4096 Byte slab +// const BITMAP_SIZE: [u32; 8] = [456, 241, 164, 125, 100, 84, 72, 63]; #[derive(Clone, Copy, FromBytes, AsBytes, Unaligned)] #[repr(transparent)] @@ -22,43 +45,161 @@ impl FreeList { #[repr(C)] pub struct AllocatorState { pub general: FreeList, - pub slabs: FilePointer, + pub slabs: SlabListPointer, } #[derive(Clone, Copy, FromBytes, AsBytes, Unaligned)] #[repr(C)] -pub struct PoolListHeader { - next: FilePointer, - size: U32, +pub struct SlabListHeader { + next: SlabListPointer, len: U32, + size: U32, } -impl PoolListHeader { - fn capacity(&self) -> u32 { - (self.size.get() - size_of::() as u32) / size_of::() as u32 +#[derive(Clone, Copy, FromBytes, AsBytes, Unaligned)] +#[repr(transparent)] +pub struct SlabListPointer(pub FilePointer); + +impl SlabListHeader { + pub fn capacity(&self) -> u32 { + (self.size.get() - size_of::() as u32) / size_of::() as u32 } } -#[derive(Clone, Copy, FromBytes, AsBytes, Unaligned)] -#[repr(C)] -pub struct SizedFreeList { - element_size: U32, - head: FreeList, -} +impl SlabListPointer { + pub fn set_next(&self, db: &mut Db, next: SlabListPointer) { + db.write(self.0, next); + } -impl AllocatorState { - pub fn init(&self, db: &mut Db, size: U32) { + pub fn set_len(&self, db: &mut Db, len: u32) { + db.write(self.0 + size_of::() as u64, U32::from(len)); + } + + pub fn init(&self, db: &mut Db, size: u32) { db.write( - self.slabs, - PoolListHeader { - next: FilePointer::null(), - size, + self.0, + SlabListHeader { + next: SlabListPointer(FilePointer::null()), + size: size.into(), len: 0.into(), }, ); } - fn slabs_mut<'db>(&self, db: &'db mut Db) -> &'db mut PoolListHeader { - db.modify(self.slabs) + pub fn ptr(&self, db: &Db, i: u32) -> FilePointer { + let this: SlabListHeader = db.read(self.0); + assert!(i < this.len.get()); + self.0 + size_of::() as u64 + i as u64 * size_of::() as u64 + } + + pub fn write(&self, db: &mut Db, i: u32, value: Slab) { + let ptr = self.ptr(db, i); + db.write(ptr, value); + } + + pub fn get(&self, db: &Db, i: u32) -> SlabPointer { + let ptr = self.ptr(db, i); + SlabPointer(ptr) + } + + pub fn add(&self, db: &mut Db, slab_size: u32) -> SlabPointer { + let this: SlabListHeader = db.read(self.0); + let capacity = this.capacity(); + let SlabListHeader { mut next, len, .. } = this; + + if len.get() >= capacity { + if next.0 == FilePointer::null() { + next = SlabListPointer(db.add_pages(1)); + next.init(db, PAGE_SIZE as u32); + self.set_next(db, next); + } + + return next.add(db, slab_size); + } + + let len = len.get(); + self.set_len(db, len + 1); + self.write( + db, + len, + Slab { + head: FilePointer::null(), + size: slab_size.into(), + }, + ); + + SlabPointer(self.ptr(db, len)) + } +} + +#[derive(Clone, Copy, FromBytes, AsBytes, Unaligned)] +#[repr(C)] +pub struct Slab { + head: FilePointer, + size: U32, +} + +#[derive(Clone, Copy, FromBytes, AsBytes, Unaligned)] +#[repr(transparent)] +pub struct SlabPointer(FilePointer); + +impl SlabPointer { + pub fn read(&self, db: &Db) -> Slab { + db.read(self.0) + } + + pub fn get(&self, db: &mut Db) -> FileRange { + let Slab { head, size } = self.read(db); + + let size = size.get(); + + match SlabKind::for_size(size) { + SlabKind::SplitFreeList => todo!(), + SlabKind::RelativeFreeList => todo!(), + SlabKind::AbsoluteFreeList => { + let mut next = head; + + if next == FilePointer::null() { + next = self.allocate_page(db); + } + + let new_next = db.read(next); + + self.set_next(db, new_next); + + next.range(size as u64) + } + } + } + + pub fn set_next(&self, db: &mut Db, next: FilePointer) { + db.write(self.0, next); + } + + pub fn allocate_page(&self, db: &mut Db) -> FilePointer { + let Slab { head, size } = self.read(db); + + let size = size.get(); + + match SlabKind::for_size(size) { + SlabKind::SplitFreeList => todo!(), + SlabKind::RelativeFreeList => todo!(), + SlabKind::AbsoluteFreeList => { + let n = PAGE_SIZE / size as u64; + + let page = db.add_pages(1); + + let mut next = head; + for i in (0..n).rev() { + let current = page + i * size as u64; + db.write(current, next); + next = current; + } + + self.set_next(db, next); + + next + } + } } } diff --git a/src/atomic_arc.rs b/src/atomic_arc.rs index 471c7bf..22e0ae4 100644 --- a/src/atomic_arc.rs +++ b/src/atomic_arc.rs @@ -62,7 +62,7 @@ mod tests { let first = atomic_arc.get(); assert_eq!(*first, 1); - atomic_arc.swap(Arc::new(2)); + _ = atomic_arc.swap(Arc::new(2)); assert_eq!(*first, 1); diff --git a/src/lib.rs b/src/lib.rs index 65ac996..114e503 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ use std::{borrow::BorrowMut, collections::HashMap, fs::File, mem::size_of, ops:: mod allocator; mod atomic_arc; -use allocator::{AllocatorState, FreeList}; +use allocator::{AllocatorState, FreeList, SlabListPointer, SlabPointer}; use atomic_arc::AtomicArc; use memmap::{Mmap, MmapMut}; use zerocopy::{AsBytes, FromBytes, LayoutVerified, Unaligned, LE}; @@ -75,7 +75,7 @@ impl Default for Header { root: FilePointer::null(), allocator_state: AllocatorState { general: FreeList::empty(), - slabs: FilePointer::page(0) + size_of::
() as u64, + slabs: SlabListPointer(FilePointer::page(0) + size_of::
() as u64), }, } } @@ -172,7 +172,12 @@ impl<'t> TransactionHandle<'t> { } impl Db { - fn transaction(f: fn(TransactionHandle)) { + pub fn add_slab(&mut self, size: u32) -> SlabPointer { + let allocator_state = self.header.allocator_state; + allocator_state.slabs.add(self, size) + } + + fn transaction(f: impl FnOnce(TransactionHandle)) { // let handle = TransactionHandle {}; } @@ -259,12 +264,13 @@ impl Db { unsafe { Mmap::map(&self.file) }.unwrap() } - fn add_pages(&mut self, n: u64) { - self.file - .set_len(self.file.metadata().unwrap().len() + PAGE_SIZE * n) - .unwrap(); + fn add_pages(&mut self, n: u64) -> FilePointer { + let len = self.file.metadata().unwrap().len(); + self.file.set_len(len + PAGE_SIZE * n).unwrap(); self.remap(); + + FilePointer::null() + len } pub fn new(file: File) -> Self { @@ -302,7 +308,7 @@ impl Db { fn init_allocator(&mut self) { let allocator_state = self.header.allocator_state; - allocator_state.init( + allocator_state.slabs.init( self, (PAGE_SIZE - size_of::
() as u64).try_into().unwrap(), ); @@ -318,7 +324,19 @@ mod tests { #[test] fn it_works() { - let db = Db::new(tempfile::tempfile().unwrap()); + let mut db = Db::new(tempfile::tempfile().unwrap()); + + let slab = db.add_slab(16); + + for i in 1..520 { + let range = slab.get(&mut db); + let start = range.start.0.get(); + dbg!(start); + + let data = [0; 16].map(|_| i as u8); + + db.write_range(range, data); + } let mut child = std::process::Command::new("hexdump") .arg("-C")