1#[cfg(not(feature = "std"))]
4use alloc::vec::Vec;
5
6use crate::error::FormatError;
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum DataLayout {
11 Compact {
13 data: Vec<u8>,
15 },
16 Contiguous {
18 address: Option<u64>,
20 size: u64,
22 },
23 Chunked {
25 chunk_dimensions: Vec<u32>,
27 btree_address: Option<u64>,
29 version: u8,
31 chunk_index_type: Option<u8>,
33 single_chunk_filtered_size: Option<u64>,
35 single_chunk_filter_mask: Option<u32>,
37 },
38 Virtual {
40 version: u8,
42 },
43}
44
45fn ensure_len(data: &[u8], offset: usize, needed: usize) -> Result<(), FormatError> {
46 match offset.checked_add(needed) {
47 Some(end) if end <= data.len() => Ok(()),
48 _ => Err(FormatError::UnexpectedEof {
49 expected: offset.saturating_add(needed),
50 available: data.len(),
51 }),
52 }
53}
54
55fn read_offset(data: &[u8], pos: usize, size: u8) -> Result<u64, FormatError> {
56 let s = size as usize;
57 ensure_len(data, pos, s)?;
58 let slice = &data[pos..pos + s];
59 Ok(match size {
60 2 => u16::from_le_bytes([slice[0], slice[1]]) as u64,
61 4 => u32::from_le_bytes([slice[0], slice[1], slice[2], slice[3]]) as u64,
62 8 => u64::from_le_bytes([
63 slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7],
64 ]),
65 _ => {
66 return Err(FormatError::InvalidOffsetSize(size));
67 }
68 })
69}
70
71fn read_length(data: &[u8], pos: usize, size: u8) -> Result<u64, FormatError> {
72 read_offset(data, pos, size)
73}
74
75fn is_undefined(data: &[u8], pos: usize, size: u8) -> bool {
77 let s = size as usize;
78 if pos + s > data.len() {
79 return false;
80 }
81 data[pos..pos + s].iter().all(|&b| b == 0xFF)
82}
83
84impl DataLayout {
85 pub fn parse(data: &[u8], offset_size: u8, length_size: u8) -> Result<DataLayout, FormatError> {
89 ensure_len(data, 0, 2)?;
90 let version = data[0];
91 let layout_class = data[1];
92
93 match version {
94 3 => Self::parse_v3(data, layout_class, offset_size, length_size),
95 4 => Self::parse_v4(data, layout_class, offset_size, length_size),
96 _ => Err(FormatError::InvalidLayoutVersion(version)),
97 }
98 }
99
100 fn parse_v3(
101 data: &[u8],
102 layout_class: u8,
103 offset_size: u8,
104 length_size: u8,
105 ) -> Result<DataLayout, FormatError> {
106 let pos = 2;
107 match layout_class {
108 0 => {
109 ensure_len(data, pos, 2)?;
111 let data_size = u16::from_le_bytes([data[pos], data[pos + 1]]) as usize;
112 ensure_len(data, pos + 2, data_size)?;
113 let raw = data[pos + 2..pos + 2 + data_size].to_vec();
114 Ok(DataLayout::Compact { data: raw })
115 }
116 1 => {
117 let os = offset_size as usize;
119 let ls = length_size as usize;
120 ensure_len(data, pos, os + ls)?;
121 let address = if is_undefined(data, pos, offset_size) {
122 None
123 } else {
124 Some(read_offset(data, pos, offset_size)?)
125 };
126 let size = read_length(data, pos + os, length_size)?;
127 Ok(DataLayout::Contiguous { address, size })
128 }
129 2 => {
130 ensure_len(data, pos, 1)?;
132 let dimensionality = data[pos] as usize;
133 let mut p = pos + 1;
134 let os = offset_size as usize;
136 ensure_len(data, p, os)?;
137 let btree_address = if is_undefined(data, p, offset_size) {
138 None
139 } else {
140 Some(read_offset(data, p, offset_size)?)
141 };
142 p += os;
143 ensure_len(data, p, dimensionality * 4)?;
145 let mut chunk_dimensions = Vec::with_capacity(dimensionality);
146 for _ in 0..dimensionality {
147 let dim = u32::from_le_bytes([data[p], data[p + 1], data[p + 2], data[p + 3]]);
148 chunk_dimensions.push(dim);
149 p += 4;
150 }
151 Ok(DataLayout::Chunked {
152 chunk_dimensions,
153 btree_address,
154 version: 3,
155 chunk_index_type: None,
156 single_chunk_filtered_size: None,
157 single_chunk_filter_mask: None,
158 })
159 }
160 _ => Err(FormatError::InvalidLayoutClass(layout_class)),
161 }
162 }
163
164 fn parse_v4(
165 data: &[u8],
166 layout_class: u8,
167 offset_size: u8,
168 length_size: u8,
169 ) -> Result<DataLayout, FormatError> {
170 let pos = 2;
171 match layout_class {
172 0 => {
173 ensure_len(data, pos, 2)?;
175 let data_size = u16::from_le_bytes([data[pos], data[pos + 1]]) as usize;
176 ensure_len(data, pos + 2, data_size)?;
177 let raw = data[pos + 2..pos + 2 + data_size].to_vec();
178 Ok(DataLayout::Compact { data: raw })
179 }
180 1 => {
181 let os = offset_size as usize;
183 let ls = length_size as usize;
184 ensure_len(data, pos, os + ls)?;
185 let address = if is_undefined(data, pos, offset_size) {
186 None
187 } else {
188 Some(read_offset(data, pos, offset_size)?)
189 };
190 let size = read_length(data, pos + os, length_size)?;
191 Ok(DataLayout::Contiguous { address, size })
192 }
193 2 => {
194 ensure_len(data, pos, 3)?;
196 let flags = data[pos];
197 let dimensionality = data[pos + 1] as usize;
198 let dim_size_encoded_length = data[pos + 2] as usize;
199 let mut p = pos + 3;
200
201 ensure_len(data, p, dimensionality * dim_size_encoded_length)?;
203 let mut chunk_dimensions = Vec::with_capacity(dimensionality);
204 for _ in 0..dimensionality {
205 let val = match dim_size_encoded_length {
206 1 => data[p] as u32,
207 2 => u16::from_le_bytes([data[p], data[p + 1]]) as u32,
208 4 => u32::from_le_bytes([
209 data[p],
210 data[p + 1],
211 data[p + 2],
212 data[p + 3],
213 ]),
214 8 => {
215 u32::from_le_bytes([
217 data[p],
218 data[p + 1],
219 data[p + 2],
220 data[p + 3],
221 ])
222 }
223 _ => {
224 return Err(FormatError::UnexpectedEof {
225 expected: p + dim_size_encoded_length,
226 available: data.len(),
227 });
228 }
229 };
230 chunk_dimensions.push(val);
231 p += dim_size_encoded_length;
232 }
233
234 ensure_len(data, p, 1)?;
236 let chunk_index_type = data[p];
237 p += 1;
238
239 let mut single_chunk_filtered_size = None;
241 let mut single_chunk_filter_mask = None;
242 let btree_address = match chunk_index_type {
243 1 => {
244 let filters_present = flags & 0x02 != 0;
247 if filters_present {
248 let ls = length_size as usize;
250 let os = offset_size as usize;
251 ensure_len(data, p, ls + 4 + os)?;
252 single_chunk_filtered_size = Some(read_length(data, p, length_size)?);
253 p += ls;
254 single_chunk_filter_mask = Some(u32::from_le_bytes([
255 data[p], data[p + 1], data[p + 2], data[p + 3],
256 ]));
257 p += 4;
258 if is_undefined(data, p, offset_size) {
259 None
260 } else {
261 Some(read_offset(data, p, offset_size)?)
262 }
263 } else {
264 ensure_len(data, p, offset_size as usize)?;
266 if is_undefined(data, p, offset_size) {
267 None
268 } else {
269 Some(read_offset(data, p, offset_size)?)
270 }
271 }
272 }
273 2 => {
274 ensure_len(data, p, offset_size as usize)?;
276 if is_undefined(data, p, offset_size) {
277 None
278 } else {
279 Some(read_offset(data, p, offset_size)?)
280 }
281 }
282 3 => {
283 ensure_len(data, p, 1 + offset_size as usize)?;
285 p += 1; if is_undefined(data, p, offset_size) {
287 None
288 } else {
289 Some(read_offset(data, p, offset_size)?)
290 }
291 }
292 4 => {
293 ensure_len(data, p, 5 + offset_size as usize)?;
295 p += 5; if is_undefined(data, p, offset_size) {
297 None
298 } else {
299 Some(read_offset(data, p, offset_size)?)
300 }
301 }
302 5 => {
303 ensure_len(data, p, 6 + offset_size as usize)?;
305 p += 6;
306 if is_undefined(data, p, offset_size) {
307 None
308 } else {
309 Some(read_offset(data, p, offset_size)?)
310 }
311 }
312 _ => {
313 ensure_len(data, p, offset_size as usize)?;
315 if is_undefined(data, p, offset_size) {
316 None
317 } else {
318 Some(read_offset(data, p, offset_size)?)
319 }
320 }
321 };
322
323 Ok(DataLayout::Chunked {
324 chunk_dimensions,
325 btree_address,
326 version: 4,
327 chunk_index_type: Some(chunk_index_type),
328 single_chunk_filtered_size,
329 single_chunk_filter_mask,
330 })
331 }
332 3 => {
333 Ok(DataLayout::Virtual { version: 4 })
335 }
336 _ => Err(FormatError::InvalidLayoutClass(layout_class)),
337 }
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn v3_compact() {
347 let mut buf = vec![3u8, 0]; buf.extend_from_slice(&5u16.to_le_bytes()); buf.extend_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE]); let layout = DataLayout::parse(&buf, 8, 8).unwrap();
351 assert_eq!(
352 layout,
353 DataLayout::Compact {
354 data: vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE]
355 }
356 );
357 }
358
359 #[test]
360 fn v3_contiguous() {
361 let mut buf = vec![3u8, 1]; buf.extend_from_slice(&0x1000u64.to_le_bytes()); buf.extend_from_slice(&256u64.to_le_bytes()); let layout = DataLayout::parse(&buf, 8, 8).unwrap();
365 assert_eq!(
366 layout,
367 DataLayout::Contiguous {
368 address: Some(0x1000),
369 size: 256,
370 }
371 );
372 }
373
374 #[test]
375 fn v3_contiguous_undefined_address() {
376 let mut buf = vec![3u8, 1];
377 buf.extend_from_slice(&[0xFF; 8]); buf.extend_from_slice(&0u64.to_le_bytes()); let layout = DataLayout::parse(&buf, 8, 8).unwrap();
380 assert_eq!(
381 layout,
382 DataLayout::Contiguous {
383 address: None,
384 size: 0,
385 }
386 );
387 }
388
389 #[test]
390 fn v3_chunked() {
391 let mut buf = vec![3u8, 2]; buf.push(3); buf.extend_from_slice(&0x2000u64.to_le_bytes()); buf.extend_from_slice(&100u32.to_le_bytes());
396 buf.extend_from_slice(&200u32.to_le_bytes());
397 buf.extend_from_slice(&8u32.to_le_bytes()); let layout = DataLayout::parse(&buf, 8, 8).unwrap();
399 assert_eq!(
400 layout,
401 DataLayout::Chunked {
402 chunk_dimensions: vec![100, 200, 8],
403 btree_address: Some(0x2000),
404 version: 3,
405 chunk_index_type: None,
406 single_chunk_filtered_size: None,
407 single_chunk_filter_mask: None,
408 }
409 );
410 }
411
412 #[test]
413 fn v4_compact() {
414 let mut buf = vec![4u8, 0]; buf.extend_from_slice(&3u16.to_le_bytes());
416 buf.extend_from_slice(&[1, 2, 3]);
417 let layout = DataLayout::parse(&buf, 8, 8).unwrap();
418 assert_eq!(layout, DataLayout::Compact { data: vec![1, 2, 3] });
419 }
420
421 #[test]
422 fn v4_contiguous() {
423 let mut buf = vec![4u8, 1];
424 buf.extend_from_slice(&0x5000u64.to_le_bytes());
425 buf.extend_from_slice(&512u64.to_le_bytes());
426 let layout = DataLayout::parse(&buf, 8, 8).unwrap();
427 assert_eq!(
428 layout,
429 DataLayout::Contiguous {
430 address: Some(0x5000),
431 size: 512,
432 }
433 );
434 }
435
436 #[test]
437 fn v4_chunked_single_chunk_no_filters() {
438 let mut buf = vec![4u8, 2]; buf.push(0); buf.push(2); buf.push(4); buf.extend_from_slice(&64u32.to_le_bytes()); buf.extend_from_slice(&32u32.to_le_bytes()); buf.push(1); buf.extend_from_slice(&0x3000u64.to_le_bytes()); let layout = DataLayout::parse(&buf, 8, 8).unwrap();
447 assert_eq!(
448 layout,
449 DataLayout::Chunked {
450 chunk_dimensions: vec![64, 32],
451 btree_address: Some(0x3000),
452 version: 4,
453 chunk_index_type: Some(1),
454 single_chunk_filtered_size: None,
455 single_chunk_filter_mask: None,
456 }
457 );
458 }
459
460 #[test]
461 fn v4_chunked_single_chunk_with_filters() {
462 let mut buf = vec![4u8, 2]; buf.push(0x02); buf.push(1); buf.push(4); buf.extend_from_slice(&128u32.to_le_bytes()); buf.push(1); buf.extend_from_slice(&1024u64.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0x4000u64.to_le_bytes()); let layout = DataLayout::parse(&buf, 8, 8).unwrap();
473 assert_eq!(
474 layout,
475 DataLayout::Chunked {
476 chunk_dimensions: vec![128],
477 btree_address: Some(0x4000),
478 version: 4,
479 chunk_index_type: Some(1),
480 single_chunk_filtered_size: Some(1024),
481 single_chunk_filter_mask: Some(0),
482 }
483 );
484 }
485
486 #[test]
487 fn invalid_version() {
488 let buf = vec![5u8, 0, 0, 0];
489 let err = DataLayout::parse(&buf, 8, 8).unwrap_err();
490 assert_eq!(err, FormatError::InvalidLayoutVersion(5));
491 }
492
493 #[test]
494 fn invalid_class_v3() {
495 let buf = vec![3u8, 5];
496 let err = DataLayout::parse(&buf, 8, 8).unwrap_err();
497 assert_eq!(err, FormatError::InvalidLayoutClass(5));
498 }
499
500 #[test]
501 fn invalid_class_v4() {
502 let buf = vec![4u8, 7];
503 let err = DataLayout::parse(&buf, 8, 8).unwrap_err();
504 assert_eq!(err, FormatError::InvalidLayoutClass(7));
505 }
506
507 #[test]
508 fn v3_contiguous_4byte_offsets() {
509 let mut buf = vec![3u8, 1];
510 buf.extend_from_slice(&0x800u32.to_le_bytes());
511 buf.extend_from_slice(&24u32.to_le_bytes());
512 let layout = DataLayout::parse(&buf, 4, 4).unwrap();
513 assert_eq!(
514 layout,
515 DataLayout::Contiguous {
516 address: Some(0x800),
517 size: 24,
518 }
519 );
520 }
521
522 #[test]
523 fn v4_virtual() {
524 let buf = vec![4u8, 3];
525 let layout = DataLayout::parse(&buf, 8, 8).unwrap();
526 assert_eq!(layout, DataLayout::Virtual { version: 4 });
527 }
528}