1use integer_encoding::FixedInt;
8use std::convert::TryFrom;
9
10use super::block_handle::BlockHandle;
11use super::errors::{FooterError, ReadError, TableReadResult};
12
13type FooterResult<T> = Result<T, FooterError>;
15
16pub(crate) const SIZE_OF_FOOTER_BYTES: usize = 48;
22
23#[cfg(not(feature = "strict"))]
30const TABLE_MAGIC_NUMBER: u64 = 1646;
31
32#[cfg(feature = "strict")]
39const TABLE_MAGIC_NUMBER: u64 = 0xdb4775248b80fb57;
40
41#[derive(Debug, Eq, PartialEq)]
53pub(crate) struct Footer {
54 metaindex_handle: BlockHandle,
55 index_handle: BlockHandle,
56}
57
58impl Footer {
60 pub fn new(metaindex_handle: BlockHandle, index_handle: BlockHandle) -> Self {
62 Self {
63 metaindex_handle,
64 index_handle,
65 }
66 }
67
68 pub fn get_metaindex_handle(&self) -> &BlockHandle {
70 &self.metaindex_handle
71 }
72
73 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}