raindb/tables/
footer.rs

1// Copyright (c) 2021 Google LLC
2//
3// Use of this source code is governed by an MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT.
6
7use integer_encoding::FixedInt;
8use std::convert::TryFrom;
9
10use super::block_handle::BlockHandle;
11use super::errors::{FooterError, ReadError, TableReadResult};
12
13/// Result that wraps [`FooterError`].
14type FooterResult<T> = Result<T, FooterError>;
15
16/**
17The fixed size of a footer.
18
19Refer to the [`Footer`] documentation for how this number is calculated.
20*/
21pub(crate) const SIZE_OF_FOOTER_BYTES: usize = 48;
22
23/**
24A magic number to provide padding.
25
26The magic number was picked summing the [`u8`] representation of each charcter in the string
27"batmann and robin".
28*/
29#[cfg(not(feature = "strict"))]
30const TABLE_MAGIC_NUMBER: u64 = 1646;
31
32/**
33A magic number to provide padding.
34
35The magic number was picked by running `echo http://code.google.com/p/leveldb/ | sha1sum` and
36taking the leading 64 bits.
37*/
38#[cfg(feature = "strict")]
39const TABLE_MAGIC_NUMBER: u64 = 0xdb4775248b80fb57;
40
41/**
42The footer of a table file.
43
44A table file's footer consists of the following parts:
45- A block handle for the metaindex
46- A block handle for the index
47- 40 zeroed bytes - (size of metaindex block handle) - (size of index block handle) to make the
48  footer a fixed length
49    - 40 comes from (2 * [`crate::block_handle::BLOCK_HANDLE_MAX_ENCODED_LENGTH`])
50- 8-byte `TABLE_MAGIC_NUMBER`
51*/
52#[derive(Debug, Eq, PartialEq)]
53pub(crate) struct Footer {
54    metaindex_handle: BlockHandle,
55    index_handle: BlockHandle,
56}
57
58/// Public methods
59impl Footer {
60    /// Create a new instance of [`Footer`].
61    pub fn new(metaindex_handle: BlockHandle, index_handle: BlockHandle) -> Self {
62        Self {
63            metaindex_handle,
64            index_handle,
65        }
66    }
67
68    /// Get a reference to the metaindex block handle.
69    pub fn get_metaindex_handle(&self) -> &BlockHandle {
70        &self.metaindex_handle
71    }
72
73    /// Get a reference to the index block handle.
74    pub fn get_index_handle(&self) -> &BlockHandle {
75        &self.index_handle
76    }
77}
78
79impl TryFrom<&Vec<u8>> for Footer {
80    type Error = ReadError;
81
82    fn try_from(value: &Vec<u8>) -> TableReadResult<Footer> {
83        if value.len() != SIZE_OF_FOOTER_BYTES {
84            return Err(ReadError::FailedToParse(
85                "The length of the buffer was not equal to the fixed size of a footer.".to_string(),
86            ));
87        }
88
89        let magic_number_ptr = value.len() - 8;
90        let magic_number = u64::decode_fixed(&value[magic_number_ptr..]);
91        if magic_number != TABLE_MAGIC_NUMBER {
92            return Err(ReadError::FailedToParse(
93                "The magic number was incorrect. This is not a table file.".to_string(),
94            ));
95        }
96
97        let (metaindex_handle, bytes_read) = BlockHandle::deserialize(value)?;
98        let (index_handle, _bytes_read) = BlockHandle::deserialize(&value[bytes_read..])?;
99
100        Ok(Footer::new(metaindex_handle, index_handle))
101    }
102}
103
104impl TryFrom<&Footer> for Vec<u8> {
105    type Error = FooterError;
106
107    fn try_from(value: &Footer) -> FooterResult<Vec<u8>> {
108        let mut buf: Vec<u8> = vec![];
109        let mut serialized_metaindex_handle = Vec::<u8>::from(&value.metaindex_handle);
110        let mut serialized_index_handle = Vec::<u8>::from(&value.index_handle);
111        let length_of_padding =
112            40 - serialized_metaindex_handle.len() - serialized_index_handle.len();
113        let mut zero_padding: Vec<u8> = vec![0; length_of_padding];
114
115        buf.append(&mut serialized_metaindex_handle);
116        buf.append(&mut serialized_index_handle);
117        buf.append(&mut zero_padding);
118        buf.append(&mut TABLE_MAGIC_NUMBER.encode_fixed_vec());
119
120        if buf.len() != SIZE_OF_FOOTER_BYTES {
121            return Err(FooterError::FooterSerialization(buf.len()));
122        }
123
124        Ok(buf)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use pretty_assertions::assert_eq;
131
132    use super::*;
133
134    #[test]
135    fn footer_can_be_serialized_and_deserialized() {
136        let metaindex_handle = BlockHandle::new(80, 20);
137        let index_handle = BlockHandle::new(100, 20);
138        let footer = Footer::new(metaindex_handle, index_handle);
139
140        let serialized = Vec::<u8>::try_from(&footer).unwrap();
141        let deserialized = Footer::try_from(&serialized).unwrap();
142
143        assert_eq!(footer, deserialized);
144    }
145}