puffin/file_format/writer/
footer.rs1use std::collections::HashMap;
16use std::io::Write;
17use std::mem;
18
19use snafu::ResultExt;
20
21use crate::blob_metadata::BlobMetadata;
22use crate::error::{Lz4CompressionSnafu, Result, SerializeJsonSnafu};
23use crate::file_format::{Flags, MAGIC, MIN_FOOTER_SIZE};
24use crate::file_metadata::FileMetadataBuilder;
25
26pub struct FooterWriter {
33 blob_metadata: Vec<BlobMetadata>,
34 file_properties: HashMap<String, String>,
35 lz4_compressed: bool,
36}
37
38impl FooterWriter {
39 pub fn new(
40 blob_metadata: Vec<BlobMetadata>,
41 file_properties: HashMap<String, String>,
42 lz4_compressed: bool,
43 ) -> Self {
44 Self {
45 blob_metadata,
46 file_properties,
47 lz4_compressed,
48 }
49 }
50
51 pub fn into_footer_bytes(mut self) -> Result<Vec<u8>> {
53 let payload = self.footer_payload()?;
54 let payload_size = payload.len();
55
56 let capacity = MIN_FOOTER_SIZE as usize + payload_size;
57 let mut buf = Vec::with_capacity(capacity);
58
59 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)
65 }
66
67 fn write_magic(&self, buf: &mut Vec<u8>) {
68 buf.extend_from_slice(&MAGIC);
69 }
70
71 fn write_payload(&self, buf: &mut Vec<u8>, payload: &[u8]) {
72 buf.extend_from_slice(payload);
73 }
74
75 fn write_footer_payload_size(&self, payload_size: i32, buf: &mut Vec<u8>) {
76 buf.extend_from_slice(&payload_size.to_le_bytes());
77 }
78
79 fn write_flags(&self, buf: &mut Vec<u8>) {
81 let mut flags = Flags::DEFAULT;
82 if self.lz4_compressed {
83 flags |= Flags::FOOTER_PAYLOAD_COMPRESSED_LZ4;
84 } else {
85 flags &= !Flags::FOOTER_PAYLOAD_COMPRESSED_LZ4;
86 }
87
88 buf.extend_from_slice(&flags.bits().to_le_bytes());
89 }
90
91 fn footer_payload(&mut self) -> Result<Vec<u8>> {
92 let file_metadata = FileMetadataBuilder::default()
93 .blobs(mem::take(&mut self.blob_metadata))
94 .properties(mem::take(&mut self.file_properties))
95 .build()
96 .expect("Required fields are not set");
97
98 if self.lz4_compressed {
99 let mut buf = vec![];
100 let mut encoder = lz4_flex::frame::FrameEncoder::new(&mut buf);
101 serde_json::to_writer(&mut encoder, &file_metadata).context(SerializeJsonSnafu)?;
102 encoder.flush().context(Lz4CompressionSnafu)?;
103 Ok(buf)
104 } else {
105 serde_json::to_vec(&file_metadata).context(SerializeJsonSnafu)
106 }
107 }
108}