feophantlib/engine/io/row_formats/
item_pointer.rs

1//! Item Pointers tell a row where the latest version of itself might be stored.
2//! Details here: https://www.postgresql.org/docs/current/storage-page-layout.html look at t_ctid
3//!
4//! We will be treating this a little different since our size will be based on usize
5
6use crate::engine::io::format_traits::{Parseable, Serializable};
7use crate::engine::io::page_formats::{PageOffset, PageOffsetError};
8use crate::engine::io::ConstEncodedSize;
9
10use super::super::page_formats::{UInt12, UInt12Error};
11use bytes::{Buf, BufMut, Bytes, BytesMut};
12use std::convert::TryFrom;
13use std::fmt;
14use std::mem::size_of;
15use std::num::TryFromIntError;
16use thiserror::Error;
17
18#[derive(Clone, Copy, Debug, PartialEq)]
19pub struct ItemPointer {
20    pub page: PageOffset,
21    pub count: UInt12,
22}
23
24impl ItemPointer {
25    pub fn new(page: PageOffset, count: UInt12) -> ItemPointer {
26        ItemPointer { page, count }
27    }
28}
29
30impl ConstEncodedSize for ItemPointer {
31    fn encoded_size() -> usize {
32        size_of::<usize>() + UInt12::encoded_size()
33    }
34}
35
36impl fmt::Display for ItemPointer {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        writeln!(f, "\tItemPointer")?;
39        writeln!(f, "\tPage: {}", self.page)?;
40        writeln!(f, "\tCount: {}", self.count)
41    }
42}
43
44impl Parseable<ItemPointerError> for ItemPointer {
45    type Output = Self;
46    fn parse(buffer: &mut impl Buf) -> Result<Self, ItemPointerError> {
47        let po = PageOffset::parse(buffer)?;
48        let items = UInt12::parse_packed(buffer, 1)?;
49        Ok(ItemPointer::new(po, items[0]))
50    }
51}
52
53impl Serializable for ItemPointer {
54    fn serialize(&self, buffer: &mut impl BufMut) {
55        self.page.serialize(buffer);
56        UInt12::serialize_packed(buffer, &[self.count]);
57    }
58}
59
60#[derive(Debug, Error, PartialEq)]
61pub enum ItemPointerError {
62    #[error(transparent)]
63    PageOffsetError(#[from] PageOffsetError),
64    #[error(transparent)]
65    TryFromIntError(#[from] TryFromIntError),
66    #[error(transparent)]
67    UInt12Error(#[from] UInt12Error),
68}
69
70#[cfg(test)]
71mod tests {
72    use std::mem::size_of;
73
74    use super::*;
75
76    #[test]
77    fn sizes_match() -> Result<(), Box<dyn std::error::Error>> {
78        let test = ItemPointer::new(PageOffset(1), UInt12::new(2)?);
79        let calc_len = ItemPointer::encoded_size();
80
81        let mut buffer = BytesMut::new();
82        test.serialize(&mut buffer);
83
84        assert_eq!(calc_len, buffer.freeze().len());
85        Ok(())
86    }
87
88    #[test]
89    fn test_item_pointer_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
90        let test = ItemPointer::new(PageOffset(1), UInt12::new(1).unwrap());
91
92        let mut buffer = BytesMut::new();
93        test.serialize(&mut buffer);
94        let test_reparse = ItemPointer::parse(&mut buffer.freeze())?;
95
96        assert_eq!(test, test_reparse);
97        Ok(())
98    }
99
100    #[test]
101    fn test_item_pointer_error_conditions() -> Result<(), Box<dyn std::error::Error>> {
102        let parse = ItemPointer::parse(&mut Bytes::new());
103
104        assert_eq!(
105            Err(ItemPointerError::PageOffsetError(
106                PageOffsetError::BufferTooShort(size_of::<usize>(), 0)
107            )),
108            parse
109        );
110
111        let parse = ItemPointer::parse(&mut Bytes::from_static(&[0, 0, 0, 0, 0, 0, 0, 0, 1]));
112
113        assert_eq!(
114            Err(ItemPointerError::UInt12Error(UInt12Error::InsufficentData(
115                0
116            ))),
117            parse
118        );
119        Ok(())
120    }
121
122    #[test]
123    fn test_encoded_size() {
124        match size_of::<usize>() {
125            4 => assert_eq!(6, ItemPointer::encoded_size()),
126            8 => assert_eq!(10, ItemPointer::encoded_size()),
127            _ => panic!("You're on your own on this arch."),
128        }
129    }
130}