use integer_encoding::FixedInt;
use std::convert::TryFrom;
use super::block_handle::BlockHandle;
use super::errors::{FooterError, ReadError, TableReadResult};
type FooterResult<T> = Result<T, FooterError>;
pub(crate) const SIZE_OF_FOOTER_BYTES: usize = 48;
#[cfg(not(feature = "strict"))]
const TABLE_MAGIC_NUMBER: u64 = 1646;
#[cfg(feature = "strict")]
const TABLE_MAGIC_NUMBER: u64 = 0xdb4775248b80fb57;
#[derive(Debug, Eq, PartialEq)]
pub(crate) struct Footer {
metaindex_handle: BlockHandle,
index_handle: BlockHandle,
}
impl Footer {
pub fn new(metaindex_handle: BlockHandle, index_handle: BlockHandle) -> Self {
Self {
metaindex_handle,
index_handle,
}
}
pub fn get_metaindex_handle(&self) -> &BlockHandle {
&self.metaindex_handle
}
pub fn get_index_handle(&self) -> &BlockHandle {
&self.index_handle
}
}
impl TryFrom<&Vec<u8>> for Footer {
type Error = ReadError;
fn try_from(value: &Vec<u8>) -> TableReadResult<Footer> {
if value.len() != SIZE_OF_FOOTER_BYTES {
return Err(ReadError::FailedToParse(
"The length of the buffer was not equal to the fixed size of a footer.".to_string(),
));
}
let magic_number_ptr = value.len() - 8;
let magic_number = u64::decode_fixed(&value[magic_number_ptr..]);
if magic_number != TABLE_MAGIC_NUMBER {
return Err(ReadError::FailedToParse(
"The magic number was incorrect. This is not a table file.".to_string(),
));
}
let (metaindex_handle, bytes_read) = BlockHandle::deserialize(value)?;
let (index_handle, _bytes_read) = BlockHandle::deserialize(&value[bytes_read..])?;
Ok(Footer::new(metaindex_handle, index_handle))
}
}
impl TryFrom<&Footer> for Vec<u8> {
type Error = FooterError;
fn try_from(value: &Footer) -> FooterResult<Vec<u8>> {
let mut buf: Vec<u8> = vec![];
let mut serialized_metaindex_handle = Vec::<u8>::from(&value.metaindex_handle);
let mut serialized_index_handle = Vec::<u8>::from(&value.index_handle);
let length_of_padding =
40 - serialized_metaindex_handle.len() - serialized_index_handle.len();
let mut zero_padding: Vec<u8> = vec![0; length_of_padding];
buf.append(&mut serialized_metaindex_handle);
buf.append(&mut serialized_index_handle);
buf.append(&mut zero_padding);
buf.append(&mut TABLE_MAGIC_NUMBER.encode_fixed_vec());
if buf.len() != SIZE_OF_FOOTER_BYTES {
return Err(FooterError::FooterSerialization(buf.len()));
}
Ok(buf)
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn footer_can_be_serialized_and_deserialized() {
let metaindex_handle = BlockHandle::new(80, 20);
let index_handle = BlockHandle::new(100, 20);
let footer = Footer::new(metaindex_handle, index_handle);
let serialized = Vec::<u8>::try_from(&footer).unwrap();
let deserialized = Footer::try_from(&serialized).unwrap();
assert_eq!(footer, deserialized);
}
}