vecs: part 5

This commit is contained in:
nym21
2025-07-22 21:26:50 +02:00
parent e5ab4dafc0
commit 3ac9c2d95e
5 changed files with 148 additions and 70 deletions

View File

@@ -8,7 +8,6 @@ use super::{PAGE_SIZE, Region, Regions};
#[derive(Debug)] #[derive(Debug)]
pub struct Layout { pub struct Layout {
start_to_index: BTreeMap<u64, usize>, start_to_index: BTreeMap<u64, usize>,
/// key: start, value: gap
start_to_hole: BTreeMap<u64, u64>, start_to_hole: BTreeMap<u64, u64>,
} }
@@ -21,7 +20,7 @@ impl From<&Regions> for Layout {
value value
.as_array() .as_array()
.into_iter() .iter()
.enumerate() .enumerate()
.flat_map(|(index, opt)| opt.as_ref().map(|region| (index, region))) .flat_map(|(index, opt)| opt.as_ref().map(|region| (index, region)))
.for_each(|(index, region)| { .for_each(|(index, region)| {
@@ -43,24 +42,34 @@ impl From<&Regions> for Layout {
} }
impl Layout { impl Layout {
pub fn get_last_region(&self) -> Option<usize> { pub fn get_last_region(&self) -> Option<(u64, usize)> {
self.start_to_index self.start_to_index
.last_key_value() .last_key_value()
.map(|(_, index)| *index) .map(|(start, index)| (*start, *index))
} }
pub fn find_smallest_adequate_hole(&self, reserved: u64) -> Option<u64> { pub fn get_last_region_index(&self) -> Option<usize> {
self.start_to_hole self.get_last_region().map(|(_, index)| index)
.iter() }
.filter(|(_, gap)| **gap >= reserved)
.map(|(start, gap)| (gap, start)) pub fn is_last_region(&self, index: usize) -> bool {
.collect::<BTreeMap<_, _>>() let last = self.get_last_region();
.pop_first() let is_last = last.is_some_and(|(_, other_index)| index == other_index);
.map(|(_, s)| *s) if is_last {
debug_assert!(self.start_to_hole.range(last.unwrap().0..).next().is_none());
}
is_last
} }
pub fn insert_region(&mut self, start: u64, index: usize) { pub fn insert_region(&mut self, start: u64, index: usize) {
assert!(self.start_to_index.insert(start, index).is_none()) debug_assert!(self.start_to_index.insert(start, index).is_none())
// TODO: Other checks related to holes ?
}
pub fn move_region(&mut self, start: u64, index: usize, region: &Region) -> Result<()> {
self.remove_region(index, region)?;
self.insert_region(start, index);
Ok(())
} }
pub fn remove_region(&mut self, index: usize, region: &Region) -> Result<()> { pub fn remove_region(&mut self, index: usize, region: &Region) -> Result<()> {
@@ -107,6 +116,16 @@ impl Layout {
self.start_to_hole.get(&start).copied() self.start_to_hole.get(&start).copied()
} }
pub fn find_smallest_adequate_hole(&self, reserved: u64) -> Option<u64> {
self.start_to_hole
.iter()
.filter(|(_, gap)| **gap >= reserved)
.map(|(start, gap)| (gap, start))
.collect::<BTreeMap<_, _>>()
.pop_first()
.map(|(_, s)| *s)
}
pub fn remove_or_compress_hole_to_right(&mut self, start: u64, compress_by: u64) { pub fn remove_or_compress_hole_to_right(&mut self, start: u64, compress_by: u64) {
if let Some(gap) = self.start_to_hole.remove(&start) if let Some(gap) = self.start_to_hole.remove(&start)
&& gap != compress_by && gap != compress_by
@@ -121,14 +140,14 @@ impl Layout {
} }
fn widen_hole_to_the_left_if_any(&mut self, start: u64, widen_by: u64) -> Option<u64> { fn widen_hole_to_the_left_if_any(&mut self, start: u64, widen_by: u64) -> Option<u64> {
assert!(start % PAGE_SIZE == 0); debug_assert!(start % PAGE_SIZE == 0);
if widen_by > start { if widen_by > start {
panic!("Hole too small") panic!("Hole too small")
} }
let gap = self.start_to_hole.remove(&start)?; let gap = self.start_to_hole.remove(&start)?;
assert!(widen_by % PAGE_SIZE == 0); debug_assert!(widen_by % PAGE_SIZE == 0);
let start = start - widen_by; let start = start - widen_by;
let gap = gap + widen_by; let gap = gap + widen_by;
@@ -137,17 +156,17 @@ impl Layout {
{ {
*prev_gap += gap; *prev_gap += gap;
} else { } else {
assert!(self.start_to_hole.insert(start, gap).is_none()); debug_assert!(self.start_to_hole.insert(start, gap).is_none());
} }
Some(start) Some(start)
} }
fn widen_hole_to_the_right_if_any(&mut self, start: u64, widen_by: u64) -> Option<u64> { fn widen_hole_to_the_right_if_any(&mut self, start: u64, widen_by: u64) -> Option<u64> {
assert!(start % PAGE_SIZE == 0); debug_assert!(start % PAGE_SIZE == 0);
let gap = self.start_to_hole.get_mut(&start)?; let gap = self.start_to_hole.get_mut(&start)?;
assert!(widen_by % PAGE_SIZE == 0); debug_assert!(widen_by % PAGE_SIZE == 0);
*gap += widen_by; *gap += widen_by;
let next_hole_start = start + *gap; let next_hole_start = start + *gap;

View File

@@ -21,6 +21,7 @@ use region::*;
use regions::*; use regions::*;
pub const PAGE_SIZE: u64 = 4096; pub const PAGE_SIZE: u64 = 4096;
pub const PAGE_SIZE_MINUS_1: u64 = PAGE_SIZE - 1;
pub struct File { pub struct File {
regions: RwLock<Regions>, regions: RwLock<Regions>,
@@ -53,9 +54,9 @@ impl File {
}) })
} }
/// len % PAGE_SIZE == 0
pub fn set_min_len(&self, len: u64) -> Result<()> { pub fn set_min_len(&self, len: u64) -> Result<()> {
assert!(len % PAGE_SIZE == 0); let len = Self::ceil_number_to_page_size_multiple(len);
if self.file.read().metadata()?.len() < len { if self.file.read().metadata()?.len() < len {
let mut mmap = self.mmap.write(); let mut mmap = self.mmap.write();
let file = self.file.write(); let file = self.file.write();
@@ -67,6 +68,13 @@ impl File {
} }
} }
pub fn set_min_regions(&self, regions: usize) -> Result<()> {
self.regions
.write()
.set_min_len((regions * SIZE_OF_REGION) as u64)?;
self.set_min_len(regions as u64 * PAGE_SIZE)
}
pub fn get_or_create(&self, id: String) -> Result<usize> { pub fn get_or_create(&self, id: String) -> Result<usize> {
if let Some(index) = self.regions.read().get_region_index_from_id(id.clone()) { if let Some(index) = self.regions.read().get_region_index_from_id(id.clone()) {
return Ok(index); return Ok(index);
@@ -79,7 +87,7 @@ impl File {
start start
} else { } else {
let start = layout let start = layout
.get_last_region() .get_last_region_index()
.map(|index| { .map(|index| {
let region_opt = regions.get_region_from_index(index); let region_opt = regions.get_region_from_index(index);
let region = region_opt.as_ref().unwrap().read(); let region = region_opt.as_ref().unwrap().read();
@@ -121,88 +129,126 @@ impl File {
self.write_all_at_(region, data, Some(at)) self.write_all_at_(region, data, Some(at))
} }
fn write_all_at_(&mut self, region: usize, data: &[u8], at: Option<u64>) -> Result<()> { fn write_all_at_(&mut self, region_index: usize, data: &[u8], at: Option<u64>) -> Result<()> {
let Some(region) = self.regions.read().get_region_from_index(region) else { let Some(region) = self.regions.read().get_region_from_index(region_index) else {
return Err(Error::Str("Unknown region")); return Err(Error::Str("Unknown region"));
}; };
let region_lock = region.read(); let region_lock = region.read();
let start = region_lock.start(); let start = region_lock.start();
let reserved = region_lock.reserved(); let reserved = region_lock.reserved();
let left = region_lock.left(); let left = region_lock.left();
let len = region_lock.len();
let end = start + len;
let data_len = data.len() as u64; let data_len = data.len() as u64;
drop(region_lock); drop(region_lock);
let new_left = at.map_or_else(|| left, |at| reserved - (at - start)); let new_left = at.map_or_else(|| left, |at| reserved - (at - start));
let new_len = reserved - new_left; let new_len = reserved - new_left;
let write_start = at.unwrap_or(start + len);
// Write to reserved space if possible // Write to reserved space if possible
if new_left >= data_len { if new_left >= data_len {
Self::write_to_mmap(&self.mmap.read(), at.unwrap_or(start), data); self.write(write_start, data);
let regions = self.regions.read();
let mut region_lock = region.write(); let mut region_lock = region.write();
region_lock.set_len(new_len); region_lock.set_len(new_len);
regions.write_to_mmap(&region_lock, region_index);
// TODO: Flush layout
return Ok(()); return Ok(());
} }
let mut layout_lock = self.layout.write(); let mut layout_lock = self.layout.write();
let hole_start = start + reserved; let new_len = len + data_len;
let hole = layout_lock.get_hole(hole_start); debug_assert!(new_len > reserved);
let mut new_reserved = reserved;
while new_len < new_reserved {
new_reserved *= 2;
}
let added_reserve = new_reserved - reserved;
// Expand region to the right if possible // If is last continue writing
if hole.is_some_and(|gap| gap >= reserved) { if layout_lock.is_last_region(region_index) {
Self::write_to_mmap(&self.mmap.read(), at.unwrap_or(start), data); self.set_min_len(start + new_reserved)?;
layout_lock.remove_or_compress_hole_to_right(hole_start, reserved); self.write(write_start, data);
drop(layout_lock);
let regions = self.regions.read();
let mut region_lock = region.write(); let mut region_lock = region.write();
region_lock.set_len(new_len); region_lock.set_len(new_len);
region_lock.set_reserved(reserved * 2); region_lock.set_reserved(new_reserved);
regions.write_to_mmap(&region_lock, region_index);
// TODO: Flush layout
return Ok(()); return Ok(());
} }
let reserved = reserved * 2; // Expand region to the right if gap is wide enough
let hole_start = start + reserved;
let gap = layout_lock.get_hole(hole_start);
if gap.is_some_and(|gap| gap >= added_reserve) {
self.write(write_start, data);
// Find hole big enough to move the current region or the next region depending on which is smaller to if possible layout_lock.remove_or_compress_hole_to_right(hole_start, added_reserve);
if let Some(hole_start) = layout_lock.find_smallest_adequate_hole(reserved) {
layout_lock.remove_or_compress_hole_to_right(hole_start, reserved);
// TODO: Before every drop of layout.write flush to disk
drop(layout_lock); drop(layout_lock);
// write let regions = self.regions.read();
Self::write_to_mmap(&self.mmap.read(), at.unwrap_or(start), data);
let mut region_lock = region.write(); let mut region_lock = region.write();
region_lock.set_len(new_len);
region_lock.set_reserved(new_reserved);
regions.write_to_mmap(&region_lock, region_index);
return Ok(());
}
// Find hole big enough to move the region
if let Some(hole_start) = layout_lock.find_smallest_adequate_hole(new_reserved) {
self.write(hole_start, &self.mmap.read()[start as usize..end as usize]);
self.write(hole_start + len, data);
let regions = self.regions.read();
let mut region_lock = region.write();
layout_lock.remove_or_compress_hole_to_right(hole_start, new_reserved);
layout_lock.move_region(hole_start, region_index, &region_lock)?;
region_lock.set_start(hole_start); region_lock.set_start(hole_start);
region_lock.set_len(new_len); region_lock.set_len(new_len);
region_lock.set_reserved(reserved * 2); region_lock.set_reserved(new_reserved);
regions.write_to_mmap(&region_lock, region_index);
// TODO: create hole in prev position drop(layout_lock);
Self::write_to_mmap(&self.mmap.read(), at.unwrap_or(start), data); self.punch_hole(start, reserved)?;
// TODO: Flush layout
return Ok(()); return Ok(());
} }
// copy region to new position then lock and update region meta then remove // Write at the end
let regions = self.regions.read();
let mut region_lock = region.write();
let (last_region_start, last_region_index) = layout_lock.get_last_region().unwrap();
let new_start = last_region_start
+ regions
.get_region_from_index(last_region_index)
.unwrap()
.read()
.reserved();
self.set_min_len(new_start + new_reserved)?;
// let old_length = region_lock.len(); self.write(new_start, &self.mmap.read()[start as usize..end as usize]);
// let new_length = old_length + data_len as u64; self.write(new_start + len, data);
// self.layout.ho region_lock.set_start(new_start);
region_lock.set_len(new_len);
region_lock.set_reserved(new_reserved);
regions.write_to_mmap(&region_lock, region_index);
todo!(); self.punch_hole(start, reserved)?;
Ok(()) Ok(())
} }
fn write_to_mmap(mmap: &MmapMut, start: u64, data: &[u8]) { fn write(&self, start: u64, data: &[u8]) {
let mmap = self.mmap.read();
let data_len = data.len(); let data_len = data.len();
let start = start as usize; let start = start as usize;
let end = start + data_len; let end = start + data_len;
@@ -223,6 +269,7 @@ impl File {
let mut region_ = region.write(); let mut region_ = region.write();
let start = region_.start(); let start = region_.start();
let len = region_.len(); let len = region_.len();
let reserved = region_.reserved();
if from <= start { if from <= start {
return Err(Error::Str("Truncating too much")); return Err(Error::Str("Truncating too much"));
@@ -232,10 +279,14 @@ impl File {
region_.set_len(from); region_.set_len(from);
// TODO: Widen hole if present and needed (if truncating a big portion) let end = start + reserved;
// Not needed in BRK and with hole punching it's not a big deal but good to have nonetheless let start = Self::ceil_number_to_page_size_multiple(from);
if start > end {
self.punch_hole(from, region_.left()) unreachable!("Should not be possible");
} else if start < end {
self.punch_hole(start, end - start)?;
}
Ok(())
} }
pub fn remove(&self, index: usize) -> Result<Option<Arc<RwLock<Region>>>> { pub fn remove(&self, index: usize) -> Result<Option<Arc<RwLock<Region>>>> {
@@ -284,6 +335,10 @@ impl File {
Ok(()) Ok(())
} }
fn ceil_number_to_page_size_multiple(num: u64) -> u64 {
(num + PAGE_SIZE_MINUS_1) & !PAGE_SIZE_MINUS_1
}
} }
#[repr(C)] #[repr(C)]

View File

@@ -17,7 +17,7 @@ impl<'a> Reader<'a> {
} }
pub fn read(&self, offset: u64, len: u64) -> &[u8] { pub fn read(&self, offset: u64, len: u64) -> &[u8] {
assert!(offset + len < self.region.len()); debug_assert!(offset + len < self.region.len());
let start = self.region.start() + offset; let start = self.region.start() + offset;
let end = start + len; let end = start + len;
&self.mmap[start as usize..end as usize] &self.mmap[start as usize..end as usize]

View File

@@ -16,10 +16,10 @@ pub const SIZE_OF_REGION: usize = size_of::<Region>();
impl Region { impl Region {
pub fn new(start: u64, length: u64, reserved: u64) -> Self { pub fn new(start: u64, length: u64, reserved: u64) -> Self {
assert!(reserved > 0); debug_assert!(reserved > 0);
assert!(start % PAGE_SIZE == 0); debug_assert!(start % PAGE_SIZE == 0);
assert!(reserved % PAGE_SIZE == 0); debug_assert!(reserved % PAGE_SIZE == 0);
assert!(length <= reserved); debug_assert!(length <= reserved);
Self { Self {
start, start,
@@ -33,7 +33,7 @@ impl Region {
} }
pub fn set_start(&mut self, start: u64) { pub fn set_start(&mut self, start: u64) {
assert!(start % PAGE_SIZE == 0); debug_assert!(start % PAGE_SIZE == 0);
self.start = start self.start = start
} }

View File

@@ -79,6 +79,14 @@ impl Regions {
}) })
} }
pub fn set_min_len(&mut self, len: u64) -> Result<()> {
if self.index_to_region_mmap.len() < len as usize {
self.index_to_region_file.set_len(len)?;
self.index_to_region_mmap = unsafe { MmapMut::map_mut(&self.index_to_region_file)? };
}
Ok(())
}
pub fn create_region(&mut self, id: String, start: u64) -> Result<usize> { pub fn create_region(&mut self, id: String, start: u64) -> Result<usize> {
let index = self let index = self
.index_to_region .index_to_region
@@ -93,11 +101,7 @@ impl Regions {
self.index_to_region self.index_to_region
.push(Some(Arc::new(RwLock::new(region.clone())))); .push(Some(Arc::new(RwLock::new(region.clone()))));
let end = index * SIZE_OF_REGION + SIZE_OF_REGION; self.set_min_len(((index + 1) * SIZE_OF_REGION) as u64)?;
if self.index_to_region_mmap.len() < end {
self.index_to_region_file.set_len(end as u64);
self.index_to_region_mmap = unsafe { MmapMut::map_mut(&self.index_to_region_file)? };
}
self.write_to_mmap(&region, index); self.write_to_mmap(&region, index);
@@ -150,7 +154,7 @@ impl Regions {
&self.index_to_region &self.index_to_region
} }
fn write_to_mmap(&self, region: &Region, index: usize) { pub fn write_to_mmap(&self, region: &Region, index: usize) {
let start = index * SIZE_OF_REGION; let start = index * SIZE_OF_REGION;
let end = start + SIZE_OF_REGION; let end = start + SIZE_OF_REGION;
let mmap = &self.index_to_region_mmap; let mmap = &self.index_to_region_mmap;