1use crate::format::bytes::read_le_uint as read_size;
12use crate::format::{FormatContext, FormatError, FormatResult};
13
14const VERSION: u8 = 2;
15const FLAG_MAX_DIMS: u8 = 0x01;
16
17const DS_TYPE_SCALAR: u8 = 0;
19const DS_TYPE_SIMPLE: u8 = 1;
20const DS_TYPE_NULL: u8 = 2;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum DataspaceClass {
30 Scalar,
32 Simple,
34 Null,
37}
38
39#[derive(Debug, Clone, PartialEq)]
41pub struct DataspaceMessage {
42 pub class: DataspaceClass,
44 pub dims: Vec<u64>,
46 pub max_dims: Option<Vec<u64>>,
62}
63
64impl DataspaceMessage {
65 pub fn scalar() -> Self {
69 Self {
70 class: DataspaceClass::Scalar,
71 dims: Vec::new(),
72 max_dims: None,
73 }
74 }
75
76 pub fn null() -> Self {
79 Self {
80 class: DataspaceClass::Null,
81 dims: Vec::new(),
82 max_dims: None,
83 }
84 }
85
86 pub fn simple(dims: &[u64]) -> Self {
95 let class = Self::class_for_rank(dims.len());
96 Self {
97 class,
98 dims: dims.to_vec(),
99 max_dims: match class {
100 DataspaceClass::Simple => Some(dims.to_vec()),
101 DataspaceClass::Scalar | DataspaceClass::Null => None,
102 },
103 }
104 }
105
106 pub fn unlimited(current: &[u64]) -> Self {
108 Self {
109 class: Self::class_for_rank(current.len()),
110 dims: current.to_vec(),
111 max_dims: Some(vec![u64::MAX; current.len()]),
112 }
113 }
114
115 fn class_for_rank(ndims: usize) -> DataspaceClass {
118 if ndims == 0 {
119 DataspaceClass::Scalar
120 } else {
121 DataspaceClass::Simple
122 }
123 }
124
125 pub fn is_null(&self) -> bool {
127 self.class == DataspaceClass::Null
128 }
129
130 pub fn element_count(&self) -> Option<u64> {
134 match self.class {
135 DataspaceClass::Null => Some(0),
136 DataspaceClass::Scalar => Some(1),
137 DataspaceClass::Simple => self.dims.iter().try_fold(1u64, |n, &d| n.checked_mul(d)),
138 }
139 }
140
141 pub fn encode(&self, ctx: &FormatContext) -> Vec<u8> {
144 self.encode_for(ctx, crate::format::ObjectFormat::Modern)
145 }
146
147 fn version_for(&self, format: crate::format::ObjectFormat) -> u8 {
153 let needed = if self.class == DataspaceClass::Null {
154 2
155 } else {
156 1
157 };
158 needed.max(format.dataspace_version())
159 }
160
161 pub fn encode_for(&self, ctx: &FormatContext, format: crate::format::ObjectFormat) -> Vec<u8> {
167 let version = self.version_for(format);
168 let ndims = self.dims.len();
169 let ss = ctx.sizeof_size as usize;
170 let max_dims = self.max_dims.as_deref();
174 let has_max = max_dims.is_some();
175 let flags: u8 = if has_max { FLAG_MAX_DIMS } else { 0 };
176
177 let ds_type = match self.class {
178 DataspaceClass::Scalar => DS_TYPE_SCALAR,
179 DataspaceClass::Simple => DS_TYPE_SIMPLE,
180 DataspaceClass::Null => DS_TYPE_NULL,
181 };
182
183 let prefix_len = if version == 1 { 8 } else { 4 };
184 let body_len = prefix_len + ndims * ss + if has_max { ndims * ss } else { 0 };
185 let mut buf = Vec::with_capacity(body_len);
186
187 buf.push(version);
188 buf.push(ndims as u8);
189 buf.push(flags);
190 if version == 1 {
191 buf.extend_from_slice(&[0u8; 5]);
194 } else {
195 buf.push(ds_type);
196 }
197
198 for &d in &self.dims {
200 buf.extend_from_slice(&d.to_le_bytes()[..ss]);
201 }
202
203 if let Some(maxes) = max_dims {
205 for &m in maxes {
206 buf.extend_from_slice(&m.to_le_bytes()[..ss]);
207 }
208 }
209
210 buf
211 }
212
213 pub fn decode(buf: &[u8], ctx: &FormatContext) -> FormatResult<(Self, usize)> {
216 if buf.len() < 4 {
217 return Err(FormatError::BufferTooShort {
218 needed: 4,
219 available: buf.len(),
220 });
221 }
222
223 let version = buf[0];
224 match version {
225 1 => Self::decode_v1(buf, ctx),
226 VERSION => Self::decode_v2(buf, ctx),
227 _ => Err(FormatError::InvalidVersion(version)),
228 }
229 }
230
231 fn decode_v2(buf: &[u8], ctx: &FormatContext) -> FormatResult<(Self, usize)> {
233 let ndims = buf[1] as usize;
234 let flags = buf[2];
235 let class = match buf[3] {
236 DS_TYPE_SCALAR => DataspaceClass::Scalar,
237 DS_TYPE_SIMPLE => DataspaceClass::Simple,
238 DS_TYPE_NULL => DataspaceClass::Null,
239 other => {
240 return Err(FormatError::InvalidData(format!(
241 "dataspace type byte {other} is not scalar(0)/simple(1)/null(2)"
242 )))
243 }
244 };
245 let has_max = (flags & FLAG_MAX_DIMS) != 0;
246 let ss = ctx.sizeof_size as usize;
247
248 let needed = 4 + ndims * ss + if has_max { ndims * ss } else { 0 };
249 if buf.len() < needed {
250 return Err(FormatError::BufferTooShort {
251 needed,
252 available: buf.len(),
253 });
254 }
255
256 let mut pos = 4;
257
258 let mut dims = Vec::with_capacity(ndims);
259 for _ in 0..ndims {
260 dims.push(read_size(&buf[pos..], ss));
261 pos += ss;
262 }
263
264 let max_dims = if has_max {
265 let mut v = Vec::with_capacity(ndims);
266 for _ in 0..ndims {
267 v.push(read_size(&buf[pos..], ss));
268 pos += ss;
269 }
270 Some(v)
271 } else {
272 None
273 };
274
275 Ok((
276 Self {
277 class,
278 dims,
279 max_dims,
280 },
281 pos,
282 ))
283 }
284
285 fn decode_v1(buf: &[u8], ctx: &FormatContext) -> FormatResult<(Self, usize)> {
299 if buf.len() < 8 {
300 return Err(FormatError::BufferTooShort {
301 needed: 8,
302 available: buf.len(),
303 });
304 }
305
306 let ndims = buf[1] as usize;
307 let flags = buf[2];
308 let has_max = (flags & FLAG_MAX_DIMS) != 0;
309 let has_perm = (flags & 0x02) != 0;
310 let ss = ctx.sizeof_size as usize;
311
312 let mut needed = 8 + ndims * ss;
314 if has_max {
315 needed += ndims * ss;
316 }
317 if has_perm {
318 needed += ndims * ss;
319 }
320 if buf.len() < needed {
321 return Err(FormatError::BufferTooShort {
322 needed,
323 available: buf.len(),
324 });
325 }
326
327 let mut pos = 8; let mut dims = Vec::with_capacity(ndims);
330 for _ in 0..ndims {
331 dims.push(read_size(&buf[pos..], ss));
332 pos += ss;
333 }
334
335 let max_dims = if has_max {
336 let mut v = Vec::with_capacity(ndims);
337 for _ in 0..ndims {
338 v.push(read_size(&buf[pos..], ss));
339 pos += ss;
340 }
341 Some(v)
342 } else {
343 None
344 };
345
346 if has_perm {
348 pos += ndims * ss;
349 }
350
351 Ok((
355 Self {
356 class: Self::class_for_rank(ndims),
357 dims,
358 max_dims,
359 },
360 pos,
361 ))
362 }
363}
364
365#[cfg(test)]
368mod tests {
369 use super::*;
370
371 fn ctx8() -> FormatContext {
372 FormatContext {
373 sizeof_addr: 8,
374 sizeof_size: 8,
375 }
376 }
377
378 fn ctx4() -> FormatContext {
379 FormatContext {
380 sizeof_addr: 4,
381 sizeof_size: 4,
382 }
383 }
384
385 #[test]
386 fn roundtrip_scalar() {
387 let msg = DataspaceMessage::scalar();
388 let encoded = msg.encode(&ctx8());
389 assert_eq!(encoded.len(), 4); let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
391 assert_eq!(consumed, 4);
392 assert_eq!(decoded, msg);
393 }
394
395 #[test]
400 fn roundtrip_simple_1d() {
401 let msg = DataspaceMessage::simple(&[100]);
402 let encoded = msg.encode(&ctx8());
403 assert_eq!(encoded.len(), 20);
405 assert_eq!(encoded[2], FLAG_MAX_DIMS);
406 let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
407 assert_eq!(consumed, 20);
408 assert_eq!(decoded.class, msg.class);
409 assert_eq!(decoded.dims, msg.dims);
410 assert_eq!(decoded.max_dims, Some(vec![100]));
411 }
412
413 #[test]
414 fn roundtrip_simple_3d_ctx4() {
415 let msg = DataspaceMessage::simple(&[10, 20, 30]);
416 let encoded = msg.encode(&ctx4());
417 assert_eq!(encoded.len(), 28);
419 let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx4()).unwrap();
420 assert_eq!(consumed, 28);
421 assert_eq!(decoded.class, msg.class);
422 assert_eq!(decoded.dims, msg.dims);
423 assert_eq!(decoded.max_dims, Some(vec![10, 20, 30]));
424 }
425
426 #[test]
427 fn roundtrip_unlimited() {
428 let msg = DataspaceMessage::unlimited(&[5, 10]);
429 let encoded = msg.encode(&ctx8());
430 assert_eq!(encoded.len(), 36);
432 let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
433 assert_eq!(consumed, 36);
434 assert_eq!(decoded, msg);
435 assert_eq!(decoded.max_dims.as_ref().unwrap(), &vec![u64::MAX; 2]);
436 }
437
438 #[test]
439 fn roundtrip_partial_max() {
440 let msg = DataspaceMessage {
441 class: DataspaceClass::Simple,
442 dims: vec![3, 4],
443 max_dims: Some(vec![100, u64::MAX]),
444 };
445 let encoded = msg.encode(&ctx8());
446 let (decoded, _) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
447 assert_eq!(decoded, msg);
448 }
449
450 #[test]
451 fn decode_bad_version() {
452 let buf = [99u8, 0, 0, 0]; let err = DataspaceMessage::decode(&buf, &ctx8()).unwrap_err();
454 match err {
455 FormatError::InvalidVersion(99) => {}
456 other => panic!("unexpected error: {:?}", other),
457 }
458 }
459
460 #[test]
461 fn decode_v1_simple_1d() {
462 let mut buf = vec![
464 1, 1, 0, 0, ];
469 buf.extend_from_slice(&[0u8; 4]); buf.extend_from_slice(&100u64.to_le_bytes()); let (msg, consumed) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
473 assert_eq!(consumed, 16); assert_eq!(msg.dims, vec![100]);
475 assert_eq!(msg.max_dims, None);
476 }
477
478 #[test]
486 fn a_decoded_message_with_no_maximum_re_encodes_without_one() {
487 let mut buf = vec![1, 1, 0, 0];
488 buf.extend_from_slice(&[0u8; 4]);
489 buf.extend_from_slice(&100u64.to_le_bytes());
490
491 let (msg, _) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
492 assert_eq!(msg.max_dims, None);
493 assert_eq!(
494 msg.encode_for(&ctx8(), crate::format::ObjectFormat::Legacy),
495 buf
496 );
497
498 let modern = msg.encode_for(&ctx8(), crate::format::ObjectFormat::Modern);
499 assert_eq!(modern.len(), 12, "4-byte prefix and one dimension");
500 assert_eq!(modern[2], 0, "H5S_VALID_MAX stays clear");
501 let (round, _) = DataspaceMessage::decode(&modern, &ctx8()).unwrap();
502 assert_eq!(round, msg);
503 }
504
505 #[test]
509 fn a_constructed_simple_dataspace_carries_its_maximum() {
510 assert_eq!(DataspaceMessage::simple(&[3, 4]).max_dims, Some(vec![3, 4]));
511 assert_eq!(DataspaceMessage::simple(&[]).max_dims, None);
512 assert_eq!(DataspaceMessage::scalar().max_dims, None);
513 assert_eq!(DataspaceMessage::null().max_dims, None);
514 }
515
516 #[test]
517 fn decode_v1_with_max_dims() {
518 let mut buf = vec![
519 1, 2, 1, 0, ];
524 buf.extend_from_slice(&[0u8; 4]); buf.extend_from_slice(&10u64.to_le_bytes()); buf.extend_from_slice(&20u64.to_le_bytes()); buf.extend_from_slice(&u64::MAX.to_le_bytes()); buf.extend_from_slice(&100u64.to_le_bytes()); let (msg, consumed) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
531 assert_eq!(consumed, 40); assert_eq!(msg.dims, vec![10, 20]);
533 assert_eq!(msg.max_dims, Some(vec![u64::MAX, 100]));
534 }
535
536 #[test]
537 fn decode_buffer_too_short() {
538 let buf = [2u8, 1, 0]; let err = DataspaceMessage::decode(&buf, &ctx8()).unwrap_err();
540 match err {
541 FormatError::BufferTooShort { .. } => {}
542 other => panic!("unexpected error: {:?}", other),
543 }
544 }
545
546 #[test]
547 fn decode_buffer_too_short_for_dims() {
548 let buf = [2u8, 1, 0, 1, 0, 0];
550 let err = DataspaceMessage::decode(&buf, &ctx8()).unwrap_err();
551 match err {
552 FormatError::BufferTooShort {
553 needed: 12,
554 available: 6,
555 } => {}
556 other => panic!("unexpected error: {:?}", other),
557 }
558 }
559
560 #[test]
561 fn version_byte_is_two() {
562 let msg = DataspaceMessage::simple(&[42]);
563 let encoded = msg.encode(&ctx8());
564 assert_eq!(encoded[0], 2);
565 }
566
567 #[test]
568 fn roundtrip_null() {
569 let msg = DataspaceMessage::null();
570 let encoded = msg.encode(&ctx8());
571 assert_eq!(encoded.len(), 4); assert_eq!(encoded[3], 2); let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
574 assert_eq!(consumed, 4);
575 assert_eq!(decoded, msg);
576 assert!(decoded.is_null());
577 }
578
579 #[test]
583 fn null_and_scalar_are_distinct() {
584 let null = DataspaceMessage::null();
585 let scalar = DataspaceMessage::scalar();
586 assert_ne!(null, scalar);
587 assert!(null.is_null());
588 assert!(!scalar.is_null());
589
590 let null_encoded = null.encode(&ctx8());
591 let scalar_encoded = scalar.encode(&ctx8());
592 assert_ne!(null_encoded[3], scalar_encoded[3]);
593
594 let (null_decoded, _) = DataspaceMessage::decode(&null_encoded, &ctx8()).unwrap();
595 let (scalar_decoded, _) = DataspaceMessage::decode(&scalar_encoded, &ctx8()).unwrap();
596 assert!(null_decoded.is_null());
597 assert!(!scalar_decoded.is_null());
598 }
599
600 #[test]
601 fn decode_v2_bad_type_byte() {
602 let buf = [2u8, 0, 0, 3];
604 let err = DataspaceMessage::decode(&buf, &ctx8()).unwrap_err();
605 match err {
606 FormatError::InvalidData(_) => {}
607 other => panic!("unexpected error: {:?}", other),
608 }
609 }
610
611 #[test]
612 fn decode_v1_is_never_null() {
613 let buf = [1u8, 0, 0, 0, 0, 0, 0, 0];
615 let (msg, _) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
616 assert!(!msg.is_null());
617 assert_eq!(msg.class, DataspaceClass::Scalar);
618 }
619
620 #[test]
624 fn a_legacy_dataspace_matches_the_bytes_libhdf5_wrote() {
625 let ds = DataspaceMessage {
626 class: DataspaceClass::Simple,
627 dims: vec![6],
628 max_dims: Some(vec![6]),
629 };
630 let buf = ds.encode_for(&ctx8(), crate::format::ObjectFormat::Legacy);
631 assert_eq!(
632 buf,
633 vec![0x01, 0x01, 0x01, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0]
634 );
635 let (back, consumed) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
636 assert_eq!(consumed, buf.len());
637 assert_eq!(back, ds);
638 }
639
640 #[test]
644 fn a_null_dataspace_stays_at_version_2_in_a_legacy_file() {
645 let buf = DataspaceMessage::null().encode_for(&ctx8(), crate::format::ObjectFormat::Legacy);
646 assert_eq!(buf[0], 2);
647 assert_eq!(buf[3], DS_TYPE_NULL);
648 let (back, _) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
649 assert!(back.is_null());
650 }
651
652 #[test]
656 fn a_legacy_scalar_dataspace_round_trips_as_scalar() {
657 let buf =
658 DataspaceMessage::scalar().encode_for(&ctx8(), crate::format::ObjectFormat::Legacy);
659 assert_eq!(buf, vec![1, 0, 0, 0, 0, 0, 0, 0]);
660 let (back, _) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
661 assert_eq!(back.class, DataspaceClass::Scalar);
662 }
663}