vortex_file/footer/
serializer.rs1use std::sync::Arc;
5
6use vortex_buffer::ByteBuffer;
7use vortex_error::VortexExpect;
8use vortex_error::VortexResult;
9use vortex_error::vortex_err;
10use vortex_flatbuffers::FlatBuffer;
11use vortex_flatbuffers::FlatBufferRoot;
12use vortex_flatbuffers::WriteFlatBuffer;
13use vortex_flatbuffers::WriteFlatBufferExt;
14use vortex_layout::LayoutContext;
15use vortex_session::registry::ReadContext;
16
17use crate::EOF_SIZE;
18use crate::Footer;
19use crate::MAGIC_BYTES;
20use crate::MAX_POSTSCRIPT_SIZE;
21use crate::VERSION;
22use crate::footer::file_layout::FooterFlatBufferWriter;
23use crate::footer::postscript::Postscript;
24use crate::footer::postscript::PostscriptSegment;
25
26pub struct FooterSerializer {
28 footer: Footer,
29 exclude_dtype: bool,
30 offset: u64,
31}
32
33impl FooterSerializer {
34 pub(super) fn new(footer: Footer) -> Self {
35 Self {
36 footer,
37 exclude_dtype: false,
38 offset: 0,
39 }
40 }
41
42 pub fn with_offset(mut self, offset: u64) -> Self {
47 self.offset = offset;
48 self
49 }
50
51 pub fn exclude_dtype(mut self) -> Self {
54 self.exclude_dtype = true;
55 self
56 }
57
58 pub fn with_exclude_dtype(mut self, exclude_dtype: bool) -> Self {
61 self.exclude_dtype = exclude_dtype;
62 self
63 }
64
65 pub fn serialize(mut self) -> VortexResult<Vec<ByteBuffer>> {
68 let mut buffers = vec![];
69
70 let dtype_segment = if self.exclude_dtype {
71 None
72 } else {
73 let (buffer, dtype_segment) = write_flatbuffer(&mut self.offset, self.footer.dtype())?;
74 buffers.push(buffer);
75 Some(dtype_segment)
76 };
77
78 let layout_ctx = LayoutContext::default();
81
82 let (buffer, layout_segment) = write_flatbuffer(
83 &mut self.offset,
84 &self.footer.layout().flatbuffer_writer(&layout_ctx),
85 )?;
86 buffers.push(buffer);
87
88 let statistics_segment = match self.footer.statistics() {
89 None => None,
90 Some(stats) if stats.stats_sets().is_empty() => None,
91 Some(stats) => {
92 let (buffer, stats_segment) = write_flatbuffer(&mut self.offset, stats)?;
93 buffers.push(buffer);
94 Some(stats_segment)
95 }
96 };
97
98 let (buffer, footer_segment) = write_flatbuffer(
99 &mut self.offset,
100 &FooterFlatBufferWriter {
101 ctx: self.footer.array_read_ctx.clone(),
102 layout_ctx: ReadContext::new(layout_ctx.to_ids()),
103 segment_specs: Arc::clone(&self.footer.segments),
104 },
105 )?;
106 buffers.push(buffer);
107
108 let postscript = Postscript {
110 dtype: dtype_segment,
111 layout: layout_segment,
112 statistics: statistics_segment,
113 footer: footer_segment,
114 };
115 let postscript_buffer = postscript.write_flatbuffer_bytes()?;
116 if postscript_buffer.len() > MAX_POSTSCRIPT_SIZE as usize {
117 Err(vortex_err!(
118 "Postscript is too large ({} bytes); max postscript size is {}",
119 postscript_buffer.len(),
120 MAX_POSTSCRIPT_SIZE
121 ))?;
122 }
123
124 let postscript_len = u16::try_from(postscript_buffer.len())
125 .vortex_expect("Postscript already verified to fit into u16");
126 buffers.push(postscript_buffer.into_inner());
127
128 let mut eof = [0u8; EOF_SIZE];
130 eof[0..2].copy_from_slice(&VERSION.to_le_bytes());
131 eof[2..4].copy_from_slice(&postscript_len.to_le_bytes());
132 eof[4..8].copy_from_slice(&MAGIC_BYTES);
133 buffers.push(ByteBuffer::copy_from(eof));
134
135 Ok(buffers)
136 }
137}
138
139fn write_flatbuffer<F: FlatBufferRoot + WriteFlatBuffer>(
140 offset: &mut u64,
141 flatbuffer: &F,
142) -> VortexResult<(ByteBuffer, PostscriptSegment)> {
143 let buffer = flatbuffer.write_flatbuffer_bytes()?;
144 let length = u32::try_from(buffer.len())
145 .map_err(|_| vortex_err!("flatbuffer length exceeds maximum u32"))?;
146
147 let segment = PostscriptSegment {
148 offset: *offset,
149 length,
150 alignment: FlatBuffer::alignment(),
151 };
152
153 *offset += u64::from(length);
154
155 Ok((buffer.into_inner(), segment))
156}