1use crate::format::{FormatError, FormatResult};
65
66pub const UNLIMITED: u64 = u64::MAX;
70
71const SEL_NONE: u32 = 0;
72const SEL_POINTS: u32 = 1;
73const SEL_HYPERSLABS: u32 = 2;
74const SEL_ALL: u32 = 3;
75
76const ALL_NONE_VERSION: u32 = 1;
77
78const HYPER_VERSION_1: u32 = 1;
79const HYPER_VERSION_2: u32 = 2;
80const HYPER_VERSION_3: u32 = 3;
81
82const HYPER_REGULAR_FLAG: u8 = 0x01;
83
84const POINT_VERSION_1: u32 = 1;
85const POINT_VERSION_2: u32 = 2;
86
87const MAX_RANK: usize = 32;
91
92#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct HyperslabBlock {
96 pub start: Vec<u64>,
97 pub end: Vec<u64>,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct RegularHyperslab {
104 pub start: Vec<u64>,
105 pub stride: Vec<u64>,
106 pub count: Vec<u64>,
107 pub block: Vec<u64>,
108}
109
110impl RegularHyperslab {
111 pub fn unlim_dim(&self) -> Option<usize> {
116 (0..self.count.len())
117 .find(|&d| self.count[d] == UNLIMITED || self.block.get(d) == Some(&UNLIMITED))
118 }
119
120 fn clip_diminfo(start: u64, stride: u64, count: u64, block: u64, clip_size: u64) -> (u64, u64) {
124 if start >= clip_size {
125 if block == UNLIMITED {
126 (count, 0)
127 } else {
128 (0, block)
129 }
130 } else if block == UNLIMITED || block == stride {
131 (1, clip_size - start)
132 } else {
133 let stride = stride.max(1);
135 ((clip_size - start).div_ceil(stride), block)
136 }
137 }
138
139 pub fn num_slices(&self, clip_size: u64) -> u64 {
144 let Some(d) = self.unlim_dim() else {
145 return 0;
146 };
147 let (start, stride) = (self.start[d], self.stride[d]);
148 let (count, block) =
149 Self::clip_diminfo(start, stride, self.count[d], self.block[d], clip_size);
150 if block == 0 || count == 0 {
151 return 0;
152 }
153 if count == 1 {
154 return block;
155 }
156 let span = stride * (count - 1) + block;
157 let avail = clip_size - start;
158 if span > avail {
159 block * count - (span - avail)
160 } else {
161 block * count
162 }
163 }
164
165 pub fn clip_extent(&self, num_slices: u64, incl_trail: bool) -> u64 {
170 let Some(d) = self.unlim_dim() else {
171 return 0;
172 };
173 let (start, stride, block) = (self.start[d], self.stride[d], self.block[d]);
174 if num_slices == 0 {
175 return if incl_trail { start } else { 0 };
176 }
177 if block == UNLIMITED || block == stride {
178 return start + num_slices;
179 }
180 let block = block.max(1);
181 let count = num_slices / block;
182 let rem = num_slices - count * block;
183 if rem > 0 {
184 start + count * stride + rem
185 } else if incl_trail {
186 start + count * stride
187 } else {
188 start + (count - 1) * stride + block
189 }
190 }
191
192 pub fn num_elem_non_unlim(&self) -> Option<u64> {
196 let d = self.unlim_dim()?;
197 (0..self.count.len())
198 .filter(|&i| i != d)
199 .try_fold(1u64, |acc, i| {
200 acc.checked_mul(self.count[i])?.checked_mul(self.block[i])
201 })
202 }
203
204 pub fn unlim_block(&self, index: u64) -> Self {
208 let mut out = self.clone();
209 if let Some(d) = self.unlim_dim() {
210 out.start[d] = self.start[d] + index * self.stride[d];
211 out.count[d] = 1;
212 }
213 out
214 }
215}
216
217#[derive(Debug, Clone, PartialEq, Eq)]
225pub enum Hyperslab {
226 Regular(RegularHyperslab),
227 Blocks(Vec<HyperslabBlock>),
228}
229
230#[derive(Debug, Clone, PartialEq, Eq)]
236pub struct PointSelection {
237 pub rank: usize,
238 pub points: Vec<Vec<u64>>,
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub(crate) struct SelectionRun {
249 pub box_index: usize,
251 pub offset_in_box: u64,
254 pub offset_in_extent: u64,
257 pub len: u64,
259}
260
261#[derive(Debug, Clone, PartialEq, Eq)]
263pub(crate) struct ResolvedSelection {
264 pub boxes: Vec<(Vec<u64>, Vec<u64>)>,
267 pub runs: Vec<SelectionRun>,
270}
271
272impl ResolvedSelection {
273 pub(crate) fn n_elements(&self) -> u64 {
275 self.runs.iter().map(|r| r.len).sum()
276 }
277}
278
279fn row_major_strides(dims: &[u64]) -> FormatResult<Vec<u64>> {
282 let mut strides = vec![1u64; dims.len()];
283 for d in (0..dims.len().saturating_sub(1)).rev() {
284 strides[d] = strides[d + 1].checked_mul(dims[d + 1]).ok_or_else(|| {
285 FormatError::InvalidData(format!(
286 "extent {dims:?} holds more elements than u64 counts"
287 ))
288 })?;
289 }
290 Ok(strides)
291}
292
293#[derive(Debug, Clone, PartialEq, Eq)]
302pub(crate) enum HyperslabError {
303 Rank { got: usize, rank: usize },
305 OutOfBounds {
307 dim: usize,
308 start: u64,
309 count: u64,
310 extent: u64,
311 },
312}
313
314impl std::fmt::Display for HyperslabError {
315 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
316 match self {
317 Self::Rank { got, rank } => {
318 write!(f, "selection rank {got} does not match dataset rank {rank}")
319 }
320 Self::OutOfBounds {
321 dim,
322 start,
323 count,
324 extent,
325 } => write!(
326 f,
327 "slice out of bounds in dimension {dim}: start {start} + count {count} exceeds extent {extent}"
328 ),
329 }
330 }
331}
332
333impl From<HyperslabError> for FormatError {
334 fn from(e: HyperslabError) -> Self {
335 FormatError::InvalidData(e.to_string())
336 }
337}
338
339pub(crate) fn check_hyperslab(
346 dims: &[u64],
347 starts: &[u64],
348 counts: &[u64],
349) -> Result<(), HyperslabError> {
350 let rank = dims.len();
351 if starts.len() != rank {
352 return Err(HyperslabError::Rank {
353 got: starts.len(),
354 rank,
355 });
356 }
357 if counts.len() != rank {
358 return Err(HyperslabError::Rank {
359 got: counts.len(),
360 rank,
361 });
362 }
363 for (dim, &extent) in dims.iter().enumerate() {
364 if starts[dim]
365 .checked_add(counts[dim])
366 .is_none_or(|end| end > extent)
367 {
368 return Err(HyperslabError::OutOfBounds {
369 dim,
370 start: starts[dim],
371 count: counts[dim],
372 extent,
373 });
374 }
375 }
376 Ok(())
377}
378
379fn push_box_runs(
386 box_index: usize,
387 start: &[u64],
388 count: &[u64],
389 dims: &[u64],
390 strides: &[u64],
391 runs: &mut Vec<SelectionRun>,
392) -> FormatResult<()> {
393 let rank = dims.len();
394 check_hyperslab(dims, start, count)?;
395 if count.contains(&0) {
396 return Ok(());
397 }
398 if rank == 0 {
399 runs.push(SelectionRun {
402 box_index,
403 offset_in_box: 0,
404 offset_in_extent: 0,
405 len: 1,
406 });
407 return Ok(());
408 }
409 let box_strides = row_major_strides(count)?;
410 let run_len = count[rank - 1];
411 let n_outer = count[..rank - 1]
412 .iter()
413 .try_fold(1u64, |acc, &c| acc.checked_mul(c))
414 .ok_or_else(|| FormatError::InvalidData("selection box element count overflows".into()))?;
415 let mut coords = vec![0u64; rank - 1];
416 for _ in 0..n_outer {
417 let mut offset_in_extent = start[rank - 1];
418 let mut offset_in_box = 0u64;
419 for d in 0..rank - 1 {
420 offset_in_extent += (start[d] + coords[d]) * strides[d];
421 offset_in_box += coords[d] * box_strides[d];
422 }
423 match runs.last_mut() {
424 Some(prev)
425 if prev.box_index == box_index
426 && prev.offset_in_extent + prev.len == offset_in_extent
427 && prev.offset_in_box + prev.len == offset_in_box =>
428 {
429 prev.len += run_len;
430 }
431 _ => runs.push(SelectionRun {
432 box_index,
433 offset_in_box,
434 offset_in_extent,
435 len: run_len,
436 }),
437 }
438 for d in (0..rank - 1).rev() {
439 coords[d] += 1;
440 if coords[d] < count[d] {
441 break;
442 }
443 coords[d] = 0;
444 }
445 }
446 Ok(())
447}
448
449#[derive(Debug, Clone, PartialEq, Eq)]
451pub enum Selection {
452 None,
454 All,
457 Hyperslab { rank: usize, form: Hyperslab },
459 Points(PointSelection),
461}
462
463impl Selection {
464 pub fn decode(buf: &[u8]) -> FormatResult<(Self, usize)> {
471 if buf.len() < 4 {
472 return Err(FormatError::BufferTooShort {
473 needed: 4,
474 available: buf.len(),
475 });
476 }
477 let sel_type = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
478 let body = &buf[4..];
479 match sel_type {
480 SEL_NONE => {
481 let consumed = decode_all_none_body(body)?;
482 Ok((Self::None, 4 + consumed))
483 }
484 SEL_ALL => {
485 let consumed = decode_all_none_body(body)?;
486 Ok((Self::All, 4 + consumed))
487 }
488 SEL_HYPERSLABS => {
489 let (rank, form, consumed) = decode_hyperslab_body(body)?;
490 Ok((Self::Hyperslab { rank, form }, 4 + consumed))
491 }
492 SEL_POINTS => {
493 let (points, consumed) = decode_points_body(body)?;
494 Ok((Self::Points(points), 4 + consumed))
495 }
496 other => Err(FormatError::InvalidData(format!(
497 "unknown dataspace selection type {other}"
498 ))),
499 }
500 }
501
502 pub fn to_boxes(&self, dims: &[u64]) -> FormatResult<Vec<(Vec<u64>, Vec<u64>)>> {
514 match self {
515 Self::None => Ok(Vec::new()),
516 Self::All => Ok(vec![(vec![0u64; dims.len()], dims.to_vec())]),
517 Self::Points(ps) => {
518 if ps.rank != dims.len() {
519 return Err(FormatError::InvalidData(format!(
520 "point selection rank {} does not match the {}-dimensional extent",
521 ps.rank,
522 dims.len()
523 )));
524 }
525 Ok(ps
529 .points
530 .iter()
531 .map(|p| (p.clone(), vec![1u64; ps.rank]))
532 .collect())
533 }
534 Self::Hyperslab { rank, form } => {
535 if *rank != dims.len() {
536 return Err(FormatError::InvalidData(format!(
537 "hyperslab selection rank {rank} does not match the \
538 {}-dimensional extent",
539 dims.len()
540 )));
541 }
542 match form {
543 Hyperslab::Blocks(blocks) => blocks
544 .iter()
545 .map(|b| {
546 let count = b
547 .start
548 .iter()
549 .zip(&b.end)
550 .map(|(&s, &e)| {
551 e.checked_sub(s).and_then(|d| d.checked_add(1)).ok_or_else(
552 || {
553 FormatError::InvalidData(
554 "hyperslab block end precedes its start".into(),
555 )
556 },
557 )
558 })
559 .collect::<FormatResult<Vec<u64>>>()?;
560 Ok((b.start.clone(), count))
561 })
562 .collect(),
563 Hyperslab::Regular(r) => regular_hyperslab_to_boxes(r),
564 }
565 }
566 }
567 }
568
569 pub(crate) fn resolve(&self, dims: &[u64]) -> FormatResult<ResolvedSelection> {
589 let boxes = self.to_boxes(dims)?;
590 let strides = row_major_strides(dims)?;
591 let mut runs = Vec::new();
592 for (i, (start, count)) in boxes.iter().enumerate() {
593 push_box_runs(i, start, count, dims, &strides, &mut runs)?;
594 }
595 if !matches!(self, Self::Points(_)) {
604 runs.sort_unstable_by_key(|r| r.offset_in_extent);
605 }
606 Ok(ResolvedSelection { boxes, runs })
607 }
608
609 pub fn encode(&self) -> FormatResult<Vec<u8>> {
631 match self {
632 Self::None => Ok(encode_all_none(SEL_NONE)),
633 Self::All => Ok(encode_all_none(SEL_ALL)),
634 Self::Points(ps) => encode_points(ps),
635 Self::Hyperslab { rank, form } => encode_hyperslab(*rank, form),
636 }
637 }
638
639 pub fn unlim_dim(&self) -> Option<usize> {
644 match self {
645 Self::Hyperslab {
646 form: Hyperslab::Regular(r),
647 ..
648 } => r.unlim_dim(),
649 _ => None,
650 }
651 }
652
653 pub fn clip_unlimited(&self, clip_size: u64) -> FormatResult<Self> {
665 let Self::Hyperslab {
666 rank,
667 form: Hyperslab::Regular(r),
668 } = self
669 else {
670 return Ok(self.clone());
671 };
672 let Some(d) = r.unlim_dim() else {
673 return Ok(self.clone());
674 };
675 let (count, block) = RegularHyperslab::clip_diminfo(
676 r.start[d],
677 r.stride[d],
678 r.count[d],
679 r.block[d],
680 clip_size,
681 );
682 if count == 0 || block == 0 {
683 return Ok(Self::None);
684 }
685 let mut clipped = r.clone();
686 clipped.count[d] = count;
687 clipped.block[d] = block;
688
689 let mut blocks = Vec::new();
690 for (start, count) in regular_hyperslab_to_boxes(&clipped)? {
691 if start[d] >= clip_size {
695 continue;
696 }
697 let mut count = count;
698 count[d] = count[d].min(clip_size - start[d]);
699 let end = start
700 .iter()
701 .zip(&count)
702 .map(|(&s, &c)| s + c - 1)
703 .collect::<Vec<u64>>();
704 blocks.push(HyperslabBlock { start, end });
705 }
706 if blocks.is_empty() {
707 return Ok(Self::None);
708 }
709 Ok(Self::Hyperslab {
710 rank: *rank,
711 form: Hyperslab::Blocks(blocks),
712 })
713 }
714
715 pub fn bounds(&self) -> Option<(Vec<u64>, Vec<u64>)> {
722 match self {
723 Self::All | Self::None => None,
724 Self::Hyperslab { rank, form } => {
725 let blocks = hyperslab_to_block_list(*rank, form).ok()?;
726 let first = blocks.first()?;
727 let mut lo = first.start.clone();
728 let mut hi = first.end.clone();
729 for b in &blocks[1..] {
730 for (l, s) in lo.iter_mut().zip(&b.start) {
731 *l = (*l).min(*s);
732 }
733 for (h, e) in hi.iter_mut().zip(&b.end) {
734 *h = (*h).max(*e);
735 }
736 }
737 Some((lo, hi))
738 }
739 Self::Points(ps) => {
740 let first = ps.points.first()?;
741 let mut lo = first.clone();
742 let mut hi = first.clone();
743 for p in &ps.points[1..] {
744 for (l, c) in lo.iter_mut().zip(p) {
745 *l = (*l).min(*c);
746 }
747 for (h, c) in hi.iter_mut().zip(p) {
748 *h = (*h).max(*c);
749 }
750 }
751 Some((lo, hi))
752 }
753 }
754 }
755}
756
757fn to_u32(v: u64) -> FormatResult<u32> {
760 u32::try_from(v).map_err(|_| {
761 FormatError::UnsupportedFeature(format!(
762 "value {v} exceeds the 4-byte encoding Selection::encode targets (version 1, \
763 matching libhdf5's default H5F_LIBVER_V18 low format-version bound)"
764 ))
765 })
766}
767
768fn encode_all_none(sel_type: u32) -> Vec<u8> {
769 let mut buf = Vec::with_capacity(16);
770 buf.extend_from_slice(&sel_type.to_le_bytes());
771 buf.extend_from_slice(&ALL_NONE_VERSION.to_le_bytes());
772 buf.extend_from_slice(&[0u8; 8]);
773 buf
774}
775
776fn encode_points(ps: &PointSelection) -> FormatResult<Vec<u8>> {
777 if ps.rank == 0 || ps.rank > MAX_RANK {
778 return Err(FormatError::InvalidData(format!(
779 "invalid point selection rank {}",
780 ps.rank
781 )));
782 }
783 for p in &ps.points {
784 if p.len() != ps.rank {
785 return Err(FormatError::InvalidData(format!(
786 "point selection coordinate length {} does not match rank {}",
787 p.len(),
788 ps.rank
789 )));
790 }
791 }
792 let num_points: u32 = ps.points.len().try_into().map_err(|_| {
793 FormatError::UnsupportedFeature(format!(
794 "point selection has {} points, too many for version-1 encode (u32 count)",
795 ps.points.len()
796 ))
797 })?;
798 let rank_u32 = ps.rank as u32;
799 let payload_bytes: u32 = num_points
800 .checked_mul(4)
801 .and_then(|v| v.checked_mul(rank_u32))
802 .ok_or_else(|| {
803 FormatError::UnsupportedFeature(
804 "point selection payload too large for version-1 encode".into(),
805 )
806 })?;
807 let len = 8u32.checked_add(payload_bytes).ok_or_else(|| {
808 FormatError::UnsupportedFeature(
809 "point selection payload too large for version-1 encode".into(),
810 )
811 })?;
812
813 let mut buf = Vec::with_capacity(24 + payload_bytes as usize);
814 buf.extend_from_slice(&SEL_POINTS.to_le_bytes());
815 buf.extend_from_slice(&POINT_VERSION_1.to_le_bytes());
816 buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&len.to_le_bytes());
818 buf.extend_from_slice(&rank_u32.to_le_bytes());
819 buf.extend_from_slice(&num_points.to_le_bytes());
820 for p in &ps.points {
821 for &c in p {
822 buf.extend_from_slice(&to_u32(c)?.to_le_bytes());
823 }
824 }
825 Ok(buf)
826}
827
828fn hyperslab_to_block_list(rank: usize, form: &Hyperslab) -> FormatResult<Vec<HyperslabBlock>> {
832 match form {
833 Hyperslab::Blocks(blocks) => {
834 for b in blocks {
835 if b.start.len() != rank || b.end.len() != rank {
836 return Err(FormatError::InvalidData(format!(
837 "hyperslab block coordinate length does not match rank {rank}"
838 )));
839 }
840 }
841 Ok(blocks.clone())
842 }
843 Hyperslab::Regular(r) => {
844 if r.start.len() != rank
845 || r.stride.len() != rank
846 || r.count.len() != rank
847 || r.block.len() != rank
848 {
849 return Err(FormatError::InvalidData(format!(
850 "regular hyperslab field length does not match rank {rank}"
851 )));
852 }
853 regular_hyperslab_to_boxes(r)?
854 .into_iter()
855 .map(|(start, count)| {
856 let end = start
857 .iter()
858 .zip(&count)
859 .map(|(&s, &c)| {
860 s.checked_add(c - 1).ok_or_else(|| {
861 FormatError::InvalidData(
862 "hyperslab box coordinate overflows".into(),
863 )
864 })
865 })
866 .collect::<FormatResult<Vec<u64>>>()?;
867 Ok(HyperslabBlock { start, end })
868 })
869 .collect()
870 }
871 }
872}
873
874fn encode_regular_hyperslab_v2(rank: usize, r: &RegularHyperslab) -> FormatResult<Vec<u8>> {
879 if r.start.len() != rank
880 || r.stride.len() != rank
881 || r.count.len() != rank
882 || r.block.len() != rank
883 {
884 return Err(FormatError::InvalidData(format!(
885 "regular hyperslab field length does not match rank {rank}"
886 )));
887 }
888 let mut buf = Vec::with_capacity(17 + rank * 32);
889 buf.extend_from_slice(&SEL_HYPERSLABS.to_le_bytes());
890 buf.extend_from_slice(&HYPER_VERSION_2.to_le_bytes());
891 buf.push(HYPER_REGULAR_FLAG);
892 let len = 4u32 + 32 * rank as u32;
897 buf.extend_from_slice(&len.to_le_bytes());
898 buf.extend_from_slice(&(rank as u32).to_le_bytes());
899 for d in 0..rank {
900 buf.extend_from_slice(&r.start[d].to_le_bytes());
901 buf.extend_from_slice(&r.stride[d].to_le_bytes());
902 buf.extend_from_slice(&r.count[d].to_le_bytes());
903 buf.extend_from_slice(&r.block[d].to_le_bytes());
904 }
905 Ok(buf)
906}
907
908fn encode_hyperslab(rank: usize, form: &Hyperslab) -> FormatResult<Vec<u8>> {
909 if rank == 0 || rank > MAX_RANK {
910 return Err(FormatError::InvalidData(format!(
911 "invalid hyperslab selection rank {rank}"
912 )));
913 }
914 if let Hyperslab::Regular(r) = form {
921 if r.unlim_dim().is_some() {
922 return encode_regular_hyperslab_v2(rank, r);
923 }
924 }
925 let blocks = hyperslab_to_block_list(rank, form)?;
926 let num_blocks: u32 = blocks.len().try_into().map_err(|_| {
927 FormatError::UnsupportedFeature(format!(
928 "hyperslab selection has {} blocks, too many for version-1 encode (u32 count)",
929 blocks.len()
930 ))
931 })?;
932 let rank_u32 = rank as u32;
933
934 let mut coords: Vec<u32> = Vec::with_capacity(blocks.len() * rank * 2);
935 for b in &blocks {
936 for &s in &b.start {
937 coords.push(to_u32(s)?);
938 }
939 for &e in &b.end {
940 coords.push(to_u32(e)?);
941 }
942 }
943
944 let block_payload = 8u32
945 .checked_mul(rank_u32)
946 .and_then(|v| v.checked_mul(num_blocks))
947 .ok_or_else(|| {
948 FormatError::UnsupportedFeature(
949 "hyperslab selection too large for version-1 encode".into(),
950 )
951 })?;
952 let len = 8u32.checked_add(block_payload).ok_or_else(|| {
953 FormatError::UnsupportedFeature("hyperslab selection too large for version-1 encode".into())
954 })?;
955
956 let mut buf = Vec::with_capacity(24 + coords.len() * 4);
957 buf.extend_from_slice(&SEL_HYPERSLABS.to_le_bytes());
958 buf.extend_from_slice(&HYPER_VERSION_1.to_le_bytes());
959 buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&len.to_le_bytes());
961 buf.extend_from_slice(&rank_u32.to_le_bytes());
962 buf.extend_from_slice(&num_blocks.to_le_bytes());
963 for v in coords {
964 buf.extend_from_slice(&v.to_le_bytes());
965 }
966 Ok(buf)
967}
968
969const MAX_REGULAR_BOXES: u64 = 1 << 20;
977
978fn regular_hyperslab_to_boxes(r: &RegularHyperslab) -> FormatResult<Vec<(Vec<u64>, Vec<u64>)>> {
979 let rank = r.start.len();
980 if r.count.contains(&UNLIMITED) || r.block.contains(&UNLIMITED) {
981 return Err(FormatError::UnsupportedFeature(
982 "unlimited (H5S_UNLIMITED) regular hyperslab dimension".into(),
983 ));
984 }
985 if r.count.contains(&0) || r.block.contains(&0) {
986 return Ok(Vec::new());
987 }
988 let total_boxes = r
989 .count
990 .iter()
991 .try_fold(1u64, |acc, &c| acc.checked_mul(c))
992 .ok_or_else(|| FormatError::InvalidData("regular hyperslab box count overflows".into()))?;
993 if total_boxes > MAX_REGULAR_BOXES {
994 return Err(FormatError::UnsupportedFeature(format!(
995 "regular hyperslab selection expands to {total_boxes} boxes, over the \
996 {MAX_REGULAR_BOXES} cap"
997 )));
998 }
999
1000 let mut boxes = Vec::with_capacity(total_boxes as usize);
1001 let mut idx = vec![0u64; rank];
1002 for _ in 0..total_boxes {
1003 let start: Vec<u64> = (0..rank)
1004 .map(|d| r.start[d] + idx[d] * r.stride[d])
1005 .collect();
1006 boxes.push((start, r.block.clone()));
1007 for d in (0..rank).rev() {
1008 idx[d] += 1;
1009 if idx[d] < r.count[d] {
1010 break;
1011 }
1012 idx[d] = 0;
1013 }
1014 }
1015 Ok(boxes)
1016}
1017
1018fn decode_all_none_body(buf: &[u8]) -> FormatResult<usize> {
1019 if buf.len() < 4 + 8 {
1020 return Err(FormatError::BufferTooShort {
1021 needed: 4 + 8,
1022 available: buf.len(),
1023 });
1024 }
1025 let version = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
1026 if version != ALL_NONE_VERSION {
1027 return Err(FormatError::InvalidData(format!(
1028 "bad version {version} for all/none dataspace selection"
1029 )));
1030 }
1031 Ok(4 + 8)
1033}
1034
1035fn decode_hyperslab_body(buf: &[u8]) -> FormatResult<(usize, Hyperslab, usize)> {
1036 let mut pos = 0usize;
1037 if buf.len() < 4 {
1038 return Err(FormatError::BufferTooShort {
1039 needed: 4,
1040 available: buf.len(),
1041 });
1042 }
1043 let version = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
1044 pos += 4;
1045 if !(HYPER_VERSION_1..=HYPER_VERSION_3).contains(&version) {
1046 return Err(FormatError::InvalidData(format!(
1047 "bad version {version} for hyperslab dataspace selection"
1048 )));
1049 }
1050
1051 let mut flags = 0u8;
1052 let enc_size: usize;
1053 if version >= HYPER_VERSION_2 {
1054 if buf.len() < pos + 1 {
1055 return Err(FormatError::BufferTooShort {
1056 needed: pos + 1,
1057 available: buf.len(),
1058 });
1059 }
1060 flags = buf[pos];
1061 pos += 1;
1062 if flags & !HYPER_REGULAR_FLAG != 0 {
1063 return Err(FormatError::InvalidData(format!(
1064 "unknown hyperslab selection flag bits in {flags:#x}"
1065 )));
1066 }
1067
1068 if version >= HYPER_VERSION_3 {
1069 if buf.len() < pos + 1 {
1070 return Err(FormatError::BufferTooShort {
1071 needed: pos + 1,
1072 available: buf.len(),
1073 });
1074 }
1075 enc_size = match buf[pos] {
1076 0x02 => 2,
1077 0x04 => 4,
1078 0x08 => 8,
1079 other => {
1080 return Err(FormatError::InvalidData(format!(
1081 "unknown hyperslab selection encoding size tag {other:#x}"
1082 )))
1083 }
1084 };
1085 pos += 1;
1086 } else {
1087 if buf.len() < pos + 4 {
1089 return Err(FormatError::BufferTooShort {
1090 needed: pos + 4,
1091 available: buf.len(),
1092 });
1093 }
1094 pos += 4;
1095 enc_size = 8;
1096 }
1097 } else {
1098 if buf.len() < pos + 8 {
1100 return Err(FormatError::BufferTooShort {
1101 needed: pos + 8,
1102 available: buf.len(),
1103 });
1104 }
1105 pos += 8;
1106 enc_size = 4;
1107 }
1108
1109 if buf.len() < pos + 4 {
1110 return Err(FormatError::BufferTooShort {
1111 needed: pos + 4,
1112 available: buf.len(),
1113 });
1114 }
1115 let rank = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]) as usize;
1116 pos += 4;
1117 if rank == 0 || rank > MAX_RANK {
1118 return Err(FormatError::InvalidData(format!(
1119 "invalid hyperslab selection rank {rank}"
1120 )));
1121 }
1122
1123 if flags & HYPER_REGULAR_FLAG != 0 {
1124 let mut start = Vec::with_capacity(rank);
1125 let mut stride = Vec::with_capacity(rank);
1126 let mut count = Vec::with_capacity(rank);
1127 let mut block = Vec::with_capacity(rank);
1128 for _ in 0..rank {
1129 if buf.len() < pos + 4 * enc_size {
1130 return Err(FormatError::BufferTooShort {
1131 needed: pos + 4 * enc_size,
1132 available: buf.len(),
1133 });
1134 }
1135 start.push(read_plain(&buf[pos..], enc_size));
1136 pos += enc_size;
1137 stride.push(read_plain(&buf[pos..], enc_size));
1138 pos += enc_size;
1139 count.push(read_dim(&buf[pos..], enc_size));
1140 pos += enc_size;
1141 block.push(read_dim(&buf[pos..], enc_size));
1142 pos += enc_size;
1143 }
1144 Ok((
1145 rank,
1146 Hyperslab::Regular(RegularHyperslab {
1147 start,
1148 stride,
1149 count,
1150 block,
1151 }),
1152 pos,
1153 ))
1154 } else {
1155 if buf.len() < pos + enc_size {
1156 return Err(FormatError::BufferTooShort {
1157 needed: pos + enc_size,
1158 available: buf.len(),
1159 });
1160 }
1161 let num_blocks = read_plain(&buf[pos..], enc_size) as usize;
1162 pos += enc_size;
1163
1164 let mut blocks = Vec::new();
1171 for _ in 0..num_blocks {
1172 let mut start = Vec::with_capacity(rank);
1173 let mut end = Vec::with_capacity(rank);
1174 if buf.len() < pos + 2 * rank * enc_size {
1175 return Err(FormatError::BufferTooShort {
1176 needed: pos + 2 * rank * enc_size,
1177 available: buf.len(),
1178 });
1179 }
1180 for _ in 0..rank {
1181 start.push(read_plain(&buf[pos..], enc_size));
1182 pos += enc_size;
1183 }
1184 for _ in 0..rank {
1185 end.push(read_plain(&buf[pos..], enc_size));
1186 pos += enc_size;
1187 }
1188 blocks.push(HyperslabBlock { start, end });
1189 }
1190 Ok((rank, Hyperslab::Blocks(blocks), pos))
1191 }
1192}
1193
1194fn decode_points_body(buf: &[u8]) -> FormatResult<(PointSelection, usize)> {
1195 let mut pos = 0usize;
1196 if buf.len() < 4 {
1197 return Err(FormatError::BufferTooShort {
1198 needed: 4,
1199 available: buf.len(),
1200 });
1201 }
1202 let version = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
1203 pos += 4;
1204 if version != POINT_VERSION_1 && version != POINT_VERSION_2 {
1205 return Err(FormatError::InvalidData(format!(
1206 "bad version {version} for point dataspace selection"
1207 )));
1208 }
1209
1210 let enc_size: usize;
1211 if version >= POINT_VERSION_2 {
1212 if buf.len() < pos + 1 {
1213 return Err(FormatError::BufferTooShort {
1214 needed: pos + 1,
1215 available: buf.len(),
1216 });
1217 }
1218 enc_size = match buf[pos] {
1219 0x02 => 2,
1220 0x04 => 4,
1221 0x08 => 8,
1222 other => {
1223 return Err(FormatError::InvalidData(format!(
1224 "unknown point selection encoding size tag {other:#x}"
1225 )))
1226 }
1227 };
1228 pos += 1;
1229 } else {
1230 if buf.len() < pos + 8 {
1235 return Err(FormatError::BufferTooShort {
1236 needed: pos + 8,
1237 available: buf.len(),
1238 });
1239 }
1240 pos += 8;
1241 enc_size = 4;
1242 }
1243
1244 if buf.len() < pos + 4 {
1245 return Err(FormatError::BufferTooShort {
1246 needed: pos + 4,
1247 available: buf.len(),
1248 });
1249 }
1250 let rank = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]) as usize;
1251 pos += 4;
1252 if rank == 0 || rank > MAX_RANK {
1253 return Err(FormatError::InvalidData(format!(
1254 "invalid point selection rank {rank}"
1255 )));
1256 }
1257
1258 if buf.len() < pos + enc_size {
1259 return Err(FormatError::BufferTooShort {
1260 needed: pos + enc_size,
1261 available: buf.len(),
1262 });
1263 }
1264 let num_points = read_plain(&buf[pos..], enc_size);
1265 pos += enc_size;
1266
1267 let point_bytes = (rank as u64)
1273 .checked_mul(enc_size as u64)
1274 .and_then(|per_point| per_point.checked_mul(num_points))
1275 .ok_or_else(|| {
1276 FormatError::InvalidData("point selection coordinate buffer size overflows".into())
1277 })?;
1278 if (buf.len() as u64) < pos as u64 + point_bytes {
1279 let needed = usize::try_from(point_bytes)
1280 .ok()
1281 .and_then(|b| pos.checked_add(b))
1282 .unwrap_or(usize::MAX);
1283 return Err(FormatError::BufferTooShort {
1284 needed,
1285 available: buf.len(),
1286 });
1287 }
1288
1289 let mut points = Vec::with_capacity(num_points as usize);
1290 for _ in 0..num_points {
1291 let mut coord = Vec::with_capacity(rank);
1292 for _ in 0..rank {
1293 coord.push(read_plain(&buf[pos..], enc_size));
1294 pos += enc_size;
1295 }
1296 points.push(coord);
1297 }
1298
1299 Ok((PointSelection { rank, points }, pos))
1300}
1301
1302fn read_plain(buf: &[u8], n: usize) -> u64 {
1305 crate::format::bytes::read_le_uint(buf, n)
1306}
1307
1308fn read_dim(buf: &[u8], n: usize) -> u64 {
1314 let v = read_plain(buf, n);
1315 let all_ones = if n >= 8 {
1316 u64::MAX
1317 } else {
1318 (1u64 << (n * 8)) - 1
1319 };
1320 if v == all_ones {
1321 UNLIMITED
1322 } else {
1323 v
1324 }
1325}
1326
1327#[cfg(test)]
1330mod tests {
1331 use super::*;
1332
1333 fn strip_h5sencode_envelope(blob: &[u8]) -> &[u8] {
1339 let extent_size = u32::from_le_bytes([blob[3], blob[4], blob[5], blob[6]]) as usize;
1340 &blob[7 + extent_size..]
1341 }
1342
1343 #[test]
1347 fn decode_all_selection() {
1348 let buf = [
1349 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1353 let (sel, consumed) = Selection::decode(&buf).unwrap();
1354 assert_eq!(consumed, buf.len());
1355 assert_eq!(sel, Selection::All);
1356 }
1357
1358 #[test]
1359 fn decode_none_selection() {
1360 let buf = [
1361 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1365 let (sel, consumed) = Selection::decode(&buf).unwrap();
1366 assert_eq!(consumed, buf.len());
1367 assert_eq!(sel, Selection::None);
1368 }
1369
1370 #[test]
1374 fn decode_all_selection_leaves_trailer_untouched() {
1375 let mut buf = vec![0x03, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1376 buf.extend_from_slice(&[0xAA; 4]);
1377 let (sel, consumed) = Selection::decode(&buf).unwrap();
1378 assert_eq!(sel, Selection::All);
1379 assert_eq!(consumed, 16);
1380 assert_eq!(&buf[consumed..], &[0xAA; 4]);
1381 }
1382
1383 #[test]
1388 fn h5py_single_block_selection_is_version_one() {
1389 let mut buf = vec![0x02, 0, 0, 0]; buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&[0u8; 8]); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&4u32.to_le_bytes()); buf.extend_from_slice(&11u32.to_le_bytes()); let (sel, consumed) = Selection::decode(&buf).unwrap();
1398 assert_eq!(consumed, buf.len());
1399 match sel {
1400 Selection::Hyperslab {
1401 rank,
1402 form: Hyperslab::Blocks(blocks),
1403 } => {
1404 assert_eq!(rank, 1);
1405 assert_eq!(
1406 blocks,
1407 vec![HyperslabBlock {
1408 start: vec![4],
1409 end: vec![11],
1410 }]
1411 );
1412 }
1413 other => panic!("expected a version-1 block list, got {other:?}"),
1414 }
1415 }
1416
1417 #[test]
1418 fn decode_hyperslab_block_list_multi_block_2d() {
1419 let mut buf = vec![0x02, 0, 0, 0]; buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&[0u8; 8]);
1422 buf.extend_from_slice(&2u32.to_le_bytes()); buf.extend_from_slice(&2u32.to_le_bytes()); for v in [0u32, 0, 1, 1] {
1426 buf.extend_from_slice(&v.to_le_bytes());
1427 }
1428 for v in [2u32, 2, 3, 3] {
1430 buf.extend_from_slice(&v.to_le_bytes());
1431 }
1432
1433 let (sel, consumed) = Selection::decode(&buf).unwrap();
1434 assert_eq!(consumed, buf.len());
1435 match sel {
1436 Selection::Hyperslab {
1437 rank,
1438 form: Hyperslab::Blocks(blocks),
1439 } => {
1440 assert_eq!(rank, 2);
1441 assert_eq!(blocks.len(), 2);
1442 assert_eq!(blocks[0].start, vec![0, 0]);
1443 assert_eq!(blocks[0].end, vec![1, 1]);
1444 assert_eq!(blocks[1].start, vec![2, 2]);
1445 assert_eq!(blocks[1].end, vec![3, 3]);
1446 }
1447 other => panic!("expected block list, got {other:?}"),
1448 }
1449 }
1450
1451 #[test]
1455 fn decode_regular_hyperslab_v2() {
1456 let mut buf = vec![0x02, 0, 0, 0]; buf.extend_from_slice(&2u32.to_le_bytes()); buf.push(0x01); buf.extend_from_slice(&[0u8; 4]); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&2u64.to_le_bytes()); buf.extend_from_slice(&4u64.to_le_bytes()); buf.extend_from_slice(&3u64.to_le_bytes()); buf.extend_from_slice(&2u64.to_le_bytes()); let (sel, consumed) = Selection::decode(&buf).unwrap();
1467 assert_eq!(consumed, buf.len());
1468 match sel {
1469 Selection::Hyperslab {
1470 rank,
1471 form: Hyperslab::Regular(r),
1472 } => {
1473 assert_eq!(rank, 1);
1474 assert_eq!(r.start, vec![2]);
1475 assert_eq!(r.stride, vec![4]);
1476 assert_eq!(r.count, vec![3]);
1477 assert_eq!(r.block, vec![2]);
1478 }
1479 other => panic!("expected a regular hyperslab, got {other:?}"),
1480 }
1481 }
1482
1483 #[test]
1488 fn decode_regular_hyperslab_v3_unlimited_count() {
1489 let mut buf = vec![0x02, 0, 0, 0]; buf.extend_from_slice(&3u32.to_le_bytes()); buf.push(0x01); buf.push(0x02); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&5u16.to_le_bytes()); buf.extend_from_slice(&0xFFFFu16.to_le_bytes()); buf.extend_from_slice(&3u16.to_le_bytes()); let (sel, consumed) = Selection::decode(&buf).unwrap();
1500 assert_eq!(consumed, buf.len());
1501 match sel {
1502 Selection::Hyperslab {
1503 form: Hyperslab::Regular(r),
1504 ..
1505 } => {
1506 assert_eq!(r.count, vec![UNLIMITED]);
1507 assert_eq!(r.block, vec![3]);
1508 }
1509 other => panic!("expected a regular hyperslab, got {other:?}"),
1510 }
1511 }
1512
1513 #[test]
1514 fn decode_points_truncated_header_is_buffer_too_short() {
1515 let buf = [0x01, 0, 0, 0]; let err = Selection::decode(&buf).unwrap_err();
1517 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1518 }
1519
1520 #[test]
1524 fn decode_points_matches_libhdf5_image() {
1525 let blob = include_bytes!("../../tests/fixtures/points4_v1.bin");
1526 let sel_bytes = strip_h5sencode_envelope(blob);
1527 let (sel, consumed) = Selection::decode(sel_bytes).unwrap();
1528 assert_eq!(consumed, sel_bytes.len());
1529 assert_eq!(
1530 sel,
1531 Selection::Points(PointSelection {
1532 rank: 1,
1533 points: vec![vec![1], vec![3], vec![7], vec![15]],
1534 })
1535 );
1536 }
1537
1538 #[test]
1539 fn decode_points_version_2_small_enc_size() {
1540 let mut buf = vec![0x01, 0, 0, 0]; buf.extend_from_slice(&2u32.to_le_bytes()); buf.push(0x02); buf.extend_from_slice(&2u32.to_le_bytes()); buf.extend_from_slice(&2u16.to_le_bytes()); buf.extend_from_slice(&1u16.to_le_bytes()); buf.extend_from_slice(&5u16.to_le_bytes());
1547 buf.extend_from_slice(&9u16.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes());
1549
1550 let (sel, consumed) = Selection::decode(&buf).unwrap();
1551 assert_eq!(consumed, buf.len());
1552 assert_eq!(
1553 sel,
1554 Selection::Points(PointSelection {
1555 rank: 2,
1556 points: vec![vec![1, 5], vec![9, 0]],
1557 })
1558 );
1559 }
1560
1561 #[test]
1562 fn decode_points_rejects_bad_version() {
1563 let mut buf = vec![0x01, 0, 0, 0];
1564 buf.extend_from_slice(&3u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1566 assert!(matches!(err, FormatError::InvalidData(_)));
1567 }
1568
1569 #[test]
1570 fn decode_points_rejects_unknown_enc_size_tag() {
1571 let mut buf = vec![0x01, 0, 0, 0];
1572 buf.extend_from_slice(&2u32.to_le_bytes()); buf.push(0x03); let err = Selection::decode(&buf).unwrap_err();
1575 assert!(matches!(err, FormatError::InvalidData(_)));
1576 }
1577
1578 #[test]
1579 fn decode_points_rejects_zero_rank() {
1580 let mut buf = vec![0x01, 0, 0, 0];
1581 buf.extend_from_slice(&1u32.to_le_bytes());
1582 buf.extend_from_slice(&[0u8; 8]);
1583 buf.extend_from_slice(&0u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1585 assert!(matches!(err, FormatError::InvalidData(_)));
1586 }
1587
1588 #[test]
1589 fn decode_points_rejects_rank_over_max() {
1590 let mut buf = vec![0x01, 0, 0, 0];
1591 buf.extend_from_slice(&1u32.to_le_bytes());
1592 buf.extend_from_slice(&[0u8; 8]);
1593 buf.extend_from_slice(&33u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1595 assert!(matches!(err, FormatError::InvalidData(_)));
1596 }
1597
1598 #[test]
1599 fn decode_points_truncated_coordinates() {
1600 let mut buf = vec![0x01, 0, 0, 0];
1602 buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&[0u8; 8]); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&5u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes());
1607 let err = Selection::decode(&buf).unwrap_err();
1608 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1609 }
1610
1611 #[test]
1616 fn decode_points_huge_num_points_claim_does_not_allocate() {
1617 let mut buf = vec![0x01, 0, 0, 0];
1618 buf.extend_from_slice(&2u32.to_le_bytes()); buf.push(0x08); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&(1u64 << 40).to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1623 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1624 }
1625
1626 #[test]
1630 fn decode_points_num_points_overflow_does_not_allocate() {
1631 let mut buf = vec![0x01, 0, 0, 0];
1632 buf.extend_from_slice(&2u32.to_le_bytes()); buf.push(0x08); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&(u64::MAX - 1).to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1637 assert!(matches!(err, FormatError::InvalidData(_)));
1638 }
1639
1640 #[test]
1641 fn to_boxes_points_each_point_is_its_own_unit_box() {
1642 let sel = Selection::Points(PointSelection {
1643 rank: 2,
1644 points: vec![vec![1, 2], vec![5, 5]],
1645 });
1646 let boxes = sel.to_boxes(&[8, 8]).unwrap();
1647 assert_eq!(
1648 boxes,
1649 vec![(vec![1, 2], vec![1, 1]), (vec![5, 5], vec![1, 1])]
1650 );
1651 }
1652
1653 #[test]
1654 fn to_boxes_points_rejects_rank_mismatch() {
1655 let sel = Selection::Points(PointSelection {
1656 rank: 2,
1657 points: vec![],
1658 });
1659 let err = sel.to_boxes(&[8]).unwrap_err();
1660 assert!(matches!(err, FormatError::InvalidData(_)));
1661 }
1662
1663 #[test]
1664 fn decode_unknown_type_is_invalid() {
1665 let buf = [0x09, 0, 0, 0];
1666 let err = Selection::decode(&buf).unwrap_err();
1667 assert!(matches!(err, FormatError::InvalidData(_)));
1668 }
1669
1670 #[test]
1671 fn decode_bad_all_version() {
1672 let buf = [0x03, 0, 0, 0, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1673 let err = Selection::decode(&buf).unwrap_err();
1674 assert!(matches!(err, FormatError::InvalidData(_)));
1675 }
1676
1677 #[test]
1678 fn decode_bad_hyperslab_version() {
1679 let buf = [0x02, 0, 0, 0, 0x04, 0, 0, 0];
1680 let err = Selection::decode(&buf).unwrap_err();
1681 assert!(matches!(err, FormatError::InvalidData(_)));
1682 }
1683
1684 #[test]
1685 fn decode_hyperslab_rejects_unknown_flag_bits() {
1686 let mut buf = vec![0x02, 0, 0, 0];
1687 buf.extend_from_slice(&2u32.to_le_bytes());
1688 buf.push(0x02); buf.extend_from_slice(&[0u8; 4]);
1690 buf.extend_from_slice(&1u32.to_le_bytes());
1691 let err = Selection::decode(&buf).unwrap_err();
1692 assert!(matches!(err, FormatError::InvalidData(_)));
1693 }
1694
1695 #[test]
1696 fn decode_hyperslab_rejects_zero_rank() {
1697 let mut buf = vec![0x02, 0, 0, 0];
1698 buf.extend_from_slice(&1u32.to_le_bytes());
1699 buf.extend_from_slice(&[0u8; 8]);
1700 buf.extend_from_slice(&0u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1702 assert!(matches!(err, FormatError::InvalidData(_)));
1703 }
1704
1705 #[test]
1706 fn decode_hyperslab_rejects_rank_over_max() {
1707 let mut buf = vec![0x02, 0, 0, 0];
1708 buf.extend_from_slice(&1u32.to_le_bytes());
1709 buf.extend_from_slice(&[0u8; 8]);
1710 buf.extend_from_slice(&33u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1712 assert!(matches!(err, FormatError::InvalidData(_)));
1713 }
1714
1715 #[test]
1716 fn decode_truncated_header() {
1717 let buf = [0x03, 0, 0];
1718 let err = Selection::decode(&buf).unwrap_err();
1719 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1720 }
1721
1722 #[test]
1723 fn decode_truncated_hyperslab_blocks() {
1724 let mut buf = vec![0x02, 0, 0, 0];
1726 buf.extend_from_slice(&1u32.to_le_bytes());
1727 buf.extend_from_slice(&[0u8; 8]);
1728 buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&5u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes());
1731 buf.extend_from_slice(&1u32.to_le_bytes());
1732 let err = Selection::decode(&buf).unwrap_err();
1733 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1734 }
1735
1736 #[test]
1741 fn decode_huge_num_blocks_claim_does_not_allocate() {
1742 let mut buf = vec![0x02, 0, 0, 0];
1743 buf.extend_from_slice(&3u32.to_le_bytes()); buf.push(0x00); buf.push(0x08); buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&(u64::MAX - 1).to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1749 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1750 }
1751
1752 #[test]
1758 fn bounds_cover_every_block_and_point() {
1759 let hyper = Selection::Hyperslab {
1760 rank: 2,
1761 form: Hyperslab::Blocks(vec![
1762 HyperslabBlock {
1763 start: vec![4, 1],
1764 end: vec![5, 3],
1765 },
1766 HyperslabBlock {
1767 start: vec![0, 6],
1768 end: vec![1, 7],
1769 },
1770 ]),
1771 };
1772 assert_eq!(hyper.bounds(), Some((vec![0, 1], vec![5, 7])));
1773 let points = Selection::Points(PointSelection {
1774 rank: 2,
1775 points: vec![vec![3, 9], vec![7, 2]],
1776 });
1777 assert_eq!(points.bounds(), Some((vec![3, 2], vec![7, 9])));
1778 assert_eq!(Selection::All.bounds(), None);
1779 assert_eq!(Selection::None.bounds(), None);
1780 }
1781
1782 #[test]
1786 fn bounds_of_a_regular_hyperslab_cover_its_expansion() {
1787 let mut buf = Vec::new();
1788 buf.extend_from_slice(&SEL_HYPERSLABS.to_le_bytes());
1789 buf.extend_from_slice(&3u32.to_le_bytes());
1790 buf.push(HYPER_REGULAR_FLAG);
1791 buf.push(4); buf.extend_from_slice(&1u32.to_le_bytes()); for v in [2u32, 5, 3, 2] {
1794 buf.extend_from_slice(&v.to_le_bytes());
1796 }
1797 let (selection, consumed) = Selection::decode(&buf).unwrap();
1798 assert_eq!(consumed, buf.len());
1799 assert_eq!(selection.bounds(), Some((vec![2], vec![13])));
1801 }
1802
1803 #[test]
1806 fn bounds_of_an_unlimited_regular_hyperslab_are_absent() {
1807 let selection = Selection::Hyperslab {
1808 rank: 1,
1809 form: Hyperslab::Regular(RegularHyperslab {
1810 start: vec![0],
1811 stride: vec![1],
1812 count: vec![UNLIMITED],
1813 block: vec![1],
1814 }),
1815 };
1816 assert_eq!(selection.bounds(), None);
1817 }
1818
1819 #[test]
1820 fn to_boxes_all_covers_the_full_extent() {
1821 let boxes = Selection::All.to_boxes(&[3, 5]).unwrap();
1822 assert_eq!(boxes, vec![(vec![0, 0], vec![3, 5])]);
1823 }
1824
1825 #[test]
1826 fn to_boxes_none_is_empty() {
1827 assert_eq!(Selection::None.to_boxes(&[3, 5]).unwrap(), vec![]);
1828 }
1829
1830 #[test]
1831 fn to_boxes_single_block_matches_h5py_fixture() {
1832 let sel = Selection::Hyperslab {
1834 rank: 1,
1835 form: Hyperslab::Blocks(vec![HyperslabBlock {
1836 start: vec![4],
1837 end: vec![11],
1838 }]),
1839 };
1840 let boxes = sel.to_boxes(&[20]).unwrap();
1841 assert_eq!(boxes, vec![(vec![4], vec![8])]);
1842 }
1843
1844 #[test]
1845 fn to_boxes_multi_block_2d() {
1846 let sel = Selection::Hyperslab {
1847 rank: 2,
1848 form: Hyperslab::Blocks(vec![
1849 HyperslabBlock {
1850 start: vec![0, 0],
1851 end: vec![1, 1],
1852 },
1853 HyperslabBlock {
1854 start: vec![2, 2],
1855 end: vec![3, 3],
1856 },
1857 ]),
1858 };
1859 let boxes = sel.to_boxes(&[4, 4]).unwrap();
1860 assert_eq!(
1861 boxes,
1862 vec![(vec![0, 0], vec![2, 2]), (vec![2, 2], vec![2, 2])]
1863 );
1864 }
1865
1866 #[test]
1872 fn resolve_orders_runs_by_element_not_by_box() {
1873 let sel = Selection::Hyperslab {
1874 rank: 2,
1875 form: Hyperslab::Regular(RegularHyperslab {
1876 start: vec![0, 0],
1877 stride: vec![2, 2],
1878 count: vec![2, 2],
1879 block: vec![2, 2],
1880 }),
1881 };
1882 let resolved = sel.resolve(&[4, 4]).unwrap();
1883 assert_eq!(resolved.n_elements(), 16);
1884 let mut covered = Vec::new();
1886 for r in &resolved.runs {
1887 for k in 0..r.len {
1888 covered.push(r.offset_in_extent + k);
1889 }
1890 }
1891 assert_eq!(covered, (0..16).collect::<Vec<u64>>());
1892 assert_eq!(resolved.runs[0].box_index, 0);
1895 assert_eq!(resolved.runs[1].box_index, 1);
1896 assert_eq!(resolved.runs[0].len, 2);
1897 assert_eq!(resolved.runs[2].box_index, 0);
1899 assert_eq!(resolved.runs[2].offset_in_box, 2);
1900 }
1901
1902 #[test]
1906 fn resolve_coalesces_a_box_that_fills_its_extent() {
1907 let resolved = Selection::All.resolve(&[3, 4]).unwrap();
1908 assert_eq!(resolved.runs.len(), 1);
1909 assert_eq!(resolved.runs[0].len, 12);
1910 assert_eq!(resolved.n_elements(), 12);
1911 }
1912
1913 #[test]
1916 fn resolve_keeps_one_run_per_row_of_a_partial_box() {
1917 let sel = Selection::Hyperslab {
1918 rank: 2,
1919 form: Hyperslab::Blocks(vec![HyperslabBlock {
1920 start: vec![1, 1],
1921 end: vec![2, 2],
1922 }]),
1923 };
1924 let resolved = sel.resolve(&[4, 4]).unwrap();
1925 assert_eq!(resolved.runs.len(), 2);
1926 assert_eq!(resolved.runs[0].offset_in_extent, 5);
1927 assert_eq!(resolved.runs[0].offset_in_box, 0);
1928 assert_eq!(resolved.runs[1].offset_in_extent, 9);
1929 assert_eq!(resolved.runs[1].offset_in_box, 2);
1930 }
1931
1932 #[test]
1936 fn resolve_keeps_point_selection_order() {
1937 let sel = Selection::Points(PointSelection {
1938 rank: 2,
1939 points: vec![vec![2, 3], vec![0, 1], vec![1, 0]],
1940 });
1941 let resolved = sel.resolve(&[4, 4]).unwrap();
1942 assert_eq!(
1943 resolved
1944 .runs
1945 .iter()
1946 .map(|r| r.offset_in_extent)
1947 .collect::<Vec<u64>>(),
1948 vec![11, 1, 4]
1949 );
1950 assert!(resolved.runs.iter().all(|r| r.len == 1));
1951 }
1952
1953 #[test]
1960 fn check_hyperslab_by_boundary() {
1961 let dims = [4u64, 6];
1962 assert_eq!(check_hyperslab(&dims, &[1, 2], &[3, 4]), Ok(()));
1963 assert_eq!(check_hyperslab(&dims, &[4, 0], &[0, 6]), Ok(()));
1964 assert_eq!(
1965 check_hyperslab(&dims, &[1, 2], &[3, 5]),
1966 Err(HyperslabError::OutOfBounds {
1967 dim: 1,
1968 start: 2,
1969 count: 5,
1970 extent: 6
1971 })
1972 );
1973 assert_eq!(
1974 check_hyperslab(&dims, &[u64::MAX, 0], &[1, 1]),
1975 Err(HyperslabError::OutOfBounds {
1976 dim: 0,
1977 start: u64::MAX,
1978 count: 1,
1979 extent: 4
1980 })
1981 );
1982 assert_eq!(
1983 check_hyperslab(&dims, &[1, 0], &[u64::MAX, 1]),
1984 Err(HyperslabError::OutOfBounds {
1985 dim: 0,
1986 start: 1,
1987 count: u64::MAX,
1988 extent: 4
1989 })
1990 );
1991 assert_eq!(check_hyperslab(&[], &[], &[]), Ok(()));
1992 assert_eq!(
1993 check_hyperslab(&dims, &[0], &[1, 1]),
1994 Err(HyperslabError::Rank { got: 1, rank: 2 })
1995 );
1996 assert_eq!(
1997 check_hyperslab(&dims, &[0, 0, 0], &[1, 1, 1]),
1998 Err(HyperslabError::Rank { got: 3, rank: 2 })
1999 );
2000 assert_eq!(
2001 check_hyperslab(&dims, &[0, 0], &[1]),
2002 Err(HyperslabError::Rank { got: 1, rank: 2 })
2003 );
2004 let text = HyperslabError::OutOfBounds {
2005 dim: 0,
2006 start: 3,
2007 count: 2,
2008 extent: 4,
2009 }
2010 .to_string();
2011 assert!(text.contains("out of bounds"), "{text}");
2012 }
2013
2014 #[test]
2015 fn resolve_rejects_a_box_past_the_extent() {
2016 let sel = Selection::Hyperslab {
2017 rank: 1,
2018 form: Hyperslab::Blocks(vec![HyperslabBlock {
2019 start: vec![2],
2020 end: vec![5],
2021 }]),
2022 };
2023 let err = sel.resolve(&[4]).unwrap_err();
2024 assert!(
2025 format!("{err}")
2026 .contains("out of bounds in dimension 0: start 2 + count 4 exceeds extent 4"),
2027 "unexpected error: {err}"
2028 );
2029 }
2030
2031 #[test]
2033 fn resolve_of_a_scalar_extent_holds_one_element() {
2034 let all = Selection::All.resolve(&[]).unwrap();
2035 assert_eq!(all.n_elements(), 1);
2036 assert_eq!(all.runs[0].len, 1);
2037 assert_eq!(Selection::None.resolve(&[]).unwrap().n_elements(), 0);
2038 }
2039
2040 #[test]
2041 fn to_boxes_rejects_rank_mismatch() {
2042 let sel = Selection::Hyperslab {
2043 rank: 2,
2044 form: Hyperslab::Blocks(vec![]),
2045 };
2046 let err = sel.to_boxes(&[4]).unwrap_err();
2047 assert!(matches!(err, FormatError::InvalidData(_)));
2048 }
2049
2050 #[test]
2051 fn to_boxes_rejects_inverted_block() {
2052 let sel = Selection::Hyperslab {
2053 rank: 1,
2054 form: Hyperslab::Blocks(vec![HyperslabBlock {
2055 start: vec![5],
2056 end: vec![2],
2057 }]),
2058 };
2059 let err = sel.to_boxes(&[10]).unwrap_err();
2060 assert!(matches!(err, FormatError::InvalidData(_)));
2061 }
2062
2063 #[test]
2067 fn to_boxes_regular_expands_2d_grid() {
2068 let sel = Selection::Hyperslab {
2069 rank: 2,
2070 form: Hyperslab::Regular(RegularHyperslab {
2071 start: vec![0, 0],
2072 stride: vec![4, 4],
2073 count: vec![2, 2],
2074 block: vec![2, 2],
2075 }),
2076 };
2077 let boxes = sel.to_boxes(&[8, 8]).unwrap();
2078 assert_eq!(
2079 boxes,
2080 vec![
2081 (vec![0, 0], vec![2, 2]),
2082 (vec![0, 4], vec![2, 2]),
2083 (vec![4, 0], vec![2, 2]),
2084 (vec![4, 4], vec![2, 2]),
2085 ]
2086 );
2087 }
2088
2089 #[test]
2090 fn to_boxes_regular_single_block_matches_block_list_shape() {
2091 let sel = Selection::Hyperslab {
2094 rank: 1,
2095 form: Hyperslab::Regular(RegularHyperslab {
2096 start: vec![4],
2097 stride: vec![1],
2098 count: vec![1],
2099 block: vec![8],
2100 }),
2101 };
2102 assert_eq!(sel.to_boxes(&[20]).unwrap(), vec![(vec![4], vec![8])]);
2103 }
2104
2105 #[test]
2106 fn to_boxes_regular_zero_count_is_empty() {
2107 let sel = Selection::Hyperslab {
2108 rank: 1,
2109 form: Hyperslab::Regular(RegularHyperslab {
2110 start: vec![0],
2111 stride: vec![1],
2112 count: vec![0],
2113 block: vec![1],
2114 }),
2115 };
2116 assert_eq!(sel.to_boxes(&[10]).unwrap(), vec![]);
2117 }
2118
2119 #[test]
2120 fn to_boxes_regular_rejects_unlimited_count() {
2121 let sel = Selection::Hyperslab {
2122 rank: 1,
2123 form: Hyperslab::Regular(RegularHyperslab {
2124 start: vec![0],
2125 stride: vec![1],
2126 count: vec![UNLIMITED],
2127 block: vec![1],
2128 }),
2129 };
2130 let err = sel.to_boxes(&[10]).unwrap_err();
2131 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2132 }
2133
2134 #[test]
2135 fn to_boxes_regular_rejects_unlimited_block() {
2136 let sel = Selection::Hyperslab {
2137 rank: 1,
2138 form: Hyperslab::Regular(RegularHyperslab {
2139 start: vec![0],
2140 stride: vec![1],
2141 count: vec![1],
2142 block: vec![UNLIMITED],
2143 }),
2144 };
2145 let err = sel.to_boxes(&[10]).unwrap_err();
2146 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2147 }
2148
2149 #[test]
2152 fn to_boxes_regular_rejects_huge_box_count() {
2153 let sel = Selection::Hyperslab {
2154 rank: 2,
2155 form: Hyperslab::Regular(RegularHyperslab {
2156 start: vec![0, 0],
2157 stride: vec![1, 1],
2158 count: vec![1 << 30, 1 << 30],
2159 block: vec![1, 1],
2160 }),
2161 };
2162 let err = sel.to_boxes(&[u64::MAX, u64::MAX]).unwrap_err();
2163 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2164 }
2165
2166 #[test]
2180 fn selection_matches_libhdf5_image() {
2181 let cases: Vec<(&[u8], Selection, Selection)> = vec![
2182 (
2183 include_bytes!("../../tests/fixtures/all_v1.bin"),
2184 Selection::All,
2185 Selection::All,
2186 ),
2187 (
2188 include_bytes!("../../tests/fixtures/none_v1.bin"),
2189 Selection::None,
2190 Selection::None,
2191 ),
2192 (
2193 include_bytes!("../../tests/fixtures/hyperslab_single_block_v1.bin"),
2194 Selection::Hyperslab {
2195 rank: 1,
2196 form: Hyperslab::Blocks(vec![HyperslabBlock {
2197 start: vec![4],
2198 end: vec![11],
2199 }]),
2200 },
2201 Selection::Hyperslab {
2202 rank: 1,
2203 form: Hyperslab::Blocks(vec![HyperslabBlock {
2204 start: vec![4],
2205 end: vec![11],
2206 }]),
2207 },
2208 ),
2209 (
2210 include_bytes!("../../tests/fixtures/hyperslab_regular_3blocks_v1.bin"),
2211 Selection::Hyperslab {
2217 rank: 1,
2218 form: Hyperslab::Regular(RegularHyperslab {
2219 start: vec![0],
2220 stride: vec![5],
2221 count: vec![3],
2222 block: vec![2],
2223 }),
2224 },
2225 Selection::Hyperslab {
2226 rank: 1,
2227 form: Hyperslab::Blocks(vec![
2228 HyperslabBlock {
2229 start: vec![0],
2230 end: vec![1],
2231 },
2232 HyperslabBlock {
2233 start: vec![5],
2234 end: vec![6],
2235 },
2236 HyperslabBlock {
2237 start: vec![10],
2238 end: vec![11],
2239 },
2240 ]),
2241 },
2242 ),
2243 (
2244 include_bytes!("../../tests/fixtures/hyperslab_2d_regular_v1.bin"),
2245 Selection::Hyperslab {
2246 rank: 2,
2247 form: Hyperslab::Regular(RegularHyperslab {
2248 start: vec![0, 0],
2249 stride: vec![4, 4],
2250 count: vec![2, 2],
2251 block: vec![2, 2],
2252 }),
2253 },
2254 Selection::Hyperslab {
2255 rank: 2,
2256 form: Hyperslab::Blocks(vec![
2257 HyperslabBlock {
2258 start: vec![0, 0],
2259 end: vec![1, 1],
2260 },
2261 HyperslabBlock {
2262 start: vec![0, 4],
2263 end: vec![1, 5],
2264 },
2265 HyperslabBlock {
2266 start: vec![4, 0],
2267 end: vec![5, 1],
2268 },
2269 HyperslabBlock {
2270 start: vec![4, 4],
2271 end: vec![5, 5],
2272 },
2273 ]),
2274 },
2275 ),
2276 (
2277 include_bytes!("../../tests/fixtures/points4_v1.bin"),
2278 Selection::Points(PointSelection {
2279 rank: 1,
2280 points: vec![vec![1], vec![3], vec![7], vec![15]],
2281 }),
2282 Selection::Points(PointSelection {
2283 rank: 1,
2284 points: vec![vec![1], vec![3], vec![7], vec![15]],
2285 }),
2286 ),
2287 ];
2288
2289 for (blob, sel, want_decoded) in cases {
2290 let expected = strip_h5sencode_envelope(blob);
2291 let encoded = sel.encode().unwrap();
2292 assert_eq!(
2293 encoded, expected,
2294 "encode() mismatch for {sel:?}: got {encoded:02x?}, want {expected:02x?}"
2295 );
2296
2297 let (decoded, consumed) = Selection::decode(expected).unwrap();
2301 assert_eq!(consumed, expected.len());
2302 assert_eq!(decoded, want_decoded);
2303 }
2304 }
2305
2306 #[test]
2311 fn encode_decode_round_trips() {
2312 let values = vec![
2313 Selection::None,
2314 Selection::All,
2315 Selection::Points(PointSelection {
2316 rank: 2,
2317 points: vec![vec![0, 0], vec![3, 5], vec![9, 1]],
2318 }),
2319 Selection::Hyperslab {
2320 rank: 2,
2321 form: Hyperslab::Blocks(vec![
2322 HyperslabBlock {
2323 start: vec![0, 0],
2324 end: vec![1, 1],
2325 },
2326 HyperslabBlock {
2327 start: vec![4, 4],
2328 end: vec![5, 6],
2329 },
2330 ]),
2331 },
2332 ];
2333 for sel in values {
2334 let encoded = sel.encode().unwrap();
2335 let (decoded, consumed) = Selection::decode(&encoded).unwrap();
2336 assert_eq!(consumed, encoded.len());
2337 assert_eq!(decoded, sel);
2338 }
2339 }
2340
2341 #[test]
2346 fn encode_decode_round_trips_regular_hyperslab_to_its_block_list() {
2347 let sel = Selection::Hyperslab {
2348 rank: 2,
2349 form: Hyperslab::Regular(RegularHyperslab {
2350 start: vec![1, 2],
2351 stride: vec![3, 3],
2352 count: vec![2, 3],
2353 block: vec![1, 2],
2354 }),
2355 };
2356 let encoded = sel.encode().unwrap();
2357 let (decoded, consumed) = Selection::decode(&encoded).unwrap();
2358 assert_eq!(consumed, encoded.len());
2359 let Selection::Hyperslab {
2360 form: Hyperslab::Blocks(blocks),
2361 ..
2362 } = decoded
2363 else {
2364 panic!("expected a decoded block list");
2365 };
2366 assert_eq!(blocks.len(), 6);
2369 assert_eq!(blocks[0].start, vec![1, 2]);
2370 assert_eq!(blocks[0].end, vec![1, 3]);
2371 assert_eq!(blocks[5].start, vec![4, 8]);
2372 assert_eq!(blocks[5].end, vec![4, 9]);
2373 }
2374
2375 #[test]
2376 fn encode_points_rejects_zero_rank() {
2377 let sel = Selection::Points(PointSelection {
2378 rank: 0,
2379 points: vec![],
2380 });
2381 let err = sel.encode().unwrap_err();
2382 assert!(matches!(err, FormatError::InvalidData(_)));
2383 }
2384
2385 #[test]
2386 fn encode_points_rejects_coordinate_length_mismatch() {
2387 let sel = Selection::Points(PointSelection {
2388 rank: 2,
2389 points: vec![vec![1, 2, 3]],
2390 });
2391 let err = sel.encode().unwrap_err();
2392 assert!(matches!(err, FormatError::InvalidData(_)));
2393 }
2394
2395 #[test]
2396 fn encode_points_rejects_coordinate_over_u32() {
2397 let sel = Selection::Points(PointSelection {
2398 rank: 1,
2399 points: vec![vec![1u64 << 40]],
2400 });
2401 let err = sel.encode().unwrap_err();
2402 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2403 }
2404
2405 #[test]
2406 fn encode_hyperslab_rejects_zero_rank() {
2407 let sel = Selection::Hyperslab {
2408 rank: 0,
2409 form: Hyperslab::Blocks(vec![]),
2410 };
2411 let err = sel.encode().unwrap_err();
2412 assert!(matches!(err, FormatError::InvalidData(_)));
2413 }
2414
2415 #[test]
2416 fn encode_hyperslab_rejects_block_length_mismatch() {
2417 let sel = Selection::Hyperslab {
2418 rank: 2,
2419 form: Hyperslab::Blocks(vec![HyperslabBlock {
2420 start: vec![0],
2421 end: vec![1],
2422 }]),
2423 };
2424 let err = sel.encode().unwrap_err();
2425 assert!(matches!(err, FormatError::InvalidData(_)));
2426 }
2427
2428 #[test]
2429 fn encode_hyperslab_rejects_regular_field_length_mismatch() {
2430 let sel = Selection::Hyperslab {
2431 rank: 2,
2432 form: Hyperslab::Regular(RegularHyperslab {
2433 start: vec![0],
2434 stride: vec![1],
2435 count: vec![1],
2436 block: vec![1],
2437 }),
2438 };
2439 let err = sel.encode().unwrap_err();
2440 assert!(matches!(err, FormatError::InvalidData(_)));
2441 }
2442
2443 #[test]
2444 fn encode_hyperslab_rejects_block_coordinate_over_u32() {
2445 let sel = Selection::Hyperslab {
2446 rank: 1,
2447 form: Hyperslab::Blocks(vec![HyperslabBlock {
2448 start: vec![0],
2449 end: vec![1u64 << 40],
2450 }]),
2451 };
2452 let err = sel.encode().unwrap_err();
2453 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2454 }
2455
2456 const LIBHDF5_UNLIMITED_HYPERSLAB: &[u8] = &[
2465 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, ];
2481
2482 fn unlimited_rows() -> Selection {
2483 Selection::Hyperslab {
2484 rank: 2,
2485 form: Hyperslab::Regular(RegularHyperslab {
2486 start: vec![0, 0],
2487 stride: vec![1, 1],
2488 count: vec![UNLIMITED, 1],
2489 block: vec![1, 2],
2490 }),
2491 }
2492 }
2493
2494 #[test]
2498 fn unlimited_hyperslab_encodes_as_the_libhdf5_version_2_image() {
2499 assert_eq!(
2500 unlimited_rows().encode().unwrap(),
2501 LIBHDF5_UNLIMITED_HYPERSLAB
2502 );
2503 }
2504
2505 #[test]
2508 fn unlimited_hyperslab_decodes_from_the_libhdf5_image() {
2509 let (sel, used) = Selection::decode(LIBHDF5_UNLIMITED_HYPERSLAB).unwrap();
2510 assert_eq!(used, LIBHDF5_UNLIMITED_HYPERSLAB.len());
2511 assert_eq!(sel, unlimited_rows());
2512 assert_eq!(sel.unlim_dim(), Some(0));
2513 }
2514
2515 #[test]
2518 fn an_unlimited_selection_has_no_bounds() {
2519 assert_eq!(unlimited_rows().bounds(), None);
2520 }
2521
2522 #[test]
2525 fn clip_unlimited_cuts_the_unlimited_dimension_to_the_extent() {
2526 let clipped = unlimited_rows().clip_unlimited(3).unwrap();
2527 assert_eq!(
2528 clipped.to_boxes(&[3, 2]).unwrap(),
2532 vec![(vec![0, 0], vec![3, 2])]
2533 );
2534 assert_eq!(unlimited_rows().clip_unlimited(0).unwrap(), Selection::None);
2537 assert_eq!(Selection::All.clip_unlimited(7).unwrap(), Selection::All);
2539 }
2540
2541 #[test]
2545 fn clip_unlimited_truncates_the_block_the_extent_cuts_through() {
2546 let sel = Selection::Hyperslab {
2547 rank: 1,
2548 form: Hyperslab::Regular(RegularHyperslab {
2549 start: vec![1],
2550 stride: vec![4],
2551 count: vec![UNLIMITED],
2552 block: vec![3],
2553 }),
2554 };
2555 assert_eq!(
2557 sel.clip_unlimited(7).unwrap().to_boxes(&[7]).unwrap(),
2558 vec![(vec![1], vec![3]), (vec![5], vec![2])]
2559 );
2560 }
2561
2562 #[test]
2566 fn clip_extent_matches_the_slices_the_source_supplies() {
2567 let Selection::Hyperslab {
2568 form: Hyperslab::Regular(rows),
2569 ..
2570 } = unlimited_rows()
2571 else {
2572 unreachable!()
2573 };
2574 for extent in [0u64, 1, 6, 10] {
2576 assert_eq!(rows.num_slices(extent), extent);
2577 assert_eq!(rows.clip_extent(extent, false), extent);
2578 }
2579
2580 let strided = RegularHyperslab {
2583 start: vec![0],
2584 stride: vec![4],
2585 count: vec![UNLIMITED],
2586 block: vec![3],
2587 };
2588 assert_eq!(strided.num_slices(10), 8);
2589 assert_eq!(strided.clip_extent(8, false), 10);
2590 assert_eq!(strided.clip_extent(6, false), 7);
2593 assert_eq!(strided.clip_extent(6, true), 8);
2594 assert_eq!(strided.clip_extent(0, false), 0);
2595 }
2596
2597 #[test]
2601 fn unlimited_slice_shape_and_block_extraction() {
2602 let Selection::Hyperslab {
2603 form: Hyperslab::Regular(rows),
2604 ..
2605 } = unlimited_rows()
2606 else {
2607 unreachable!()
2608 };
2609 assert_eq!(rows.num_elem_non_unlim(), Some(2));
2610 let third = rows.unlim_block(3);
2611 assert_eq!(third.start, vec![3, 0]);
2612 assert_eq!(third.count, vec![1, 1]);
2613 assert_eq!(third.block, vec![1, 2]);
2614 assert_eq!(third.unlim_dim(), None);
2615 }
2616}