1#[cfg(not(feature = "std"))]
4use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
5
6#[cfg(feature = "std")]
7use std::collections::BTreeMap;
8
9use crate::chunk_cache::ChunkCache;
10use crate::chunked_read::{read_chunked_data, read_chunked_data_cached};
11use crate::data_layout::DataLayout;
12use crate::dataspace::Dataspace;
13use crate::datatype::{Datatype, DatatypeByteOrder};
14use crate::error::FormatError;
15use crate::filter_pipeline::FilterPipeline;
16
17pub fn read_raw_data_zerocopy<'a>(
23 file_data: &'a [u8],
24 layout: &DataLayout,
25 dataspace: &Dataspace,
26 datatype: &Datatype,
27) -> Result<Option<&'a [u8]>, FormatError> {
28 let num_elements = dataspace.num_elements() as usize;
29 let elem_size = datatype.type_size() as usize;
30 let expected_size = num_elements * elem_size;
31
32 match layout {
33 DataLayout::Contiguous { address, size } => {
34 let addr = address.ok_or(FormatError::NoDataAllocated)?;
35 let addr = addr as usize;
36 let sz = *size as usize;
37 if sz != expected_size {
38 return Err(FormatError::DataSizeMismatch {
39 expected: expected_size,
40 actual: sz,
41 });
42 }
43 if addr + sz > file_data.len() {
44 return Err(FormatError::UnexpectedEof {
45 expected: addr + sz,
46 available: file_data.len(),
47 });
48 }
49 Ok(Some(&file_data[addr..addr + sz]))
50 }
51 _ => Ok(None),
52 }
53}
54
55pub fn read_raw_data(
61 file_data: &[u8],
62 layout: &DataLayout,
63 dataspace: &Dataspace,
64 datatype: &Datatype,
65) -> Result<Vec<u8>, FormatError> {
66 read_raw_data_full(file_data, layout, dataspace, datatype, None, 8, 8)
67}
68
69pub fn read_raw_data_full(
71 file_data: &[u8],
72 layout: &DataLayout,
73 dataspace: &Dataspace,
74 datatype: &Datatype,
75 pipeline: Option<&FilterPipeline>,
76 offset_size: u8,
77 length_size: u8,
78) -> Result<Vec<u8>, FormatError> {
79 let num_elements = dataspace.num_elements() as usize;
80 let elem_size = datatype.type_size() as usize;
81 let expected_size = num_elements * elem_size;
82
83 match layout {
84 DataLayout::Compact { data } => {
85 if data.len() != expected_size {
86 return Err(FormatError::DataSizeMismatch {
87 expected: expected_size,
88 actual: data.len(),
89 });
90 }
91 Ok(data.clone())
92 }
93 DataLayout::Contiguous { address, size } => {
94 let addr = address.ok_or(FormatError::NoDataAllocated)?;
95 let addr = addr as usize;
96 let sz = *size as usize;
97 if sz != expected_size {
98 return Err(FormatError::DataSizeMismatch {
99 expected: expected_size,
100 actual: sz,
101 });
102 }
103 if addr + sz > file_data.len() {
104 return Err(FormatError::UnexpectedEof {
105 expected: addr + sz,
106 available: file_data.len(),
107 });
108 }
109 Ok(file_data[addr..addr + sz].to_vec())
110 }
111 DataLayout::Chunked { .. } => {
112 read_chunked_data(file_data, layout, dataspace, datatype, pipeline, offset_size, length_size)
113 }
114 DataLayout::Virtual { .. } => Err(FormatError::UnsupportedVersion(0)),
115 }
116}
117
118pub fn read_raw_data_cached(
124 file_data: &[u8],
125 layout: &DataLayout,
126 dataspace: &Dataspace,
127 datatype: &Datatype,
128 pipeline: Option<&FilterPipeline>,
129 offset_size: u8,
130 length_size: u8,
131 cache: &ChunkCache,
132) -> Result<Vec<u8>, FormatError> {
133 match layout {
134 DataLayout::Chunked { .. } => {
135 read_chunked_data_cached(
136 file_data, layout, dataspace, datatype, pipeline,
137 offset_size, length_size, cache,
138 )
139 }
140 _ => read_raw_data_full(
141 file_data, layout, dataspace, datatype, pipeline,
142 offset_size, length_size,
143 ),
144 }
145}
146
147fn datatype_name(dt: &Datatype) -> &'static str {
148 match dt {
149 Datatype::FixedPoint { .. } => "FixedPoint",
150 Datatype::FloatingPoint { .. } => "FloatingPoint",
151 Datatype::String { .. } => "String",
152 Datatype::Time { .. } => "Time",
153 Datatype::BitField { .. } => "BitField",
154 Datatype::Opaque { .. } => "Opaque",
155 Datatype::Compound { .. } => "Compound",
156 Datatype::Reference { .. } => "Reference",
157 Datatype::Enumeration { .. } => "Enumeration",
158 Datatype::VariableLength { .. } => "VariableLength",
159 Datatype::Array { .. } => "Array",
160 }
161}
162
163fn ensure_numeric(dt: &Datatype, expected: &'static str) -> Result<(), FormatError> {
164 match dt {
165 Datatype::FixedPoint { .. } | Datatype::FloatingPoint { .. } => Ok(()),
166 _ => Err(FormatError::TypeMismatch {
167 expected,
168 actual: datatype_name(dt),
169 }),
170 }
171}
172
173fn get_byte_order(dt: &Datatype) -> DatatypeByteOrder {
174 match dt {
175 Datatype::FixedPoint { byte_order, .. } => byte_order.clone(),
176 Datatype::FloatingPoint { byte_order, .. } => byte_order.clone(),
177 _ => DatatypeByteOrder::LittleEndian,
178 }
179}
180
181fn get_size(dt: &Datatype) -> usize {
182 dt.type_size() as usize
183}
184
185pub fn read_as_f64(raw: &[u8], datatype: &Datatype) -> Result<Vec<f64>, FormatError> {
187 ensure_numeric(datatype, "FloatingPoint or FixedPoint")?;
188 let elem_size = get_size(datatype);
189 if elem_size == 0 || !raw.len().is_multiple_of(elem_size) {
190 return Err(FormatError::DataSizeMismatch {
191 expected: 0,
192 actual: raw.len(),
193 });
194 }
195 let count = raw.len() / elem_size;
196
197 #[cfg(target_endian = "little")]
199 if matches!(datatype, Datatype::FloatingPoint { size: 8, byte_order: DatatypeByteOrder::LittleEndian, .. })
200 {
201 let mut result = vec![0.0f64; count];
202 for (i, val) in result.iter_mut().enumerate() {
204 let off = i * 8;
205 *val = f64::from_le_bytes([
206 raw[off], raw[off+1], raw[off+2], raw[off+3],
207 raw[off+4], raw[off+5], raw[off+6], raw[off+7],
208 ]);
209 }
210 return Ok(result);
211 }
212
213 let order = get_byte_order(datatype);
214 let mut result = Vec::with_capacity(count);
215
216 for i in 0..count {
217 let chunk = &raw[i * elem_size..(i + 1) * elem_size];
218 let val = convert_to_f64(chunk, datatype, &order)?;
219 result.push(val);
220 }
221 Ok(result)
222}
223
224fn convert_to_f64(
225 bytes: &[u8],
226 dt: &Datatype,
227 order: &DatatypeByteOrder,
228) -> Result<f64, FormatError> {
229 match dt {
230 Datatype::FloatingPoint { size, .. } => match size {
231 4 => {
232 let v = read_f32_bytes(bytes, order);
233 Ok(v as f64)
234 }
235 8 => Ok(read_f64_bytes(bytes, order)),
236 _ => Err(FormatError::DataSizeMismatch {
237 expected: 8,
238 actual: *size as usize,
239 }),
240 },
241 Datatype::FixedPoint { size, signed, .. } => {
242 if *signed {
243 let v = read_signed_int(bytes, *size as usize, order);
244 Ok(v as f64)
245 } else {
246 let v = read_unsigned_int(bytes, *size as usize, order);
247 Ok(v as f64)
248 }
249 }
250 _ => Err(FormatError::TypeMismatch {
251 expected: "numeric",
252 actual: datatype_name(dt),
253 }),
254 }
255}
256
257pub fn read_as_i64(raw: &[u8], datatype: &Datatype) -> Result<Vec<i64>, FormatError> {
259 ensure_numeric(datatype, "FixedPoint (signed)")?;
260 let elem_size = get_size(datatype);
261 if elem_size == 0 || !raw.len().is_multiple_of(elem_size) {
262 return Err(FormatError::DataSizeMismatch {
263 expected: 0,
264 actual: raw.len(),
265 });
266 }
267 let count = raw.len() / elem_size;
268 let order = get_byte_order(datatype);
269 let mut result = Vec::with_capacity(count);
270 for i in 0..count {
271 let chunk = &raw[i * elem_size..(i + 1) * elem_size];
272 let v = read_signed_int(chunk, elem_size, &order);
273 result.push(v);
274 }
275 Ok(result)
276}
277
278pub fn read_as_u64(raw: &[u8], datatype: &Datatype) -> Result<Vec<u64>, FormatError> {
280 ensure_numeric(datatype, "FixedPoint (unsigned)")?;
281 let elem_size = get_size(datatype);
282 if elem_size == 0 || !raw.len().is_multiple_of(elem_size) {
283 return Err(FormatError::DataSizeMismatch {
284 expected: 0,
285 actual: raw.len(),
286 });
287 }
288 let count = raw.len() / elem_size;
289 let order = get_byte_order(datatype);
290 let mut result = Vec::with_capacity(count);
291 for i in 0..count {
292 let chunk = &raw[i * elem_size..(i + 1) * elem_size];
293 let v = read_unsigned_int(chunk, elem_size, &order);
294 result.push(v);
295 }
296 Ok(result)
297}
298
299pub fn read_as_f32(raw: &[u8], datatype: &Datatype) -> Result<Vec<f32>, FormatError> {
301 ensure_numeric(datatype, "FloatingPoint")?;
302 let elem_size = get_size(datatype);
303 if elem_size == 0 || !raw.len().is_multiple_of(elem_size) {
304 return Err(FormatError::DataSizeMismatch {
305 expected: 0,
306 actual: raw.len(),
307 });
308 }
309 let count = raw.len() / elem_size;
310 let order = get_byte_order(datatype);
311 let mut result = Vec::with_capacity(count);
312 for i in 0..count {
313 let chunk = &raw[i * elem_size..(i + 1) * elem_size];
314 match datatype {
315 Datatype::FloatingPoint { size: 4, .. } => {
316 result.push(read_f32_bytes(chunk, &order));
317 }
318 Datatype::FloatingPoint { size: 8, .. } => {
319 result.push(read_f64_bytes(chunk, &order) as f32);
320 }
321 Datatype::FixedPoint { signed: true, size, .. } => {
322 result.push(read_signed_int(chunk, *size as usize, &order) as f32);
323 }
324 Datatype::FixedPoint { signed: false, size, .. } => {
325 result.push(read_unsigned_int(chunk, *size as usize, &order) as f32);
326 }
327 _ => {
328 return Err(FormatError::TypeMismatch {
329 expected: "numeric",
330 actual: datatype_name(datatype),
331 });
332 }
333 }
334 }
335 Ok(result)
336}
337
338pub fn read_as_i32(raw: &[u8], datatype: &Datatype) -> Result<Vec<i32>, FormatError> {
340 ensure_numeric(datatype, "FixedPoint")?;
341 let elem_size = get_size(datatype);
342 if elem_size == 0 || !raw.len().is_multiple_of(elem_size) {
343 return Err(FormatError::DataSizeMismatch {
344 expected: 0,
345 actual: raw.len(),
346 });
347 }
348 let count = raw.len() / elem_size;
349 let order = get_byte_order(datatype);
350 let mut result = Vec::with_capacity(count);
351 for i in 0..count {
352 let chunk = &raw[i * elem_size..(i + 1) * elem_size];
353 let v = read_signed_int(chunk, elem_size, &order);
354 result.push(v as i32);
355 }
356 Ok(result)
357}
358
359pub fn read_as_strings(raw: &[u8], datatype: &Datatype) -> Result<Vec<String>, FormatError> {
361 match datatype {
362 Datatype::String { size, padding, .. } => {
363 let elem_size = *size as usize;
364 if elem_size == 0 {
365 return Ok(Vec::new());
366 }
367 if !raw.len().is_multiple_of(elem_size) {
368 return Err(FormatError::DataSizeMismatch {
369 expected: 0,
370 actual: raw.len(),
371 });
372 }
373 let count = raw.len() / elem_size;
374 let mut result = Vec::with_capacity(count);
375 for i in 0..count {
376 let chunk = &raw[i * elem_size..(i + 1) * elem_size];
377 let s = match padding {
378 crate::datatype::StringPadding::NullTerminate => {
379 let end = chunk.iter().position(|&b| b == 0).unwrap_or(chunk.len());
380 String::from_utf8_lossy(&chunk[..end]).into_owned()
381 }
382 crate::datatype::StringPadding::NullPad => {
383 let end = chunk.iter().rposition(|&b| b != 0).map_or(0, |p| p + 1);
384 String::from_utf8_lossy(&chunk[..end]).into_owned()
385 }
386 crate::datatype::StringPadding::SpacePad => {
387 let end = chunk
388 .iter()
389 .rposition(|&b| b != b' ')
390 .map_or(0, |p| p + 1);
391 String::from_utf8_lossy(&chunk[..end]).into_owned()
392 }
393 };
394 result.push(s);
395 }
396 Ok(result)
397 }
398 _ => Err(FormatError::TypeMismatch {
399 expected: "String",
400 actual: datatype_name(datatype),
401 }),
402 }
403}
404
405#[derive(Debug, Clone)]
410pub struct CompoundFieldData {
411 pub name: String,
413 pub datatype: Datatype,
415 pub raw_data: Vec<u8>,
417}
418
419pub fn read_compound_fields(raw: &[u8], datatype: &Datatype) -> Result<Vec<CompoundFieldData>, FormatError> {
424 match datatype {
425 Datatype::Compound { size, members } => {
426 let elem_size = *size as usize;
427 if elem_size == 0 {
428 return Ok(Vec::new());
429 }
430 if !raw.len().is_multiple_of(elem_size) {
431 return Err(FormatError::DataSizeMismatch {
432 expected: 0,
433 actual: raw.len(),
434 });
435 }
436 let count = raw.len() / elem_size;
437 let mut fields = Vec::with_capacity(members.len());
438 for m in members {
439 let field_size = m.datatype.type_size() as usize;
440 let offset = m.byte_offset as usize;
441 let mut field_raw = Vec::with_capacity(count * field_size);
442 for i in 0..count {
443 let elem_start = i * elem_size + offset;
444 field_raw.extend_from_slice(&raw[elem_start..elem_start + field_size]);
445 }
446 fields.push(CompoundFieldData {
447 name: m.name.clone(),
448 datatype: m.datatype.clone(),
449 raw_data: field_raw,
450 });
451 }
452 Ok(fields)
453 }
454 _ => Err(FormatError::TypeMismatch {
455 expected: "Compound",
456 actual: datatype_name(datatype),
457 }),
458 }
459}
460
461pub fn read_compound_field(raw: &[u8], datatype: &Datatype, field_name: &str) -> Result<CompoundFieldData, FormatError> {
463 let fields = read_compound_fields(raw, datatype)?;
464 fields.into_iter()
465 .find(|f| f.name == field_name)
466 .ok_or_else(|| FormatError::PathNotFound(field_name.into()))
467}
468
469#[derive(Debug, Clone)]
473pub struct EnumValue {
474 pub name: String,
476 pub raw_value: Vec<u8>,
478}
479
480pub fn read_enum_values(raw: &[u8], datatype: &Datatype) -> Result<Vec<EnumValue>, FormatError> {
484 match datatype {
485 Datatype::Enumeration { size, members, .. } => {
486 let elem_size = *size as usize;
487 if elem_size == 0 {
488 return Ok(Vec::new());
489 }
490 if !raw.len().is_multiple_of(elem_size) {
491 return Err(FormatError::DataSizeMismatch {
492 expected: 0,
493 actual: raw.len(),
494 });
495 }
496 let count = raw.len() / elem_size;
497 let mut lookup = BTreeMap::new();
499 for m in members {
500 lookup.insert(m.value.clone(), m.name.clone());
501 }
502 let mut result = Vec::with_capacity(count);
503 for i in 0..count {
504 let val_bytes = raw[i * elem_size..(i + 1) * elem_size].to_vec();
505 let name = lookup.get(&val_bytes).cloned().unwrap_or_else(|| {
506 let hex: Vec<String> = val_bytes.iter().map(|b| {
507 let mut s = String::new();
508 core::fmt::Write::write_fmt(&mut s, format_args!("{b:02x}")).ok();
509 s
510 }).collect();
511 let mut result = String::from("UNKNOWN(0x");
512 for h in &hex { result.push_str(h); }
513 result.push(')');
514 result
515 });
516 result.push(EnumValue { name, raw_value: val_bytes });
517 }
518 Ok(result)
519 }
520 _ => Err(FormatError::TypeMismatch {
521 expected: "Enumeration",
522 actual: datatype_name(datatype),
523 }),
524 }
525}
526
527pub fn read_enum_names(raw: &[u8], datatype: &Datatype) -> Result<Vec<String>, FormatError> {
529 let values = read_enum_values(raw, datatype)?;
530 Ok(values.into_iter().map(|v| v.name).collect())
531}
532
533#[derive(Debug, Clone, PartialEq, Eq)]
537pub struct ObjectReference {
538 pub address: u64,
541}
542
543impl ObjectReference {
544 pub fn is_null(&self) -> bool {
546 self.address == u64::MAX
547 }
548}
549
550#[derive(Debug, Clone, PartialEq, Eq)]
552pub struct RegionReference {
553 pub raw: Vec<u8>,
557}
558
559pub fn read_object_references(
570 raw: &[u8],
571 datatype: &Datatype,
572 offset_size: u8,
573) -> Result<Vec<ObjectReference>, FormatError> {
574 match datatype {
575 Datatype::Reference {
576 ref_type: crate::datatype::ReferenceType::Object,
577 size,
578 } => {
579 let elem_size = *size as usize;
580 if elem_size == 0 {
581 return Ok(Vec::new());
582 }
583 if !raw.len().is_multiple_of(elem_size) {
584 return Err(FormatError::DataSizeMismatch {
585 expected: 0,
586 actual: raw.len(),
587 });
588 }
589 let count = raw.len() / elem_size;
590 let mut result = Vec::with_capacity(count);
591 let read_size = (offset_size as usize).min(elem_size);
592 for i in 0..count {
593 let chunk = &raw[i * elem_size..(i + 1) * elem_size];
594 let address = read_ref_address(chunk, read_size);
595 result.push(ObjectReference { address });
596 }
597 Ok(result)
598 }
599 _ => Err(FormatError::TypeMismatch {
600 expected: "Reference(Object)",
601 actual: datatype_name(datatype),
602 }),
603 }
604}
605
606pub fn read_region_references(
617 raw: &[u8],
618 datatype: &Datatype,
619) -> Result<Vec<RegionReference>, FormatError> {
620 match datatype {
621 Datatype::Reference {
622 ref_type: crate::datatype::ReferenceType::DatasetRegion,
623 size,
624 } => {
625 let elem_size = *size as usize;
626 if elem_size == 0 {
627 return Ok(Vec::new());
628 }
629 if !raw.len().is_multiple_of(elem_size) {
630 return Err(FormatError::DataSizeMismatch {
631 expected: 0,
632 actual: raw.len(),
633 });
634 }
635 let count = raw.len() / elem_size;
636 let mut result = Vec::with_capacity(count);
637 for i in 0..count {
638 let chunk = &raw[i * elem_size..(i + 1) * elem_size];
639 result.push(RegionReference {
640 raw: chunk.to_vec(),
641 });
642 }
643 Ok(result)
644 }
645 _ => Err(FormatError::TypeMismatch {
646 expected: "Reference(DatasetRegion)",
647 actual: datatype_name(datatype),
648 }),
649 }
650}
651
652fn read_ref_address(bytes: &[u8], size: usize) -> u64 {
654 let mut buf = [0xFFu8; 8];
655 let len = size.min(bytes.len()).min(8);
656 buf[..len].copy_from_slice(&bytes[..len]);
657 if len < 8 && bytes[..len].iter().all(|&b| b == 0xFF) {
659 return u64::MAX;
660 }
661 if len < 8 && !bytes[..len].iter().all(|&b| b == 0xFF) {
663 for b in buf[len..].iter_mut() {
664 *b = 0;
665 }
666 }
667 u64::from_le_bytes(buf)
668}
669
670pub fn read_array_flat(raw: &[u8], datatype: &Datatype) -> Result<(Vec<u8>, Datatype, Vec<u32>), FormatError> {
679 match datatype {
680 Datatype::Array { base_type, dimensions } => {
681 Ok((raw.to_vec(), *base_type.clone(), dimensions.clone()))
682 }
683 _ => Err(FormatError::TypeMismatch {
684 expected: "Array",
685 actual: datatype_name(datatype),
686 }),
687 }
688}
689
690fn reorder_bytes(bytes: &[u8], order: &DatatypeByteOrder) -> [u8; 8] {
693 let mut buf = [0u8; 8];
694 let len = bytes.len().min(8);
695 match order {
696 DatatypeByteOrder::LittleEndian | DatatypeByteOrder::Vax => {
697 buf[..len].copy_from_slice(&bytes[..len]);
698 }
699 DatatypeByteOrder::BigEndian => {
700 for i in 0..len {
702 buf[i] = bytes[len - 1 - i];
703 }
704 }
705 }
706 buf
707}
708
709fn read_f64_bytes(bytes: &[u8], order: &DatatypeByteOrder) -> f64 {
710 let buf = reorder_bytes(bytes, order);
711 f64::from_le_bytes(buf)
712}
713
714fn read_f32_bytes(bytes: &[u8], order: &DatatypeByteOrder) -> f32 {
715 let mut buf = [0u8; 4];
716 let len = bytes.len().min(4);
717 match order {
718 DatatypeByteOrder::LittleEndian | DatatypeByteOrder::Vax => {
719 buf[..len].copy_from_slice(&bytes[..len]);
720 }
721 DatatypeByteOrder::BigEndian => {
722 for i in 0..len {
723 buf[i] = bytes[len - 1 - i];
724 }
725 }
726 }
727 f32::from_le_bytes(buf)
728}
729
730fn read_unsigned_int(bytes: &[u8], size: usize, order: &DatatypeByteOrder) -> u64 {
731 let buf = reorder_bytes(bytes, order);
732 match size {
733 1 => buf[0] as u64,
734 2 => u16::from_le_bytes([buf[0], buf[1]]) as u64,
735 4 => u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]) as u64,
736 8 => u64::from_le_bytes(buf),
737 _ => {
738 let mut val = 0u64;
740 for (i, &byte) in buf.iter().enumerate().take(size.min(8)) {
741 val |= (byte as u64) << (i * 8);
742 }
743 val
744 }
745 }
746}
747
748fn read_signed_int(bytes: &[u8], size: usize, order: &DatatypeByteOrder) -> i64 {
749 let buf = reorder_bytes(bytes, order);
750 match size {
751 1 => buf[0] as i8 as i64,
752 2 => i16::from_le_bytes([buf[0], buf[1]]) as i64,
753 4 => i32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]) as i64,
754 8 => i64::from_le_bytes(buf),
755 _ => {
756 let u = read_unsigned_int(bytes, size, order);
757 let shift = 64 - (size * 8);
759 ((u as i64) << shift) >> shift
760 }
761 }
762}
763
764#[cfg(test)]
765mod tests {
766 use super::*;
767 use crate::dataspace::{Dataspace, DataspaceType};
768 use crate::datatype::{CharacterSet, StringPadding};
769
770 fn make_f64_le_type() -> Datatype {
771 Datatype::FloatingPoint {
772 size: 8,
773 byte_order: DatatypeByteOrder::LittleEndian,
774 bit_offset: 0,
775 bit_precision: 64,
776 exponent_location: 52,
777 exponent_size: 11,
778 mantissa_location: 0,
779 mantissa_size: 52,
780 exponent_bias: 1023,
781 }
782 }
783
784 fn make_f32_be_type() -> Datatype {
785 Datatype::FloatingPoint {
786 size: 4,
787 byte_order: DatatypeByteOrder::BigEndian,
788 bit_offset: 0,
789 bit_precision: 32,
790 exponent_location: 23,
791 exponent_size: 8,
792 mantissa_location: 0,
793 mantissa_size: 23,
794 exponent_bias: 127,
795 }
796 }
797
798 fn make_i32_le_type() -> Datatype {
799 Datatype::FixedPoint {
800 size: 4,
801 byte_order: DatatypeByteOrder::LittleEndian,
802 signed: true,
803 bit_offset: 0,
804 bit_precision: 32,
805 }
806 }
807
808 fn make_i16_le_type() -> Datatype {
809 Datatype::FixedPoint {
810 size: 2,
811 byte_order: DatatypeByteOrder::LittleEndian,
812 signed: true,
813 bit_offset: 0,
814 bit_precision: 16,
815 }
816 }
817
818 fn make_u8_type() -> Datatype {
819 Datatype::FixedPoint {
820 size: 1,
821 byte_order: DatatypeByteOrder::LittleEndian,
822 signed: false,
823 bit_offset: 0,
824 bit_precision: 8,
825 }
826 }
827
828 fn make_simple_dataspace(dims: &[u64]) -> Dataspace {
829 Dataspace {
830 space_type: DataspaceType::Simple,
831 rank: dims.len() as u8,
832 dimensions: dims.to_vec(),
833 max_dimensions: None,
834 }
835 }
836
837 #[test]
838 fn read_f64_compact() {
839 let dt = make_f64_le_type();
840 let ds = make_simple_dataspace(&[3]);
841 let mut data = Vec::new();
842 data.extend_from_slice(&1.0f64.to_le_bytes());
843 data.extend_from_slice(&2.0f64.to_le_bytes());
844 data.extend_from_slice(&3.0f64.to_le_bytes());
845 let layout = DataLayout::Compact { data: data.clone() };
846 let raw = read_raw_data(&[], &layout, &ds, &dt).unwrap();
847 assert_eq!(raw, data);
848 let values = read_as_f64(&raw, &dt).unwrap();
849 assert_eq!(values, vec![1.0, 2.0, 3.0]);
850 }
851
852 #[test]
853 fn read_i32_contiguous() {
854 let dt = make_i32_le_type();
855 let ds = make_simple_dataspace(&[4]);
856 let mut file_data = vec![0u8; 1024];
857 let offset = 256usize;
858 let vals: Vec<i32> = vec![10, -20, 30, -40];
859 for (i, v) in vals.iter().enumerate() {
860 let bytes = v.to_le_bytes();
861 file_data[offset + i * 4..offset + i * 4 + 4].copy_from_slice(&bytes);
862 }
863 let layout = DataLayout::Contiguous {
864 address: Some(offset as u64),
865 size: 16,
866 };
867 let raw = read_raw_data(&file_data, &layout, &ds, &dt).unwrap();
868 let result = read_as_i32(&raw, &dt).unwrap();
869 assert_eq!(result, vec![10, -20, 30, -40]);
870 }
871
872 #[test]
873 fn read_u8_data() {
874 let dt = make_u8_type();
875 let ds = make_simple_dataspace(&[5]);
876 let data = vec![10u8, 20, 30, 40, 50];
877 let layout = DataLayout::Compact { data: data.clone() };
878 let raw = read_raw_data(&[], &layout, &ds, &dt).unwrap();
879 let result = read_as_u64(&raw, &dt).unwrap();
880 assert_eq!(result, vec![10, 20, 30, 40, 50]);
881 }
882
883 #[test]
884 fn read_f32_be() {
885 let dt = make_f32_be_type();
886 let ds = make_simple_dataspace(&[2]);
887 let mut data = Vec::new();
888 data.extend_from_slice(&1.5f32.to_be_bytes());
890 data.extend_from_slice(&2.5f32.to_be_bytes());
891 let layout = DataLayout::Compact { data: data.clone() };
892 let raw = read_raw_data(&[], &layout, &ds, &dt).unwrap();
893 let result = read_as_f32(&raw, &dt).unwrap();
894 assert_eq!(result, vec![1.5, 2.5]);
895 }
896
897 #[test]
898 fn read_i16_le() {
899 let dt = make_i16_le_type();
900 let ds = make_simple_dataspace(&[3]);
901 let mut data = Vec::new();
902 data.extend_from_slice(&(-100i16).to_le_bytes());
903 data.extend_from_slice(&200i16.to_le_bytes());
904 data.extend_from_slice(&(-300i16).to_le_bytes());
905 let layout = DataLayout::Compact { data: data.clone() };
906 let raw = read_raw_data(&[], &layout, &ds, &dt).unwrap();
907 let result = read_as_i64(&raw, &dt).unwrap();
908 assert_eq!(result, vec![-100, 200, -300]);
909 }
910
911 #[test]
912 fn read_strings_compact() {
913 let dt = Datatype::String {
914 size: 5,
915 padding: StringPadding::NullPad,
916 charset: CharacterSet::Ascii,
917 };
918 let ds = make_simple_dataspace(&[2]);
919 let mut data = Vec::new();
920 data.extend_from_slice(b"hello");
921 data.extend_from_slice(b"hi\0\0\0");
922 let layout = DataLayout::Compact { data: data.clone() };
923 let raw = read_raw_data(&[], &layout, &ds, &dt).unwrap();
924 let result = read_as_strings(&raw, &dt).unwrap();
925 assert_eq!(result, vec!["hello", "hi"]);
926 }
927
928 #[test]
929 fn type_mismatch_f64_on_string() {
930 let dt = Datatype::String {
931 size: 4,
932 padding: StringPadding::NullTerminate,
933 charset: CharacterSet::Ascii,
934 };
935 let raw = vec![0u8; 8];
936 let err = read_as_f64(&raw, &dt).unwrap_err();
937 assert!(matches!(err, FormatError::TypeMismatch { .. }));
938 }
939
940 #[test]
941 fn size_mismatch_compact() {
942 let dt = make_f64_le_type();
943 let ds = make_simple_dataspace(&[3]);
944 let data = vec![0u8; 16]; let layout = DataLayout::Compact { data };
946 let err = read_raw_data(&[], &layout, &ds, &dt).unwrap_err();
947 assert!(matches!(err, FormatError::DataSizeMismatch { .. }));
948 }
949
950 #[test]
951 fn no_data_allocated() {
952 let dt = make_f64_le_type();
953 let ds = make_simple_dataspace(&[3]);
954 let layout = DataLayout::Contiguous {
955 address: None,
956 size: 24,
957 };
958 let err = read_raw_data(&[], &layout, &ds, &dt).unwrap_err();
959 assert!(matches!(err, FormatError::NoDataAllocated));
960 }
961
962 #[test]
963 fn string_type_mismatch_on_read_as_strings() {
964 let dt = make_i32_le_type();
965 let raw = vec![0u8; 8];
966 let err = read_as_strings(&raw, &dt).unwrap_err();
967 assert!(matches!(err, FormatError::TypeMismatch { .. }));
968 }
969
970 #[test]
971 fn read_f64_from_i32() {
972 let dt = make_i32_le_type();
974 let mut raw = Vec::new();
975 raw.extend_from_slice(&42i32.to_le_bytes());
976 raw.extend_from_slice(&(-7i32).to_le_bytes());
977 let result = read_as_f64(&raw, &dt).unwrap();
978 assert_eq!(result, vec![42.0, -7.0]);
979 }
980
981 #[test]
982 fn read_strings_space_padded() {
983 let dt = Datatype::String {
984 size: 8,
985 padding: StringPadding::SpacePad,
986 charset: CharacterSet::Ascii,
987 };
988 let raw = b"hello world ";
989 let result = read_as_strings(raw, &dt).unwrap();
990 assert_eq!(result, vec!["hello", "world"]);
991 }
992
993 #[test]
994 fn read_strings_null_terminated() {
995 let dt = Datatype::String {
996 size: 6,
997 padding: StringPadding::NullTerminate,
998 charset: CharacterSet::Ascii,
999 };
1000 let raw = b"abc\0\0\0de\0\0\0\0";
1001 let result = read_as_strings(raw, &dt).unwrap();
1002 assert_eq!(result, vec!["abc", "de"]);
1003 }
1004
1005 #[test]
1006 fn read_compound_two_fields() {
1007 use crate::datatype::CompoundMember;
1008 let dt = Datatype::Compound {
1010 size: 12,
1011 members: vec![
1012 CompoundMember {
1013 name: "x".to_string(),
1014 byte_offset: 0,
1015 datatype: make_f64_le_type(),
1016 },
1017 CompoundMember {
1018 name: "id".to_string(),
1019 byte_offset: 8,
1020 datatype: make_i32_le_type(),
1021 },
1022 ],
1023 };
1024 let mut raw = Vec::new();
1026 raw.extend_from_slice(&1.5f64.to_le_bytes());
1027 raw.extend_from_slice(&10i32.to_le_bytes());
1028 raw.extend_from_slice(&2.5f64.to_le_bytes());
1029 raw.extend_from_slice(&20i32.to_le_bytes());
1030
1031 let fields = read_compound_fields(&raw, &dt).unwrap();
1032 assert_eq!(fields.len(), 2);
1033 assert_eq!(fields[0].name, "x");
1034 let x_vals = read_as_f64(&fields[0].raw_data, &fields[0].datatype).unwrap();
1035 assert_eq!(x_vals, vec![1.5, 2.5]);
1036
1037 assert_eq!(fields[1].name, "id");
1038 let id_vals = read_as_i32(&fields[1].raw_data, &fields[1].datatype).unwrap();
1039 assert_eq!(id_vals, vec![10, 20]);
1040 }
1041
1042 #[test]
1043 fn read_compound_single_field_by_name() {
1044 use crate::datatype::CompoundMember;
1045 let dt = Datatype::Compound {
1046 size: 12,
1047 members: vec![
1048 CompoundMember {
1049 name: "x".to_string(),
1050 byte_offset: 0,
1051 datatype: make_f64_le_type(),
1052 },
1053 CompoundMember {
1054 name: "id".to_string(),
1055 byte_offset: 8,
1056 datatype: make_i32_le_type(),
1057 },
1058 ],
1059 };
1060 let mut raw = Vec::new();
1061 raw.extend_from_slice(&3.14f64.to_le_bytes());
1062 raw.extend_from_slice(&42i32.to_le_bytes());
1063
1064 let field = read_compound_field(&raw, &dt, "id").unwrap();
1065 let vals = read_as_i32(&field.raw_data, &field.datatype).unwrap();
1066 assert_eq!(vals, vec![42]);
1067
1068 let err = read_compound_field(&raw, &dt, "missing").unwrap_err();
1070 assert!(matches!(err, FormatError::PathNotFound(_)));
1071 }
1072
1073 #[test]
1074 fn read_enum_values_basic() {
1075 use crate::datatype::EnumMember;
1076 let dt = Datatype::Enumeration {
1077 size: 4,
1078 base_type: Box::new(make_i32_le_type()),
1079 members: vec![
1080 EnumMember { name: "RED".to_string(), value: 0i32.to_le_bytes().to_vec() },
1081 EnumMember { name: "GREEN".to_string(), value: 1i32.to_le_bytes().to_vec() },
1082 EnumMember { name: "BLUE".to_string(), value: 2i32.to_le_bytes().to_vec() },
1083 ],
1084 };
1085 let mut raw = Vec::new();
1086 raw.extend_from_slice(&1i32.to_le_bytes()); raw.extend_from_slice(&0i32.to_le_bytes()); raw.extend_from_slice(&2i32.to_le_bytes()); raw.extend_from_slice(&99i32.to_le_bytes()); let names = read_enum_names(&raw, &dt).unwrap();
1092 assert_eq!(names[0], "GREEN");
1093 assert_eq!(names[1], "RED");
1094 assert_eq!(names[2], "BLUE");
1095 assert!(names[3].starts_with("UNKNOWN("));
1096 }
1097
1098 #[test]
1099 fn read_array_flat_basic() {
1100 let dt = Datatype::Array {
1102 base_type: Box::new(make_f64_le_type()),
1103 dimensions: vec![3],
1104 };
1105 let mut raw = Vec::new();
1106 for v in &[1.0f64, 2.0, 3.0] {
1107 raw.extend_from_slice(&v.to_le_bytes());
1108 }
1109 let (data, base_dt, dims) = read_array_flat(&raw, &dt).unwrap();
1110 assert_eq!(dims, vec![3]);
1111 let vals = read_as_f64(&data, &base_dt).unwrap();
1112 assert_eq!(vals, vec![1.0, 2.0, 3.0]);
1113 }
1114
1115 #[test]
1116 fn read_object_references_basic() {
1117 use crate::datatype::ReferenceType;
1118 let dt = Datatype::Reference {
1119 size: 8,
1120 ref_type: ReferenceType::Object,
1121 };
1122 let mut raw = Vec::new();
1123 raw.extend_from_slice(&1024u64.to_le_bytes()); raw.extend_from_slice(&u64::MAX.to_le_bytes()); raw.extend_from_slice(&2048u64.to_le_bytes()); let refs = read_object_references(&raw, &dt, 8).unwrap();
1128 assert_eq!(refs.len(), 3);
1129 assert_eq!(refs[0].address, 1024);
1130 assert!(!refs[0].is_null());
1131 assert!(refs[1].is_null());
1132 assert_eq!(refs[2].address, 2048);
1133 }
1134
1135 #[test]
1136 fn read_object_references_4byte_offset() {
1137 use crate::datatype::ReferenceType;
1138 let dt = Datatype::Reference {
1139 size: 4,
1140 ref_type: ReferenceType::Object,
1141 };
1142 let mut raw = Vec::new();
1143 raw.extend_from_slice(&512u32.to_le_bytes());
1144 raw.extend_from_slice(&u32::MAX.to_le_bytes()); let refs = read_object_references(&raw, &dt, 4).unwrap();
1147 assert_eq!(refs.len(), 2);
1148 assert_eq!(refs[0].address, 512);
1149 assert!(refs[1].is_null());
1150 }
1151
1152 #[test]
1153 fn read_object_references_type_mismatch() {
1154 let dt = make_f64_le_type();
1155 let raw = vec![0u8; 8];
1156 let err = read_object_references(&raw, &dt, 8).unwrap_err();
1157 assert!(matches!(err, FormatError::TypeMismatch { .. }));
1158 }
1159
1160 #[test]
1161 fn read_region_references_basic() {
1162 use crate::datatype::ReferenceType;
1163 let dt = Datatype::Reference {
1164 size: 12,
1165 ref_type: ReferenceType::DatasetRegion,
1166 };
1167 let raw = vec![0xABu8; 24]; let refs = read_region_references(&raw, &dt).unwrap();
1170 assert_eq!(refs.len(), 2);
1171 assert_eq!(refs[0].raw.len(), 12);
1172 assert_eq!(refs[1].raw.len(), 12);
1173 }
1174
1175 #[test]
1176 fn read_region_references_type_mismatch() {
1177 let dt = make_i32_le_type();
1178 let raw = vec![0u8; 12];
1179 let err = read_region_references(&raw, &dt).unwrap_err();
1180 assert!(matches!(err, FormatError::TypeMismatch { .. }));
1181 }
1182
1183 #[test]
1184 fn zerocopy_contiguous_returns_slice_into_file_data() {
1185 let dt = make_f64_le_type();
1186 let ds = make_simple_dataspace(&[3]);
1187 let mut file_data = vec![0u8; 1024];
1188 let offset = 256usize;
1189 let vals = [1.0f64, 2.0, 3.0];
1190 for (i, v) in vals.iter().enumerate() {
1191 file_data[offset + i * 8..offset + i * 8 + 8].copy_from_slice(&v.to_le_bytes());
1192 }
1193 let layout = DataLayout::Contiguous {
1194 address: Some(offset as u64),
1195 size: 24,
1196 };
1197 let result = read_raw_data_zerocopy(&file_data, &layout, &ds, &dt).unwrap();
1198 let slice = result.expect("contiguous should return Some");
1199 let file_range = file_data.as_ptr_range();
1201 assert!(file_range.contains(&slice.as_ptr()));
1202 assert_eq!(slice.len(), 24);
1203 let values = read_as_f64(slice, &dt).unwrap();
1205 assert_eq!(values, vec![1.0, 2.0, 3.0]);
1206 }
1207
1208 #[test]
1209 fn zerocopy_compact_returns_none() {
1210 let dt = make_f64_le_type();
1211 let ds = make_simple_dataspace(&[1]);
1212 let data = vec![0u8; 8];
1213 let layout = DataLayout::Compact { data };
1214 let result = read_raw_data_zerocopy(&[], &layout, &ds, &dt).unwrap();
1215 assert!(result.is_none());
1216 }
1217
1218 #[test]
1219 fn zerocopy_no_data_allocated() {
1220 let dt = make_f64_le_type();
1221 let ds = make_simple_dataspace(&[1]);
1222 let layout = DataLayout::Contiguous {
1223 address: None,
1224 size: 8,
1225 };
1226 let err = read_raw_data_zerocopy(&[], &layout, &ds, &dt).unwrap_err();
1227 assert!(matches!(err, FormatError::NoDataAllocated));
1228 }
1229
1230 #[test]
1231 fn zerocopy_size_mismatch() {
1232 let dt = make_f64_le_type();
1233 let ds = make_simple_dataspace(&[3]);
1234 let file_data = vec![0u8; 1024];
1235 let layout = DataLayout::Contiguous {
1236 address: Some(0),
1237 size: 16, };
1239 let err = read_raw_data_zerocopy(&file_data, &layout, &ds, &dt).unwrap_err();
1240 assert!(matches!(err, FormatError::DataSizeMismatch { .. }));
1241 }
1242}