1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct MegakernelWorkspaceRegion<R> {
11 pub id: R,
13 pub offset_words: u32,
15 pub words: u32,
17 pub record_words: u32,
19 pub capacity_records: u32,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum MegakernelWorkspaceRegionSpec<R> {
26 Fixed {
28 id: R,
30 words: u32,
32 record_words: u32,
34 capacity_records: u32,
36 },
37 Record {
39 id: R,
41 record_words: u32,
43 capacity_records: u32,
45 },
46}
47
48impl<R> MegakernelWorkspaceRegionSpec<R> {
49 #[must_use]
51 pub const fn fixed(id: R, words: u32, record_words: u32, capacity_records: u32) -> Self {
52 Self::Fixed {
53 id,
54 words,
55 record_words,
56 capacity_records,
57 }
58 }
59
60 #[must_use]
62 pub const fn record(id: R, record_words: u32, capacity_records: u32) -> Self {
63 Self::Record {
64 id,
65 record_words,
66 capacity_records,
67 }
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum MegakernelWorkspaceLayoutError<R> {
74 RecordWordsOverflow {
76 region: R,
78 },
79 OffsetOverflow {
81 region: R,
83 },
84}
85
86impl<R: Copy> MegakernelWorkspaceRegion<R> {
87 #[must_use]
89 pub const fn end_words(self) -> Option<u32> {
90 self.offset_words.checked_add(self.words)
91 }
92}
93
94#[must_use]
96pub const fn workspace_record_words(record_words: u32, capacity_records: u32) -> Option<u32> {
97 record_words.checked_mul(capacity_records)
98}
99
100#[must_use]
102pub const fn first_workspace_region<R>(
103 id: R,
104 words: u32,
105 record_words: u32,
106 capacity_records: u32,
107) -> MegakernelWorkspaceRegion<R> {
108 MegakernelWorkspaceRegion {
109 id,
110 offset_words: 0,
111 words,
112 record_words,
113 capacity_records,
114 }
115}
116
117#[must_use]
119pub fn next_workspace_region<R: Copy>(
120 previous: MegakernelWorkspaceRegion<R>,
121 id: R,
122 words: u32,
123 record_words: u32,
124 capacity_records: u32,
125) -> Option<MegakernelWorkspaceRegion<R>> {
126 Some(MegakernelWorkspaceRegion {
127 id,
128 offset_words: previous.end_words()?,
129 words,
130 record_words,
131 capacity_records,
132 })
133}
134
135#[must_use]
137pub fn next_record_workspace_region<R: Copy>(
138 previous: MegakernelWorkspaceRegion<R>,
139 id: R,
140 record_words: u32,
141 capacity_records: u32,
142) -> Option<MegakernelWorkspaceRegion<R>> {
143 next_workspace_region(
144 previous,
145 id,
146 workspace_record_words(record_words, capacity_records)?,
147 record_words,
148 capacity_records,
149 )
150}
151
152pub fn build_workspace_regions<R: Copy>(
159 specs: &[MegakernelWorkspaceRegionSpec<R>],
160) -> Result<Vec<MegakernelWorkspaceRegion<R>>, MegakernelWorkspaceLayoutError<R>> {
161 let mut regions = Vec::with_capacity(specs.len());
162 let mut next_offset_words = 0_u32;
163
164 for spec in specs {
165 let (id, words, record_words, capacity_records) = match *spec {
166 MegakernelWorkspaceRegionSpec::Fixed {
167 id,
168 words,
169 record_words,
170 capacity_records,
171 } => (id, words, record_words, capacity_records),
172 MegakernelWorkspaceRegionSpec::Record {
173 id,
174 record_words,
175 capacity_records,
176 } => {
177 let words = workspace_record_words(record_words, capacity_records)
178 .ok_or(MegakernelWorkspaceLayoutError::RecordWordsOverflow { region: id })?;
179 (id, words, record_words, capacity_records)
180 }
181 };
182 let end_words = next_offset_words
183 .checked_add(words)
184 .ok_or(MegakernelWorkspaceLayoutError::OffsetOverflow { region: id })?;
185 regions.push(MegakernelWorkspaceRegion {
186 id,
187 offset_words: next_offset_words,
188 words,
189 record_words,
190 capacity_records,
191 });
192 next_offset_words = end_words;
193 }
194
195 Ok(regions)
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
203 enum Region {
204 Header,
205 Rows,
206 Work,
207 }
208
209 #[test]
210 fn generated_workspace_regions_are_contiguous_for_many_capacities() {
211 for rows in [1_u32, 2, 7, 8, 31, 32, 1024] {
212 for work in [1_u32, 3, 64, 4096] {
213 let header = first_workspace_region(Region::Header, 16, 1, 16);
214 let rows = next_record_workspace_region(header, Region::Rows, 5, rows)
215 .expect("Fix: generated row region should fit");
216 let work = next_record_workspace_region(rows, Region::Work, 2, work)
217 .expect("Fix: generated work region should fit");
218
219 assert_eq!(header.offset_words, 0);
220 assert_eq!(rows.offset_words, header.end_words().unwrap());
221 assert_eq!(work.offset_words, rows.end_words().unwrap());
222 }
223 }
224 }
225
226 #[test]
227 fn record_region_word_overflow_is_reported_before_offset_overflow() {
228 let header = first_workspace_region(Region::Header, 16, 1, 16);
229
230 assert_eq!(
231 workspace_record_words(u32::MAX, 2),
232 None,
233 "record sizing must catch multiplication overflow"
234 );
235 assert_eq!(
236 next_record_workspace_region(header, Region::Rows, u32::MAX, 2),
237 None,
238 "record-backed layout must reject overflowing record arenas"
239 );
240 }
241
242 #[test]
243 fn next_region_rejects_offset_overflow() {
244 let previous = MegakernelWorkspaceRegion {
245 id: Region::Header,
246 offset_words: u32::MAX,
247 words: 1,
248 record_words: 1,
249 capacity_records: 1,
250 };
251
252 assert_eq!(
253 next_workspace_region(previous, Region::Rows, 1, 1, 1),
254 None,
255 "contiguous layout must reject overflowing end offsets"
256 );
257 }
258
259 #[test]
260 fn bulk_workspace_region_builder_matches_chained_layout_for_generated_capacities() {
261 for rows in [1_u32, 2, 7, 8, 31, 32, 1024] {
262 for work in [1_u32, 3, 64, 4096] {
263 let specs = [
264 MegakernelWorkspaceRegionSpec::fixed(Region::Header, 16, 1, 16),
265 MegakernelWorkspaceRegionSpec::record(Region::Rows, 5, rows),
266 MegakernelWorkspaceRegionSpec::record(Region::Work, 2, work),
267 ];
268 let bulk = build_workspace_regions(&specs)
269 .expect("Fix: generated bulk workspace layout should fit");
270
271 let header = first_workspace_region(Region::Header, 16, 1, 16);
272 let rows = next_record_workspace_region(header, Region::Rows, 5, rows)
273 .expect("Fix: generated row region should fit");
274 let work = next_record_workspace_region(rows, Region::Work, 2, work)
275 .expect("Fix: generated work region should fit");
276
277 assert_eq!(bulk, vec![header, rows, work]);
278 }
279 }
280 }
281
282 #[test]
283 fn bulk_workspace_region_builder_reports_record_and_offset_overflow_separately() {
284 let record = [MegakernelWorkspaceRegionSpec::record(
285 Region::Rows,
286 u32::MAX,
287 2,
288 )];
289 assert_eq!(
290 build_workspace_regions(&record),
291 Err(MegakernelWorkspaceLayoutError::RecordWordsOverflow {
292 region: Region::Rows
293 })
294 );
295
296 let offset = [
297 MegakernelWorkspaceRegionSpec::fixed(Region::Header, u32::MAX, 1, u32::MAX),
298 MegakernelWorkspaceRegionSpec::fixed(Region::Rows, 1, 1, 1),
299 ];
300 assert_eq!(
301 build_workspace_regions(&offset),
302 Err(MegakernelWorkspaceLayoutError::OffsetOverflow {
303 region: Region::Rows
304 })
305 );
306 }
307}