puffin/file_format/writer/
footer.rsuse std::collections::HashMap;
use std::io::Write;
use std::mem;
use snafu::ResultExt;
use crate::blob_metadata::BlobMetadata;
use crate::error::{Lz4CompressionSnafu, Result, SerializeJsonSnafu};
use crate::file_format::{Flags, MAGIC, MIN_FOOTER_SIZE};
use crate::file_metadata::FileMetadataBuilder;
pub struct FooterWriter {
blob_metadata: Vec<BlobMetadata>,
file_properties: HashMap<String, String>,
lz4_compressed: bool,
}
impl FooterWriter {
pub fn new(
blob_metadata: Vec<BlobMetadata>,
file_properties: HashMap<String, String>,
lz4_compressed: bool,
) -> Self {
Self {
blob_metadata,
file_properties,
lz4_compressed,
}
}
pub fn into_footer_bytes(mut self) -> Result<Vec<u8>> {
let payload = self.footer_payload()?;
let payload_size = payload.len();
let capacity = MIN_FOOTER_SIZE as usize + payload_size;
let mut buf = Vec::with_capacity(capacity);
self.write_magic(&mut buf); self.write_payload(&mut buf, &payload); self.write_footer_payload_size(payload_size as _, &mut buf); self.write_flags(&mut buf); self.write_magic(&mut buf); Ok(buf)
}
fn write_magic(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(&MAGIC);
}
fn write_payload(&self, buf: &mut Vec<u8>, payload: &[u8]) {
buf.extend_from_slice(payload);
}
fn write_footer_payload_size(&self, payload_size: i32, buf: &mut Vec<u8>) {
buf.extend_from_slice(&payload_size.to_le_bytes());
}
fn write_flags(&self, buf: &mut Vec<u8>) {
let mut flags = Flags::DEFAULT;
if self.lz4_compressed {
flags |= Flags::FOOTER_PAYLOAD_COMPRESSED_LZ4;
} else {
flags &= !Flags::FOOTER_PAYLOAD_COMPRESSED_LZ4;
}
buf.extend_from_slice(&flags.bits().to_le_bytes());
}
fn footer_payload(&mut self) -> Result<Vec<u8>> {
let file_metadata = FileMetadataBuilder::default()
.blobs(mem::take(&mut self.blob_metadata))
.properties(mem::take(&mut self.file_properties))
.build()
.expect("Required fields are not set");
if self.lz4_compressed {
let mut buf = vec![];
let mut encoder = lz4_flex::frame::FrameEncoder::new(&mut buf);
serde_json::to_writer(&mut encoder, &file_metadata).context(SerializeJsonSnafu)?;
encoder.flush().context(Lz4CompressionSnafu)?;
Ok(buf)
} else {
serde_json::to_vec(&file_metadata).context(SerializeJsonSnafu)
}
}
}