1use super::entry::{EntrySource, LeafEntry};
2use super::error::LayoutError;
3use super::header::Header;
4use super::scalar_type::ScalarType;
5use super::settings::{Endianness, MintConfig};
6use super::used_values::ValueSink;
7use super::value::{DataValue, ValueSource};
8use crate::data::DataSource;
9use crate::output::checksum;
10
11use indexmap::IndexMap;
12use serde::Deserialize;
13use std::collections::HashMap;
14
15struct PendingRef {
17 buffer_position: usize,
19 target_path: String,
21 scalar_type: ScalarType,
23 field_path: Vec<String>,
25}
26
27struct PendingChecksum {
29 buffer_position: usize,
31 scalar_type: ScalarType,
33 config_name: String,
35 field_path: Vec<String>,
37}
38
39struct BuildState {
41 buffer: Vec<u8>,
42 offset: usize,
43 padding_count: u32,
44 known_offsets: HashMap<String, usize>,
46 pending_refs: Vec<PendingRef>,
48 pending_checksums: Vec<PendingChecksum>,
50 resolved_checksum_values: Vec<u32>,
52}
53
54pub struct BuildConfig<'a> {
56 pub endianness: &'a Endianness,
57 pub padding: u8,
58 pub strict: bool,
59 pub consts: &'a HashMap<String, ValueSource>,
60}
61
62pub struct BuildOutput {
63 pub bytestream: Vec<u8>,
64 pub padding_count: u32,
65 pub checksum_values: Vec<u32>,
66}
67
68#[derive(Debug, Deserialize)]
69pub struct Config {
70 pub mint: MintConfig,
71 #[serde(flatten)]
72 pub blocks: IndexMap<String, Block>,
73}
74
75#[derive(Debug, Deserialize)]
77pub struct Block {
78 pub header: Header,
79 pub data: Entry,
80}
81
82#[derive(Debug, Deserialize)]
86#[serde(untagged)]
87pub enum Entry {
88 Leaf(LeafEntry),
89 Branch(IndexMap<String, Entry>),
90}
91
92impl Block {
93 pub fn build_bytestream(
94 &self,
95 data_source: Option<&dyn DataSource>,
96 settings: &MintConfig,
97 strict: bool,
98 value_sink: &mut dyn ValueSink,
99 ) -> Result<BuildOutput, LayoutError> {
100 let mut state = BuildState {
101 buffer: Vec::with_capacity((self.header.length as usize).min(64 * 1024)),
102 offset: 0,
103 padding_count: 0,
104 known_offsets: HashMap::new(),
105 pending_refs: Vec::new(),
106 pending_checksums: Vec::new(),
107 resolved_checksum_values: Vec::new(),
108 };
109 let config = BuildConfig {
110 endianness: &settings.endianness,
111 padding: self.header.padding,
112 strict,
113 consts: &settings.consts,
114 };
115
116 let mut field_path = Vec::new();
117 let _ = Self::build_bytestream_inner(
118 &self.data,
119 data_source,
120 settings,
121 &mut state,
122 &config,
123 value_sink,
124 &mut field_path,
125 )?;
126
127 if !state.pending_refs.is_empty() {
129 Self::resolve_pending_refs(&mut state, &config, &self.header, value_sink)?;
130 }
131
132 if !state.pending_checksums.is_empty() {
134 Self::resolve_pending_checksums(&mut state, settings, &config, value_sink)?;
135 }
136
137 Ok(BuildOutput {
138 bytestream: state.buffer,
139 padding_count: state.padding_count,
140 checksum_values: state.resolved_checksum_values,
141 })
142 }
143
144 fn build_bytestream_inner(
148 table: &Entry,
149 data_source: Option<&dyn DataSource>,
150 settings: &MintConfig,
151 state: &mut BuildState,
152 config: &BuildConfig,
153 value_sink: &mut dyn ValueSink,
154 field_path: &mut Vec<String>,
155 ) -> Result<usize, LayoutError> {
156 match table {
157 Entry::Leaf(leaf) => {
158 let alignment = leaf.get_alignment();
159 while !state.offset.is_multiple_of(alignment) {
160 state.buffer.push(config.padding);
161 state.offset += 1;
162 state.padding_count += 1;
163 }
164
165 let leaf_offset = state.offset;
166
167 if let EntrySource::Ref(target) = &leaf.source {
168 leaf.validate_ref(target)?;
169 let size = leaf.scalar_type.size_bytes();
170 state.pending_refs.push(PendingRef {
171 buffer_position: state.buffer.len(),
172 target_path: target.clone(),
173 scalar_type: leaf.scalar_type,
174 field_path: field_path.clone(),
175 });
176 state.buffer.extend(std::iter::repeat_n(0u8, size));
177 state.offset += size;
178 return Ok(leaf_offset);
179 }
180
181 if let EntrySource::Checksum(config_name) = &leaf.source {
182 leaf.validate_checksum(config_name, settings)?;
183 let size = leaf.scalar_type.size_bytes();
184 state.pending_checksums.push(PendingChecksum {
185 buffer_position: state.buffer.len(),
186 scalar_type: leaf.scalar_type,
187 config_name: config_name.clone(),
188 field_path: field_path.clone(),
189 });
190 state.buffer.extend(std::iter::repeat_n(0u8, size));
191 state.offset += size;
192 return Ok(leaf_offset);
193 }
194
195 let bytes = leaf.emit_bytes(data_source, config, value_sink, field_path)?;
196 state.offset += bytes.len();
197 state.buffer.extend(bytes);
198 Ok(leaf_offset)
199 }
200 Entry::Branch(branch) => {
201 if branch.is_empty() {
202 let branch_path = if field_path.is_empty() {
203 "<root>".to_owned()
204 } else {
205 field_path.join(".")
206 };
207 return Err(LayoutError::DataValueExportFailed(format!(
208 "Empty branch '{}' is invalid.",
209 branch_path
210 )));
211 }
212
213 let mut branch_offset = None;
214 for (field_name, v) in branch.iter() {
215 let path_len = field_path.len();
216 field_path.extend(split_field_path(field_name)?);
217
218 let offset = Self::build_bytestream_inner(
219 v,
220 data_source,
221 settings,
222 state,
223 config,
224 value_sink,
225 field_path,
226 );
227
228 if let Ok(o) = offset {
229 state.known_offsets.insert(field_path.join("."), o);
230 branch_offset.get_or_insert(o);
231 }
232
233 field_path.truncate(path_len);
234 offset.map_err(|e| LayoutError::InField {
235 field: field_name.clone(),
236 source: Box::new(e),
237 })?;
238 }
239 Ok(branch_offset.unwrap_or(state.offset))
240 }
241 }
242 }
243
244 fn resolve_pending_refs(
246 state: &mut BuildState,
247 config: &BuildConfig,
248 header: &Header,
249 value_sink: &mut dyn ValueSink,
250 ) -> Result<(), LayoutError> {
251 for pending in &state.pending_refs {
252 let target_offset = state
253 .known_offsets
254 .get(&pending.target_path)
255 .ok_or_else(|| {
256 LayoutError::DataValueExportFailed(format!(
257 "Ref target '{}' not found in block. Available fields: [{}]",
258 pending.target_path,
259 state
260 .known_offsets
261 .keys()
262 .cloned()
263 .collect::<Vec<_>>()
264 .join(", ")
265 ))
266 })?;
267
268 let address = header
269 .start_address
270 .checked_add(*target_offset as u32)
271 .ok_or_else(|| {
272 LayoutError::DataValueExportFailed(format!(
273 "Address overflow resolving ref to '{}'.",
274 pending.target_path
275 ))
276 })?;
277
278 let address_value = DataValue::U64(address as u64);
279 let bytes = address_value.to_bytes(pending.scalar_type, config.endianness, true)?;
280
281 let pos = pending.buffer_position;
283 state.buffer[pos..pos + bytes.len()].copy_from_slice(&bytes);
284
285 value_sink.record_value(
287 &pending.field_path,
288 serde_json::Value::Number(serde_json::Number::from(address as u64)),
289 )?;
290 }
291 Ok(())
292 }
293
294 fn resolve_pending_checksums(
296 state: &mut BuildState,
297 settings: &MintConfig,
298 config: &BuildConfig,
299 value_sink: &mut dyn ValueSink,
300 ) -> Result<(), LayoutError> {
301 for pending in &state.pending_checksums {
302 let crc_config = settings.checksum.get(&pending.config_name).ok_or_else(|| {
303 LayoutError::DataValueExportFailed(format!(
304 "Checksum config '{}' not found in [mint.checksum].",
305 pending.config_name
306 ))
307 })?;
308
309 let crc_val =
310 checksum::calculate_crc(&state.buffer[..pending.buffer_position], crc_config);
311
312 let crc_bytes = match config.endianness {
314 Endianness::Big => crc_val.to_be_bytes(),
315 Endianness::Little => crc_val.to_le_bytes(),
316 };
317
318 let size = pending.scalar_type.size_bytes();
320 state.buffer[pending.buffer_position..pending.buffer_position + size]
321 .copy_from_slice(&crc_bytes[..size]);
322
323 value_sink.record_value(
325 &pending.field_path,
326 serde_json::Value::Number(serde_json::Number::from(crc_val as u64)),
327 )?;
328 state.resolved_checksum_values.push(crc_val);
329 }
330 Ok(())
331 }
332}
333
334fn split_field_path(field_name: &str) -> Result<Vec<String>, LayoutError> {
335 let segments: Vec<&str> = field_name.split('.').collect();
336 if segments.iter().any(|s| s.is_empty()) {
337 return Err(LayoutError::DataValueExportFailed(format!(
338 "Invalid field path '{}'.",
339 field_name
340 )));
341 }
342 Ok(segments.into_iter().map(|s| s.to_owned()).collect())
343}