1pub mod generate;
2pub mod stats;
3
4use crate::args::Args;
5use crate::error::NvmError;
6use crate::layout;
7use crate::layout::errors::LayoutError;
8use crate::output;
9use crate::output::errors::OutputError;
10use crate::variant::DataSheet;
11use crate::writer::write_output;
12use rayon::prelude::*;
13use stats::{BlockStat, BuildStats};
14use std::time::Instant;
15
16pub fn build_separate_blocks(
17 args: &Args,
18 data_sheet: Option<&DataSheet>,
19) -> Result<BuildStats, NvmError> {
20 let start_time = Instant::now();
21
22 let block_stats: Result<Vec<BlockStat>, NvmError> = args
23 .layout
24 .blocks
25 .par_iter()
26 .map(|input| generate::build_block_single(input, data_sheet, args))
27 .collect();
28
29 let block_stats = block_stats?;
30
31 let mut stats = BuildStats::new();
32 for stat in block_stats {
33 stats.add_block(stat);
34 }
35 stats.total_duration = start_time.elapsed();
36
37 Ok(stats)
38}
39
40pub fn build_single_file(
41 args: &Args,
42 data_sheet: Option<&DataSheet>,
43) -> Result<BuildStats, NvmError> {
44 let start_time = Instant::now();
45
46 let mut ranges = Vec::new();
47 let mut block_ranges: Vec<(String, u32, u32)> = Vec::new();
48 let mut stats = BuildStats::new();
49
50 for input in &args.layout.blocks {
51 let result =
52 (|| {
53 let layout = layout::load_layout(&input.file)?;
54
55 let block = layout
56 .blocks
57 .get(&input.name)
58 .ok_or(LayoutError::BlockNotFound(input.name.clone()))?;
59
60 let (bytestream, padding_bytes) =
61 block.build_bytestream(data_sheet, &layout.settings, args.layout.strict)?;
62
63 let dr = output::bytestream_to_datarange(
64 bytestream,
65 &block.header,
66 &layout.settings,
67 layout.settings.byte_swap,
68 layout.settings.pad_to_end,
69 padding_bytes,
70 )?;
71
72 let mut crc_bytes = [
73 dr.crc_bytestream[0],
74 dr.crc_bytestream[1],
75 dr.crc_bytestream[2],
76 dr.crc_bytestream[3],
77 ];
78 if layout.settings.byte_swap {
79 crc_bytes.swap(0, 1);
80 crc_bytes.swap(2, 3);
81 }
82 let crc_value = match layout.settings.endianness {
83 layout::settings::Endianness::Big => u32::from_be_bytes(crc_bytes),
84 layout::settings::Endianness::Little => u32::from_le_bytes(crc_bytes),
85 };
86
87 let stat = BlockStat {
88 name: input.name.clone(),
89 start_address: dr.start_address,
90 allocated_size: dr.allocated_size,
91 used_size: dr.used_size,
92 crc_value,
93 };
94
95 let start = block
96 .header
97 .start_address
98 .checked_add(layout.settings.virtual_offset)
99 .ok_or(LayoutError::InvalidBlockArgument(
100 "start_address + virtual_offset overflow".into(),
101 ))?;
102 let end = start.checked_add(block.header.length).ok_or(
103 LayoutError::InvalidBlockArgument("start + length overflow".into()),
104 )?;
105
106 Ok((dr, stat, start, end))
107 })()
108 .map_err(|e| NvmError::InBlock {
109 block_name: input.name.clone(),
110 layout_file: input.file.clone(),
111 source: Box::new(e),
112 })?;
113
114 let (dr, stat, start, end) = result;
115 stats.add_block(stat);
116 ranges.push(dr);
117 block_ranges.push((input.name.clone(), start, end));
118 }
119
120 for i in 0..block_ranges.len() {
122 for j in (i + 1)..block_ranges.len() {
123 let (ref name_a, a_start, a_end) = block_ranges[i];
124 let (ref name_b, b_start, b_end) = block_ranges[j];
125
126 let overlap_start = a_start.max(b_start);
127 let overlap_end = a_end.min(b_end);
128
129 if overlap_start < overlap_end {
130 let overlap_size = overlap_end - overlap_start;
131 let msg = format!(
132 "Block '{}' (0x{:08X}-0x{:08X}) overlaps with block '{}' (0x{:08X}-0x{:08X}). Overlap: 0x{:08X}-0x{:08X} ({} bytes)",
133 name_a,
134 a_start,
135 a_end - 1,
136 name_b,
137 b_start,
138 b_end - 1,
139 overlap_start,
140 overlap_end - 1,
141 overlap_size
142 );
143 return Err(OutputError::BlockOverlapError(msg).into());
144 }
145 }
146 }
147
148 let hex_string = output::emit_hex(
149 &ranges,
150 args.output.record_width as usize,
151 args.output.format,
152 )?;
153
154 write_output(&args.output, "combined", &hex_string)?;
155
156 stats.total_duration = start_time.elapsed();
157
158 Ok(stats)
159}