1use iris_abi::{CapabilitySet, Writer, wire::align_up};
9
10use crate::digest::Digest;
11use crate::error::Result;
12use crate::layout::{
13 DecoderLocation, FORMAT_MAJOR, FORMAT_MINOR, HEADER_SIZE, MAGIC, SchemaEncoding, SectionKind,
14 TRAILER_SIZE, tag,
15};
16use crate::meta::{Dataset, DecoderRef, Schema, Section};
17
18#[derive(Clone, Debug)]
20struct PendingDecoder {
21 abi_major: u16,
22 abi_minor: u16,
23 location: DecoderLocation,
24 digest: Digest,
25 required: CapabilitySet,
26 name: String,
27}
28
29#[derive(Clone, Debug)]
48pub struct Builder {
49 dataset: Dataset,
50 schema: Option<(SchemaEncoding, Vec<u8>)>,
51 decoder: Option<PendingDecoder>,
52 sections: Vec<(u32, SectionKind, Vec<u8>)>,
53 next_id: u32,
54}
55
56impl Builder {
57 #[must_use]
59 pub fn new(name: impl Into<String>, rows: u64) -> Self {
60 Self {
61 dataset: Dataset {
62 rows,
63 name: name.into(),
64 },
65 schema: None,
66 decoder: None,
67 sections: Vec::new(),
68 next_id: 0,
69 }
70 }
71
72 pub fn schema(&mut self, encoding: SchemaEncoding, bytes: impl Into<Vec<u8>>) -> &mut Self {
74 self.schema = Some((encoding, bytes.into()));
75 self
76 }
77
78 pub fn section(&mut self, kind: SectionKind, bytes: impl Into<Vec<u8>>) -> u32 {
80 let id = self.next_id;
81 self.next_id += 1;
82 self.sections.push((id, kind, bytes.into()));
83 id
84 }
85
86 pub fn embed_decoder(
91 &mut self,
92 name: impl Into<String>,
93 abi: (u16, u16),
94 required: CapabilitySet,
95 module: impl Into<Vec<u8>>,
96 ) -> u32 {
97 let module = module.into();
98 let digest = Digest::of(&module);
99 let section = self.section(SectionKind::Decoder, module);
100 self.decoder = Some(PendingDecoder {
101 abi_major: abi.0,
102 abi_minor: abi.1,
103 location: DecoderLocation::Embedded { section },
104 digest,
105 required,
106 name: name.into(),
107 });
108 section
109 }
110
111 pub fn external_decoder(
113 &mut self,
114 name: impl Into<String>,
115 abi: (u16, u16),
116 required: CapabilitySet,
117 digest: Digest,
118 ) -> &mut Self {
119 self.decoder = Some(PendingDecoder {
120 abi_major: abi.0,
121 abi_minor: abi.1,
122 location: DecoderLocation::External,
123 digest,
124 required,
125 name: name.into(),
126 });
127 self
128 }
129
130 pub fn build(&self) -> Result<Vec<u8>> {
137 let mut out = Vec::new();
138 out.extend_from_slice(&MAGIC);
139 out.extend_from_slice(&FORMAT_MAJOR.to_le_bytes());
140 out.extend_from_slice(&FORMAT_MINOR.to_le_bytes());
141 out.extend_from_slice(&0u32.to_le_bytes());
142 debug_assert_eq!(out.len(), HEADER_SIZE);
143
144 let mut placed = Vec::with_capacity(self.sections.len());
145 for (id, kind, bytes) in &self.sections {
146 pad(&mut out);
147 placed.push(Section {
148 id: *id,
149 kind: *kind,
150 offset: out.len() as u64,
151 len: bytes.len() as u64,
152 digest: Digest::of(bytes),
153 });
154 out.extend_from_slice(bytes);
155 }
156
157 pad(&mut out);
158 let footer_offset = out.len() as u64;
159 let footer = self.encode_footer(&placed)?;
160 out.extend_from_slice(&footer);
161
162 let mut hasher = blake3::Hasher::new();
163 hasher.update(&out[..HEADER_SIZE]);
164 hasher.update(&footer);
165 let root = Digest(*hasher.finalize().as_bytes());
166
167 out.extend_from_slice(&footer_offset.to_le_bytes());
168 let footer_len =
171 u32::try_from(footer.len()).map_err(|_| iris_abi::Error::LengthOverflow)?;
172 out.extend_from_slice(&footer_len.to_le_bytes());
173 out.extend_from_slice(&0u32.to_le_bytes());
174 out.extend_from_slice(root.as_bytes());
175 out.extend_from_slice(&MAGIC);
176 debug_assert_eq!(
177 out.len() as u64,
178 footer_offset + footer.len() as u64 + TRAILER_SIZE as u64
179 );
180
181 Ok(out)
182 }
183
184 fn encode_footer(&self, sections: &[Section]) -> Result<Vec<u8>> {
191 let mut capacity = 1024 + sections.len() * 128;
192 loop {
193 let mut buf = vec![0u8; capacity];
194 match self.write_footer(&mut Writer::new(&mut buf), sections) {
195 Ok(written) => {
196 buf.truncate(written);
197 return Ok(buf);
198 }
199 Err(iris_abi::Error::BufferFull { .. }) => capacity *= 2,
200 Err(other) => return Err(other.into()),
201 }
202 }
203 }
204
205 fn write_footer(
206 &self,
207 w: &mut Writer<'_>,
208 sections: &[Section],
209 ) -> core::result::Result<usize, iris_abi::Error> {
210 w.record(tag::DATASET, Dataset::VERSION, |w| self.dataset.encode(w))?;
211 if let Some((encoding, bytes)) = &self.schema {
212 let schema = Schema {
213 encoding: *encoding,
214 bytes,
215 };
216 w.record(tag::SCHEMA, Schema::VERSION, |w| schema.encode(w))?;
217 }
218 if let Some(decoder) = &self.decoder {
219 let reference = DecoderRef {
220 abi_major: decoder.abi_major,
221 abi_minor: decoder.abi_minor,
222 location: decoder.location,
223 digest: decoder.digest,
224 required: decoder.required,
225 name: &decoder.name,
226 };
227 w.record(tag::DECODER, DecoderRef::VERSION, |w| reference.encode(w))?;
228 }
229 for section in sections {
230 w.record(tag::SECTION, Section::VERSION, |w| section.encode(w))?;
231 }
232 Ok(w.position())
233 }
234}
235
236fn pad(out: &mut Vec<u8>) {
239 out.resize(align_up(out.len()), 0);
240}