diff --git a/src/engine/io/page_formats/item_id_data.rs b/src/engine/io/page_formats/item_id_data.rs index 1b67041..c311083 100644 --- a/src/engine/io/page_formats/item_id_data.rs +++ b/src/engine/io/page_formats/item_id_data.rs @@ -1,6 +1,9 @@ //! Pointer type to indicate where an item is inside a page //! See here for doc: https://www.postgresql.org/docs/current/storage-page-layout.html -use crate::engine::io::ConstEncodedSize; +use crate::engine::io::{ + format_traits::{Parseable, Serializable}, + ConstEncodedSize, +}; use super::{UInt12, UInt12Error}; use bytes::{Buf, BufMut}; @@ -23,20 +26,25 @@ impl ItemIdData { let length_usize = self.length.to_u16() as usize; offset_usize..(offset_usize + length_usize) } +} - pub fn serialize(&self, buffer: &mut impl BufMut) { - UInt12::serialize_packed(buffer, &[self.offset, self.length]); +impl ConstEncodedSize for ItemIdData { + fn encoded_size() -> usize { + 3 } +} - pub fn parse(buffer: &mut impl Buf) -> Result { +impl Parseable for ItemIdData { + type Output = Self; + fn parse(buffer: &mut impl Buf) -> Result { let items = UInt12::parse_packed(buffer, 2)?; Ok(ItemIdData::new(items[0], items[1])) } } -impl ConstEncodedSize for ItemIdData { - fn encoded_size() -> usize { - 3 +impl Serializable for ItemIdData { + fn serialize(&self, buffer: &mut impl BufMut) { + UInt12::serialize_packed(buffer, &[self.offset, self.length]); } } @@ -47,3 +55,27 @@ pub enum ItemIdDataError { #[error(transparent)] UInt12Error(#[from] UInt12Error), } + +#[cfg(test)] +mod tests { + use bytes::BytesMut; + + use crate::constants::PAGE_SIZE; + + use super::*; + + #[test] + fn test_roundtrip() -> Result<(), Box> { + let iid = ItemIdData::new(UInt12::new(1)?, UInt12::new(2)?); + + let mut buffer = BytesMut::with_capacity(PAGE_SIZE as usize); + iid.serialize(&mut buffer); + + let mut buffer = buffer.freeze(); + let result = ItemIdData::parse(&mut buffer)?; + + assert_eq!(iid, result); + + Ok(()) + } +} diff --git a/src/engine/io/page_formats/page_data.rs b/src/engine/io/page_formats/page_data.rs index c660311..c1d297e 100644 --- a/src/engine/io/page_formats/page_data.rs +++ b/src/engine/io/page_formats/page_data.rs @@ -1,3 +1,4 @@ +use crate::engine::io::format_traits::{Parseable, Serializable}; use crate::engine::io::{ConstEncodedSize, EncodedSize}; use crate::engine::objects::SqlTuple; use crate::engine::transactions::TransactionId; @@ -95,20 +96,6 @@ impl PageData { } } - pub fn serialize(&self, buffer: &mut impl BufMut) { - self.page_header.serialize(buffer); - - //Now write items data in order - self.item_ids.iter().for_each(|f| f.serialize(buffer)); - - //Fill the free space - let free_space = vec![0; self.page_header.get_free_space()]; - buffer.put_slice(&free_space); - - //Write items in reverse order - self.rows.iter().rev().for_each(|r| r.serialize(buffer)); - } - pub fn parse( table: Arc, page: PageOffset, @@ -141,8 +128,22 @@ impl PageData { rows, }) } +} + +impl Serializable for PageData { + fn serialize(&self, buffer: &mut impl BufMut) { + self.page_header.serialize(buffer); - //Todo implement updates, just unsure if it should be here + //Now write items data in order + self.item_ids.iter().for_each(|f| f.serialize(buffer)); + + //Fill the free space + let free_space = vec![0; self.page_header.get_free_space()]; + buffer.put_slice(&free_space); + + //Write items in reverse order + self.rows.iter().rev().for_each(|r| r.serialize(buffer)); + } } #[derive(Debug, Error)] diff --git a/src/engine/io/page_formats/page_header.rs b/src/engine/io/page_formats/page_header.rs index 15de859..b53faf6 100644 --- a/src/engine/io/page_formats/page_header.rs +++ b/src/engine/io/page_formats/page_header.rs @@ -1,6 +1,6 @@ //! See https://www.postgresql.org/docs/current/storage-page-layout.html for reference documentation //! I'm only implementing enough for my needs until proven otherwise -use crate::engine::io::ConstEncodedSize; +use crate::engine::io::{format_traits::Parseable, ConstEncodedSize}; use super::{ItemIdData, UInt12, UInt12Error}; use bytes::{Buf, BufMut}; @@ -59,14 +59,6 @@ impl PageHeader { pub fn serialize(&self, buffer: &mut impl BufMut) { UInt12::serialize_packed(buffer, &[self.pd_lower, self.pd_upper]); } - - pub fn parse(buffer: &mut impl Buf) -> Result { - let items = UInt12::parse_packed(buffer, 2)?; - Ok(PageHeader { - pd_lower: items[0], - pd_upper: items[1], - }) - } } impl Default for PageHeader { @@ -81,6 +73,17 @@ impl ConstEncodedSize for PageHeader { } } +impl Parseable for PageHeader { + type Output = Self; + fn parse(buffer: &mut impl Buf) -> Result { + let items = UInt12::parse_packed(buffer, 2)?; + Ok(PageHeader { + pd_lower: items[0], + pd_upper: items[1], + }) + } +} + #[derive(Debug, Error)] pub enum PageHeaderError { #[error("Not enough space to add")] diff --git a/src/engine/io/page_formats/page_offset.rs b/src/engine/io/page_formats/page_offset.rs index 13a4939..fa9a014 100644 --- a/src/engine/io/page_formats/page_offset.rs +++ b/src/engine/io/page_formats/page_offset.rs @@ -1,6 +1,9 @@ use crate::{ constants::{PAGES_PER_FILE, PAGE_SIZE}, - engine::io::ConstEncodedSize, + engine::io::{ + format_traits::{Parseable, Serializable}, + ConstEncodedSize, + }, }; use bytes::{Buf, BufMut}; use std::{ @@ -9,6 +12,7 @@ use std::{ mem::size_of, num::TryFromIntError, ops::{Add, AddAssign, Mul}, + process::Output, }; use thiserror::Error; @@ -16,26 +20,6 @@ use thiserror::Error; pub struct PageOffset(pub usize); impl PageOffset { - pub fn serialize(&self, buffer: &mut impl BufMut) { - //TODO switch this and other serialize calls to usize based once my pull request is accepted - //https://github.com/tokio-rs/bytes/pull/511 - buffer.put_uint_le(self.0 as u64, size_of::()); - } - - pub fn parse(buffer: &mut impl Buf) -> Result { - if buffer.remaining() < size_of::() { - return Err(PageOffsetError::BufferTooShort( - size_of::(), - buffer.remaining(), - )); - } - - let value = buffer.get_uint_le(size_of::()); - let page = usize::try_from(value)?; - - Ok(PageOffset(page)) - } - /// This will calculate a page offset based on the max file count and the offset from the first /// non-zero page in a file. /// @@ -115,6 +99,23 @@ impl fmt::Display for PageOffset { } } +impl Parseable for PageOffset { + type Output = Self; + fn parse(buffer: &mut impl Buf) -> Result { + if buffer.remaining() < size_of::() { + return Err(PageOffsetError::BufferTooShort( + size_of::(), + buffer.remaining(), + )); + } + + let value = buffer.get_uint_le(size_of::()); + let page = usize::try_from(value)?; + + Ok(PageOffset(page)) + } +} + impl Mul for PageOffset { type Output = PageOffset; @@ -123,6 +124,14 @@ impl Mul for PageOffset { } } +impl Serializable for PageOffset { + fn serialize(&self, buffer: &mut impl BufMut) { + //TODO switch this and other serialize calls to usize based once my pull request is accepted + //https://github.com/tokio-rs/bytes/pull/511 + buffer.put_uint_le(self.0 as u64, size_of::()); + } +} + #[derive(Debug, Error, PartialEq)] pub enum PageOffsetError { #[error("Not enough space to parse usize need {0} got {1}")] diff --git a/src/engine/io/row_formats/item_pointer.rs b/src/engine/io/row_formats/item_pointer.rs index 77141e5..cc0e419 100644 --- a/src/engine/io/row_formats/item_pointer.rs +++ b/src/engine/io/row_formats/item_pointer.rs @@ -3,6 +3,7 @@ //! //! We will be treating this a little different since our size will be based on usize +use crate::engine::io::format_traits::{Parseable, Serializable}; use crate::engine::io::page_formats::{PageOffset, PageOffsetError}; use crate::engine::io::ConstEncodedSize; @@ -24,16 +25,11 @@ impl ItemPointer { pub fn new(page: PageOffset, count: UInt12) -> ItemPointer { ItemPointer { page, count } } +} - pub fn serialize(&self, buffer: &mut impl BufMut) { - self.page.serialize(buffer); - UInt12::serialize_packed(buffer, &[self.count]); - } - - pub fn parse(buffer: &mut impl Buf) -> Result { - let po = PageOffset::parse(buffer)?; - let items = UInt12::parse_packed(buffer, 1)?; - Ok(ItemPointer::new(po, items[0])) +impl ConstEncodedSize for ItemPointer { + fn encoded_size() -> usize { + size_of::() + UInt12::encoded_size() } } @@ -45,9 +41,19 @@ impl fmt::Display for ItemPointer { } } -impl ConstEncodedSize for ItemPointer { - fn encoded_size() -> usize { - size_of::() + UInt12::encoded_size() +impl Parseable for ItemPointer { + type Output = Self; + fn parse(buffer: &mut impl Buf) -> Result { + let po = PageOffset::parse(buffer)?; + let items = UInt12::parse_packed(buffer, 1)?; + Ok(ItemPointer::new(po, items[0])) + } +} + +impl Serializable for ItemPointer { + fn serialize(&self, buffer: &mut impl BufMut) { + self.page.serialize(buffer); + UInt12::serialize_packed(buffer, &[self.count]); } }