diff --git a/Cargo.toml b/Cargo.toml index d1a297d..2b75c8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["codegen", "tools/create-data-file", "tools/dump-data-file"] [package] name = "data_bucket" -version = "0.2.4" +version = "0.2.5" edition = "2021" authors = ["Handy-caT"] license = "MIT" diff --git a/src/page/index/table_of_contents_page.rs b/src/page/index/table_of_contents_page.rs index b76e3f5..392dbb9 100644 --- a/src/page/index/table_of_contents_page.rs +++ b/src/page/index/table_of_contents_page.rs @@ -1,12 +1,11 @@ -use std::collections::BTreeMap; - -use data_bucket_codegen::Persistable; use rkyv::{Archive, Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::fmt::Debug; use crate::page::PageId; use crate::{align, Persistable, SizeMeasurable}; -#[derive(Archive, Clone, Deserialize, Debug, Serialize, Persistable)] +#[derive(Archive, Clone, Deserialize, Debug, Serialize)] pub struct TableOfContentsPage { records: BTreeMap, @@ -22,15 +21,66 @@ where Self { records: BTreeMap::new(), empty_pages: vec![], - estimated_size: usize::default().aligned_size() - + Option::::default().aligned_size(), + estimated_size: usize::default().aligned_size() + 12, + } + } +} + +#[derive(Archive, Clone, Deserialize, Debug, Serialize)] +struct TableOfContentsPagePersisted { + records: Vec<(T, PageId)>, + empty_pages: Vec, + estimated_size: usize, +} + +impl Persistable for TableOfContentsPage +where + T: Clone + + rkyv::Archive + + for<'a> rkyv::Serialize< + rkyv::rancor::Strategy< + rkyv::ser::Serializer< + rkyv::util::AlignedVec, + rkyv::ser::allocator::ArenaHandle<'a>, + rkyv::ser::sharing::Share, + >, + rkyv::rancor::Error, + >, + >, + ::Archived: + rkyv::Deserialize> + Ord, +{ + fn as_bytes(&self) -> impl AsRef<[u8]> { + let records = self + .records + .iter() + .map(|(k, v)| (k.clone(), *v)) + .collect::>(); + let model = TableOfContentsPagePersisted { + records, + empty_pages: self.empty_pages.clone(), + estimated_size: self.estimated_size, + }; + rkyv::to_bytes::(&model).unwrap() + } + fn from_bytes(bytes: &[u8]) -> Self { + let archived = unsafe { + rkyv::access_unchecked::< as Archive>::Archived>(bytes) + }; + let model: TableOfContentsPagePersisted = + rkyv::deserialize::<_, rkyv::rancor::Error>(archived).expect("data should be valid"); + let records = BTreeMap::from_iter(model.records); + Self { + records, + estimated_size: model.estimated_size, + empty_pages: model.empty_pages, } } } impl TableOfContentsPage where - T: Ord + Eq, + T: Debug + Ord + Eq, { pub fn estimated_size(&self) -> usize { self.estimated_size @@ -38,9 +88,9 @@ where pub fn insert(&mut self, val: T, page_id: PageId) where - T: SizeMeasurable, + T: SizeMeasurable + Clone, { - self.estimated_size += align(val.aligned_size() + page_id.0.aligned_size()); + self.estimated_size += (val.clone(), page_id).aligned_size(); let _ = self.records.insert(val, page_id); } @@ -85,12 +135,12 @@ where .expect("value should be available if remove is called") } - pub fn update_key(&mut self, old_key: &T, new_key: T) { - let id = self - .records - .remove(old_key) - .expect("value should be available if update is called"); - self.records.insert(new_key, id); + pub fn update_key(&mut self, old_key: &T, new_key: T) -> Option<()> { + if let Some(id) = self.records.remove(old_key) { + self.records.insert(new_key, id); + return Some(()); + } + None } pub fn contains(&self, val: &T) -> bool { @@ -113,3 +163,26 @@ where self.records.into_iter() } } + +#[cfg(test)] +mod test { + use crate::{Link, Persistable, TableOfContentsPage}; + + #[test] + fn test_sizes() { + let mut toc_page = TableOfContentsPage::<(u64, Link)>::default(); + assert_eq!(toc_page.as_bytes().as_ref().len(), toc_page.estimated_size); + toc_page.insert( + ( + 128, + Link { + page_id: 1.into(), + offset: 40, + length: 80, + }, + ), + 6.into(), + ); + assert_eq!(toc_page.as_bytes().as_ref().len(), toc_page.estimated_size); + } +} diff --git a/src/util/sized.rs b/src/util/sized.rs index f137dc8..e04c4b7 100644 --- a/src/util/sized.rs +++ b/src/util/sized.rs @@ -97,8 +97,32 @@ where T2: SizeMeasurable, { fn aligned_size(&self) -> usize { + if let Some(align) = T1::align() { + if align % 8 == 0 { + return align8(self.0.aligned_size() + self.1.aligned_size()); + } + } + if let Some(align) = T2::align() { + if align % 8 == 0 { + return align8(self.0.aligned_size() + self.1.aligned_size()); + } + } align(self.0.aligned_size() + self.1.aligned_size()) } + + fn align() -> Option { + if let Some(align) = T1::align() { + if align % 8 == 0 { + return Some(8); + } + } + if let Some(align) = T2::align() { + if align % 8 == 0 { + return Some(8); + } + } + None + } } // That was found on practice... Check unit test for proofs that works. @@ -207,7 +231,27 @@ where #[cfg(test)] mod test { use crate::util::sized::SizeMeasurable; - use crate::IndexValue; + use crate::{IndexValue, Link}; + use rkyv::to_bytes; + + #[test] + fn test_tuple() { + let t = (u64::MAX, Link::default()); + assert_eq!( + t.aligned_size(), + to_bytes::(&t).unwrap().len() + ); + let t = (u32::MAX, Link::default()); + assert_eq!( + t.aligned_size(), + to_bytes::(&t).unwrap().len() + ); + let t = (u8::MAX, Link::default()); + assert_eq!( + t.aligned_size(), + to_bytes::(&t).unwrap().len() + ) + } #[test] fn test_string() {