basic layout of allocator

This commit is contained in:
soruh 2023-07-25 03:13:28 +02:00
parent c762d6b18f
commit 2f3c46136b
3 changed files with 191 additions and 32 deletions

View File

@ -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::<PoolListHeader>() as u32) / size_of::<SizedFreeList>() 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::<SlabListHeader>() as u32) / size_of::<Slab>() 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::<SlabListPointer>() 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::<SlabListHeader>() as u64 + i as u64 * size_of::<Slab>() 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
}
}
}
}

View File

@ -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);

View File

@ -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::<Header>() as u64,
slabs: SlabListPointer(FilePointer::page(0) + size_of::<Header>() 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::<Header>() 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")