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 encode(&self, ctx: &FormatContext) -> Vec<u8> {
133 self.encode_for(ctx, crate::format::ObjectFormat::Modern)
134 }
135
136 fn version_for(&self, format: crate::format::ObjectFormat) -> u8 {
142 let needed = if self.class == DataspaceClass::Null {
143 2
144 } else {
145 1
146 };
147 needed.max(format.dataspace_version())
148 }
149
150 pub fn encode_for(&self, ctx: &FormatContext, format: crate::format::ObjectFormat) -> Vec<u8> {
156 let version = self.version_for(format);
157 let ndims = self.dims.len();
158 let ss = ctx.sizeof_size as usize;
159 let max_dims = self.max_dims.as_deref();
163 let has_max = max_dims.is_some();
164 let flags: u8 = if has_max { FLAG_MAX_DIMS } else { 0 };
165
166 let ds_type = match self.class {
167 DataspaceClass::Scalar => DS_TYPE_SCALAR,
168 DataspaceClass::Simple => DS_TYPE_SIMPLE,
169 DataspaceClass::Null => DS_TYPE_NULL,
170 };
171
172 let prefix_len = if version == 1 { 8 } else { 4 };
173 let body_len = prefix_len + ndims * ss + if has_max { ndims * ss } else { 0 };
174 let mut buf = Vec::with_capacity(body_len);
175
176 buf.push(version);
177 buf.push(ndims as u8);
178 buf.push(flags);
179 if version == 1 {
180 buf.extend_from_slice(&[0u8; 5]);
183 } else {
184 buf.push(ds_type);
185 }
186
187 for &d in &self.dims {
189 buf.extend_from_slice(&d.to_le_bytes()[..ss]);
190 }
191
192 if let Some(maxes) = max_dims {
194 for &m in maxes {
195 buf.extend_from_slice(&m.to_le_bytes()[..ss]);
196 }
197 }
198
199 buf
200 }
201
202 pub fn decode(buf: &[u8], ctx: &FormatContext) -> FormatResult<(Self, usize)> {
205 if buf.len() < 4 {
206 return Err(FormatError::BufferTooShort {
207 needed: 4,
208 available: buf.len(),
209 });
210 }
211
212 let version = buf[0];
213 match version {
214 1 => Self::decode_v1(buf, ctx),
215 VERSION => Self::decode_v2(buf, ctx),
216 _ => Err(FormatError::InvalidVersion(version)),
217 }
218 }
219
220 fn decode_v2(buf: &[u8], ctx: &FormatContext) -> FormatResult<(Self, usize)> {
222 let ndims = buf[1] as usize;
223 let flags = buf[2];
224 let class = match buf[3] {
225 DS_TYPE_SCALAR => DataspaceClass::Scalar,
226 DS_TYPE_SIMPLE => DataspaceClass::Simple,
227 DS_TYPE_NULL => DataspaceClass::Null,
228 other => {
229 return Err(FormatError::InvalidData(format!(
230 "dataspace type byte {other} is not scalar(0)/simple(1)/null(2)"
231 )))
232 }
233 };
234 let has_max = (flags & FLAG_MAX_DIMS) != 0;
235 let ss = ctx.sizeof_size as usize;
236
237 let needed = 4 + ndims * ss + if has_max { ndims * ss } else { 0 };
238 if buf.len() < needed {
239 return Err(FormatError::BufferTooShort {
240 needed,
241 available: buf.len(),
242 });
243 }
244
245 let mut pos = 4;
246
247 let mut dims = Vec::with_capacity(ndims);
248 for _ in 0..ndims {
249 dims.push(read_size(&buf[pos..], ss));
250 pos += ss;
251 }
252
253 let max_dims = if has_max {
254 let mut v = Vec::with_capacity(ndims);
255 for _ in 0..ndims {
256 v.push(read_size(&buf[pos..], ss));
257 pos += ss;
258 }
259 Some(v)
260 } else {
261 None
262 };
263
264 Ok((
265 Self {
266 class,
267 dims,
268 max_dims,
269 },
270 pos,
271 ))
272 }
273
274 fn decode_v1(buf: &[u8], ctx: &FormatContext) -> FormatResult<(Self, usize)> {
288 if buf.len() < 8 {
289 return Err(FormatError::BufferTooShort {
290 needed: 8,
291 available: buf.len(),
292 });
293 }
294
295 let ndims = buf[1] as usize;
296 let flags = buf[2];
297 let has_max = (flags & FLAG_MAX_DIMS) != 0;
298 let has_perm = (flags & 0x02) != 0;
299 let ss = ctx.sizeof_size as usize;
300
301 let mut needed = 8 + ndims * ss;
303 if has_max {
304 needed += ndims * ss;
305 }
306 if has_perm {
307 needed += ndims * ss;
308 }
309 if buf.len() < needed {
310 return Err(FormatError::BufferTooShort {
311 needed,
312 available: buf.len(),
313 });
314 }
315
316 let mut pos = 8; let mut dims = Vec::with_capacity(ndims);
319 for _ in 0..ndims {
320 dims.push(read_size(&buf[pos..], ss));
321 pos += ss;
322 }
323
324 let max_dims = if has_max {
325 let mut v = Vec::with_capacity(ndims);
326 for _ in 0..ndims {
327 v.push(read_size(&buf[pos..], ss));
328 pos += ss;
329 }
330 Some(v)
331 } else {
332 None
333 };
334
335 if has_perm {
337 pos += ndims * ss;
338 }
339
340 Ok((
344 Self {
345 class: Self::class_for_rank(ndims),
346 dims,
347 max_dims,
348 },
349 pos,
350 ))
351 }
352}
353
354#[cfg(test)]
357mod tests {
358 use super::*;
359
360 fn ctx8() -> FormatContext {
361 FormatContext {
362 sizeof_addr: 8,
363 sizeof_size: 8,
364 }
365 }
366
367 fn ctx4() -> FormatContext {
368 FormatContext {
369 sizeof_addr: 4,
370 sizeof_size: 4,
371 }
372 }
373
374 #[test]
375 fn roundtrip_scalar() {
376 let msg = DataspaceMessage::scalar();
377 let encoded = msg.encode(&ctx8());
378 assert_eq!(encoded.len(), 4); let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
380 assert_eq!(consumed, 4);
381 assert_eq!(decoded, msg);
382 }
383
384 #[test]
389 fn roundtrip_simple_1d() {
390 let msg = DataspaceMessage::simple(&[100]);
391 let encoded = msg.encode(&ctx8());
392 assert_eq!(encoded.len(), 20);
394 assert_eq!(encoded[2], FLAG_MAX_DIMS);
395 let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
396 assert_eq!(consumed, 20);
397 assert_eq!(decoded.class, msg.class);
398 assert_eq!(decoded.dims, msg.dims);
399 assert_eq!(decoded.max_dims, Some(vec![100]));
400 }
401
402 #[test]
403 fn roundtrip_simple_3d_ctx4() {
404 let msg = DataspaceMessage::simple(&[10, 20, 30]);
405 let encoded = msg.encode(&ctx4());
406 assert_eq!(encoded.len(), 28);
408 let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx4()).unwrap();
409 assert_eq!(consumed, 28);
410 assert_eq!(decoded.class, msg.class);
411 assert_eq!(decoded.dims, msg.dims);
412 assert_eq!(decoded.max_dims, Some(vec![10, 20, 30]));
413 }
414
415 #[test]
416 fn roundtrip_unlimited() {
417 let msg = DataspaceMessage::unlimited(&[5, 10]);
418 let encoded = msg.encode(&ctx8());
419 assert_eq!(encoded.len(), 36);
421 let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
422 assert_eq!(consumed, 36);
423 assert_eq!(decoded, msg);
424 assert_eq!(decoded.max_dims.as_ref().unwrap(), &vec![u64::MAX; 2]);
425 }
426
427 #[test]
428 fn roundtrip_partial_max() {
429 let msg = DataspaceMessage {
430 class: DataspaceClass::Simple,
431 dims: vec![3, 4],
432 max_dims: Some(vec![100, u64::MAX]),
433 };
434 let encoded = msg.encode(&ctx8());
435 let (decoded, _) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
436 assert_eq!(decoded, msg);
437 }
438
439 #[test]
440 fn decode_bad_version() {
441 let buf = [99u8, 0, 0, 0]; let err = DataspaceMessage::decode(&buf, &ctx8()).unwrap_err();
443 match err {
444 FormatError::InvalidVersion(99) => {}
445 other => panic!("unexpected error: {:?}", other),
446 }
447 }
448
449 #[test]
450 fn decode_v1_simple_1d() {
451 let mut buf = vec![
453 1, 1, 0, 0, ];
458 buf.extend_from_slice(&[0u8; 4]); buf.extend_from_slice(&100u64.to_le_bytes()); let (msg, consumed) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
462 assert_eq!(consumed, 16); assert_eq!(msg.dims, vec![100]);
464 assert_eq!(msg.max_dims, None);
465 }
466
467 #[test]
475 fn a_decoded_message_with_no_maximum_re_encodes_without_one() {
476 let mut buf = vec![1, 1, 0, 0];
477 buf.extend_from_slice(&[0u8; 4]);
478 buf.extend_from_slice(&100u64.to_le_bytes());
479
480 let (msg, _) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
481 assert_eq!(msg.max_dims, None);
482 assert_eq!(
483 msg.encode_for(&ctx8(), crate::format::ObjectFormat::Legacy),
484 buf
485 );
486
487 let modern = msg.encode_for(&ctx8(), crate::format::ObjectFormat::Modern);
488 assert_eq!(modern.len(), 12, "4-byte prefix and one dimension");
489 assert_eq!(modern[2], 0, "H5S_VALID_MAX stays clear");
490 let (round, _) = DataspaceMessage::decode(&modern, &ctx8()).unwrap();
491 assert_eq!(round, msg);
492 }
493
494 #[test]
498 fn a_constructed_simple_dataspace_carries_its_maximum() {
499 assert_eq!(DataspaceMessage::simple(&[3, 4]).max_dims, Some(vec![3, 4]));
500 assert_eq!(DataspaceMessage::simple(&[]).max_dims, None);
501 assert_eq!(DataspaceMessage::scalar().max_dims, None);
502 assert_eq!(DataspaceMessage::null().max_dims, None);
503 }
504
505 #[test]
506 fn decode_v1_with_max_dims() {
507 let mut buf = vec![
508 1, 2, 1, 0, ];
513 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();
520 assert_eq!(consumed, 40); assert_eq!(msg.dims, vec![10, 20]);
522 assert_eq!(msg.max_dims, Some(vec![u64::MAX, 100]));
523 }
524
525 #[test]
526 fn decode_buffer_too_short() {
527 let buf = [2u8, 1, 0]; let err = DataspaceMessage::decode(&buf, &ctx8()).unwrap_err();
529 match err {
530 FormatError::BufferTooShort { .. } => {}
531 other => panic!("unexpected error: {:?}", other),
532 }
533 }
534
535 #[test]
536 fn decode_buffer_too_short_for_dims() {
537 let buf = [2u8, 1, 0, 1, 0, 0];
539 let err = DataspaceMessage::decode(&buf, &ctx8()).unwrap_err();
540 match err {
541 FormatError::BufferTooShort {
542 needed: 12,
543 available: 6,
544 } => {}
545 other => panic!("unexpected error: {:?}", other),
546 }
547 }
548
549 #[test]
550 fn version_byte_is_two() {
551 let msg = DataspaceMessage::simple(&[42]);
552 let encoded = msg.encode(&ctx8());
553 assert_eq!(encoded[0], 2);
554 }
555
556 #[test]
557 fn roundtrip_null() {
558 let msg = DataspaceMessage::null();
559 let encoded = msg.encode(&ctx8());
560 assert_eq!(encoded.len(), 4); assert_eq!(encoded[3], 2); let (decoded, consumed) = DataspaceMessage::decode(&encoded, &ctx8()).unwrap();
563 assert_eq!(consumed, 4);
564 assert_eq!(decoded, msg);
565 assert!(decoded.is_null());
566 }
567
568 #[test]
572 fn null_and_scalar_are_distinct() {
573 let null = DataspaceMessage::null();
574 let scalar = DataspaceMessage::scalar();
575 assert_ne!(null, scalar);
576 assert!(null.is_null());
577 assert!(!scalar.is_null());
578
579 let null_encoded = null.encode(&ctx8());
580 let scalar_encoded = scalar.encode(&ctx8());
581 assert_ne!(null_encoded[3], scalar_encoded[3]);
582
583 let (null_decoded, _) = DataspaceMessage::decode(&null_encoded, &ctx8()).unwrap();
584 let (scalar_decoded, _) = DataspaceMessage::decode(&scalar_encoded, &ctx8()).unwrap();
585 assert!(null_decoded.is_null());
586 assert!(!scalar_decoded.is_null());
587 }
588
589 #[test]
590 fn decode_v2_bad_type_byte() {
591 let buf = [2u8, 0, 0, 3];
593 let err = DataspaceMessage::decode(&buf, &ctx8()).unwrap_err();
594 match err {
595 FormatError::InvalidData(_) => {}
596 other => panic!("unexpected error: {:?}", other),
597 }
598 }
599
600 #[test]
601 fn decode_v1_is_never_null() {
602 let buf = [1u8, 0, 0, 0, 0, 0, 0, 0];
604 let (msg, _) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
605 assert!(!msg.is_null());
606 assert_eq!(msg.class, DataspaceClass::Scalar);
607 }
608
609 #[test]
613 fn a_legacy_dataspace_matches_the_bytes_libhdf5_wrote() {
614 let ds = DataspaceMessage {
615 class: DataspaceClass::Simple,
616 dims: vec![6],
617 max_dims: Some(vec![6]),
618 };
619 let buf = ds.encode_for(&ctx8(), crate::format::ObjectFormat::Legacy);
620 assert_eq!(
621 buf,
622 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]
623 );
624 let (back, consumed) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
625 assert_eq!(consumed, buf.len());
626 assert_eq!(back, ds);
627 }
628
629 #[test]
633 fn a_null_dataspace_stays_at_version_2_in_a_legacy_file() {
634 let buf = DataspaceMessage::null().encode_for(&ctx8(), crate::format::ObjectFormat::Legacy);
635 assert_eq!(buf[0], 2);
636 assert_eq!(buf[3], DS_TYPE_NULL);
637 let (back, _) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
638 assert!(back.is_null());
639 }
640
641 #[test]
645 fn a_legacy_scalar_dataspace_round_trips_as_scalar() {
646 let buf =
647 DataspaceMessage::scalar().encode_for(&ctx8(), crate::format::ObjectFormat::Legacy);
648 assert_eq!(buf, vec![1, 0, 0, 0, 0, 0, 0, 0]);
649 let (back, _) = DataspaceMessage::decode(&buf, &ctx8()).unwrap();
650 assert_eq!(back.class, DataspaceClass::Scalar);
651 }
652}