1use crate::format::{FormatError, FormatResult};
64
65pub const UNLIMITED: u64 = u64::MAX;
69
70const SEL_NONE: u32 = 0;
71const SEL_POINTS: u32 = 1;
72const SEL_HYPERSLABS: u32 = 2;
73const SEL_ALL: u32 = 3;
74
75const ALL_NONE_VERSION: u32 = 1;
76
77const HYPER_VERSION_1: u32 = 1;
78const HYPER_VERSION_2: u32 = 2;
79const HYPER_VERSION_3: u32 = 3;
80
81const HYPER_REGULAR_FLAG: u8 = 0x01;
82
83const POINT_VERSION_1: u32 = 1;
84const POINT_VERSION_2: u32 = 2;
85
86const MAX_RANK: usize = 32;
90
91#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct HyperslabBlock {
95 pub start: Vec<u64>,
96 pub end: Vec<u64>,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
102pub struct RegularHyperslab {
103 pub start: Vec<u64>,
104 pub stride: Vec<u64>,
105 pub count: Vec<u64>,
106 pub block: Vec<u64>,
107}
108
109impl RegularHyperslab {
110 pub fn unlim_dim(&self) -> Option<usize> {
115 (0..self.count.len())
116 .find(|&d| self.count[d] == UNLIMITED || self.block.get(d) == Some(&UNLIMITED))
117 }
118
119 fn clip_diminfo(start: u64, stride: u64, count: u64, block: u64, clip_size: u64) -> (u64, u64) {
123 if start >= clip_size {
124 if block == UNLIMITED {
125 (count, 0)
126 } else {
127 (0, block)
128 }
129 } else if block == UNLIMITED || block == stride {
130 (1, clip_size - start)
131 } else {
132 let stride = stride.max(1);
134 ((clip_size - start).div_ceil(stride), block)
135 }
136 }
137
138 pub fn num_slices(&self, clip_size: u64) -> u64 {
143 let Some(d) = self.unlim_dim() else {
144 return 0;
145 };
146 let (start, stride) = (self.start[d], self.stride[d]);
147 let (count, block) =
148 Self::clip_diminfo(start, stride, self.count[d], self.block[d], clip_size);
149 if block == 0 || count == 0 {
150 return 0;
151 }
152 if count == 1 {
153 return block;
154 }
155 let span = stride * (count - 1) + block;
156 let avail = clip_size - start;
157 if span > avail {
158 block * count - (span - avail)
159 } else {
160 block * count
161 }
162 }
163
164 pub fn clip_extent(&self, num_slices: u64, incl_trail: bool) -> u64 {
169 let Some(d) = self.unlim_dim() else {
170 return 0;
171 };
172 let (start, stride, block) = (self.start[d], self.stride[d], self.block[d]);
173 if num_slices == 0 {
174 return if incl_trail { start } else { 0 };
175 }
176 if block == UNLIMITED || block == stride {
177 return start + num_slices;
178 }
179 let block = block.max(1);
180 let count = num_slices / block;
181 let rem = num_slices - count * block;
182 if rem > 0 {
183 start + count * stride + rem
184 } else if incl_trail {
185 start + count * stride
186 } else {
187 start + (count - 1) * stride + block
188 }
189 }
190
191 pub fn num_elem_non_unlim(&self) -> Option<u64> {
195 let d = self.unlim_dim()?;
196 (0..self.count.len())
197 .filter(|&i| i != d)
198 .try_fold(1u64, |acc, i| {
199 acc.checked_mul(self.count[i])?.checked_mul(self.block[i])
200 })
201 }
202
203 pub fn unlim_block(&self, index: u64) -> Self {
207 let mut out = self.clone();
208 if let Some(d) = self.unlim_dim() {
209 out.start[d] = self.start[d] + index * self.stride[d];
210 out.count[d] = 1;
211 }
212 out
213 }
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
224pub enum Hyperslab {
225 Regular(RegularHyperslab),
226 Blocks(Vec<HyperslabBlock>),
227}
228
229#[derive(Debug, Clone, PartialEq, Eq)]
235pub struct PointSelection {
236 pub rank: usize,
237 pub points: Vec<Vec<u64>>,
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub(crate) struct SelectionRun {
248 pub box_index: usize,
250 pub offset_in_box: u64,
253 pub offset_in_extent: u64,
256 pub len: u64,
258}
259
260#[derive(Debug, Clone, PartialEq, Eq)]
262pub(crate) struct ResolvedSelection {
263 pub boxes: Vec<(Vec<u64>, Vec<u64>)>,
266 pub runs: Vec<SelectionRun>,
269}
270
271impl ResolvedSelection {
272 pub(crate) fn n_elements(&self) -> u64 {
274 self.runs.iter().map(|r| r.len).sum()
275 }
276}
277
278fn row_major_strides(dims: &[u64]) -> FormatResult<Vec<u64>> {
281 let mut strides = vec![1u64; dims.len()];
282 for d in (0..dims.len().saturating_sub(1)).rev() {
283 strides[d] = strides[d + 1].checked_mul(dims[d + 1]).ok_or_else(|| {
284 FormatError::InvalidData(format!(
285 "extent {dims:?} holds more elements than u64 counts"
286 ))
287 })?;
288 }
289 Ok(strides)
290}
291
292fn push_box_runs(
299 box_index: usize,
300 start: &[u64],
301 count: &[u64],
302 dims: &[u64],
303 strides: &[u64],
304 runs: &mut Vec<SelectionRun>,
305) -> FormatResult<()> {
306 let rank = dims.len();
307 if start.len() != rank || count.len() != rank {
308 return Err(FormatError::InvalidData(format!(
309 "selection box of rank {} against a {rank}-dimensional extent",
310 start.len()
311 )));
312 }
313 for d in 0..rank {
314 let end = start[d]
315 .checked_add(count[d])
316 .ok_or_else(|| FormatError::InvalidData("selection box coordinate overflows".into()))?;
317 if end > dims[d] {
318 return Err(FormatError::InvalidData(format!(
319 "selection box covers [{}, {end}) of dimension {d}, past the extent {}",
320 start[d], dims[d]
321 )));
322 }
323 }
324 if count.contains(&0) {
325 return Ok(());
326 }
327 if rank == 0 {
328 runs.push(SelectionRun {
331 box_index,
332 offset_in_box: 0,
333 offset_in_extent: 0,
334 len: 1,
335 });
336 return Ok(());
337 }
338 let box_strides = row_major_strides(count)?;
339 let run_len = count[rank - 1];
340 let n_outer = count[..rank - 1]
341 .iter()
342 .try_fold(1u64, |acc, &c| acc.checked_mul(c))
343 .ok_or_else(|| FormatError::InvalidData("selection box element count overflows".into()))?;
344 let mut coords = vec![0u64; rank - 1];
345 for _ in 0..n_outer {
346 let mut offset_in_extent = start[rank - 1];
347 let mut offset_in_box = 0u64;
348 for d in 0..rank - 1 {
349 offset_in_extent += (start[d] + coords[d]) * strides[d];
350 offset_in_box += coords[d] * box_strides[d];
351 }
352 match runs.last_mut() {
353 Some(prev)
354 if prev.box_index == box_index
355 && prev.offset_in_extent + prev.len == offset_in_extent
356 && prev.offset_in_box + prev.len == offset_in_box =>
357 {
358 prev.len += run_len;
359 }
360 _ => runs.push(SelectionRun {
361 box_index,
362 offset_in_box,
363 offset_in_extent,
364 len: run_len,
365 }),
366 }
367 for d in (0..rank - 1).rev() {
368 coords[d] += 1;
369 if coords[d] < count[d] {
370 break;
371 }
372 coords[d] = 0;
373 }
374 }
375 Ok(())
376}
377
378#[derive(Debug, Clone, PartialEq, Eq)]
380pub enum Selection {
381 None,
383 All,
386 Hyperslab { rank: usize, form: Hyperslab },
388 Points(PointSelection),
390}
391
392impl Selection {
393 pub fn decode(buf: &[u8]) -> FormatResult<(Self, usize)> {
400 if buf.len() < 4 {
401 return Err(FormatError::BufferTooShort {
402 needed: 4,
403 available: buf.len(),
404 });
405 }
406 let sel_type = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
407 let body = &buf[4..];
408 match sel_type {
409 SEL_NONE => {
410 let consumed = decode_all_none_body(body)?;
411 Ok((Self::None, 4 + consumed))
412 }
413 SEL_ALL => {
414 let consumed = decode_all_none_body(body)?;
415 Ok((Self::All, 4 + consumed))
416 }
417 SEL_HYPERSLABS => {
418 let (rank, form, consumed) = decode_hyperslab_body(body)?;
419 Ok((Self::Hyperslab { rank, form }, 4 + consumed))
420 }
421 SEL_POINTS => {
422 let (points, consumed) = decode_points_body(body)?;
423 Ok((Self::Points(points), 4 + consumed))
424 }
425 other => Err(FormatError::InvalidData(format!(
426 "unknown dataspace selection type {other}"
427 ))),
428 }
429 }
430
431 pub fn to_boxes(&self, dims: &[u64]) -> FormatResult<Vec<(Vec<u64>, Vec<u64>)>> {
443 match self {
444 Self::None => Ok(Vec::new()),
445 Self::All => Ok(vec![(vec![0u64; dims.len()], dims.to_vec())]),
446 Self::Points(ps) => {
447 if ps.rank != dims.len() {
448 return Err(FormatError::InvalidData(format!(
449 "point selection rank {} does not match the {}-dimensional extent",
450 ps.rank,
451 dims.len()
452 )));
453 }
454 Ok(ps
458 .points
459 .iter()
460 .map(|p| (p.clone(), vec![1u64; ps.rank]))
461 .collect())
462 }
463 Self::Hyperslab { rank, form } => {
464 if *rank != dims.len() {
465 return Err(FormatError::InvalidData(format!(
466 "hyperslab selection rank {rank} does not match the \
467 {}-dimensional extent",
468 dims.len()
469 )));
470 }
471 match form {
472 Hyperslab::Blocks(blocks) => blocks
473 .iter()
474 .map(|b| {
475 let count = b
476 .start
477 .iter()
478 .zip(&b.end)
479 .map(|(&s, &e)| {
480 e.checked_sub(s).and_then(|d| d.checked_add(1)).ok_or_else(
481 || {
482 FormatError::InvalidData(
483 "hyperslab block end precedes its start".into(),
484 )
485 },
486 )
487 })
488 .collect::<FormatResult<Vec<u64>>>()?;
489 Ok((b.start.clone(), count))
490 })
491 .collect(),
492 Hyperslab::Regular(r) => regular_hyperslab_to_boxes(r),
493 }
494 }
495 }
496 }
497
498 pub(crate) fn resolve(&self, dims: &[u64]) -> FormatResult<ResolvedSelection> {
518 let boxes = self.to_boxes(dims)?;
519 let strides = row_major_strides(dims)?;
520 let mut runs = Vec::new();
521 for (i, (start, count)) in boxes.iter().enumerate() {
522 push_box_runs(i, start, count, dims, &strides, &mut runs)?;
523 }
524 if !matches!(self, Self::Points(_)) {
533 runs.sort_unstable_by_key(|r| r.offset_in_extent);
534 }
535 Ok(ResolvedSelection { boxes, runs })
536 }
537
538 pub fn encode(&self) -> FormatResult<Vec<u8>> {
560 match self {
561 Self::None => Ok(encode_all_none(SEL_NONE)),
562 Self::All => Ok(encode_all_none(SEL_ALL)),
563 Self::Points(ps) => encode_points(ps),
564 Self::Hyperslab { rank, form } => encode_hyperslab(*rank, form),
565 }
566 }
567
568 pub fn unlim_dim(&self) -> Option<usize> {
573 match self {
574 Self::Hyperslab {
575 form: Hyperslab::Regular(r),
576 ..
577 } => r.unlim_dim(),
578 _ => None,
579 }
580 }
581
582 pub fn clip_unlimited(&self, clip_size: u64) -> FormatResult<Self> {
594 let Self::Hyperslab {
595 rank,
596 form: Hyperslab::Regular(r),
597 } = self
598 else {
599 return Ok(self.clone());
600 };
601 let Some(d) = r.unlim_dim() else {
602 return Ok(self.clone());
603 };
604 let (count, block) = RegularHyperslab::clip_diminfo(
605 r.start[d],
606 r.stride[d],
607 r.count[d],
608 r.block[d],
609 clip_size,
610 );
611 if count == 0 || block == 0 {
612 return Ok(Self::None);
613 }
614 let mut clipped = r.clone();
615 clipped.count[d] = count;
616 clipped.block[d] = block;
617
618 let mut blocks = Vec::new();
619 for (start, count) in regular_hyperslab_to_boxes(&clipped)? {
620 if start[d] >= clip_size {
624 continue;
625 }
626 let mut count = count;
627 count[d] = count[d].min(clip_size - start[d]);
628 let end = start
629 .iter()
630 .zip(&count)
631 .map(|(&s, &c)| s + c - 1)
632 .collect::<Vec<u64>>();
633 blocks.push(HyperslabBlock { start, end });
634 }
635 if blocks.is_empty() {
636 return Ok(Self::None);
637 }
638 Ok(Self::Hyperslab {
639 rank: *rank,
640 form: Hyperslab::Blocks(blocks),
641 })
642 }
643
644 pub fn bounds(&self) -> Option<(Vec<u64>, Vec<u64>)> {
651 match self {
652 Self::All | Self::None => None,
653 Self::Hyperslab { rank, form } => {
654 let blocks = hyperslab_to_block_list(*rank, form).ok()?;
655 let first = blocks.first()?;
656 let mut lo = first.start.clone();
657 let mut hi = first.end.clone();
658 for b in &blocks[1..] {
659 for (l, s) in lo.iter_mut().zip(&b.start) {
660 *l = (*l).min(*s);
661 }
662 for (h, e) in hi.iter_mut().zip(&b.end) {
663 *h = (*h).max(*e);
664 }
665 }
666 Some((lo, hi))
667 }
668 Self::Points(ps) => {
669 let first = ps.points.first()?;
670 let mut lo = first.clone();
671 let mut hi = first.clone();
672 for p in &ps.points[1..] {
673 for (l, c) in lo.iter_mut().zip(p) {
674 *l = (*l).min(*c);
675 }
676 for (h, c) in hi.iter_mut().zip(p) {
677 *h = (*h).max(*c);
678 }
679 }
680 Some((lo, hi))
681 }
682 }
683 }
684}
685
686fn to_u32(v: u64) -> FormatResult<u32> {
689 u32::try_from(v).map_err(|_| {
690 FormatError::UnsupportedFeature(format!(
691 "value {v} exceeds the 4-byte encoding Selection::encode targets (version 1, \
692 matching libhdf5's default H5F_LIBVER_V18 low format-version bound)"
693 ))
694 })
695}
696
697fn encode_all_none(sel_type: u32) -> Vec<u8> {
698 let mut buf = Vec::with_capacity(16);
699 buf.extend_from_slice(&sel_type.to_le_bytes());
700 buf.extend_from_slice(&ALL_NONE_VERSION.to_le_bytes());
701 buf.extend_from_slice(&[0u8; 8]);
702 buf
703}
704
705fn encode_points(ps: &PointSelection) -> FormatResult<Vec<u8>> {
706 if ps.rank == 0 || ps.rank > MAX_RANK {
707 return Err(FormatError::InvalidData(format!(
708 "invalid point selection rank {}",
709 ps.rank
710 )));
711 }
712 for p in &ps.points {
713 if p.len() != ps.rank {
714 return Err(FormatError::InvalidData(format!(
715 "point selection coordinate length {} does not match rank {}",
716 p.len(),
717 ps.rank
718 )));
719 }
720 }
721 let num_points: u32 = ps.points.len().try_into().map_err(|_| {
722 FormatError::UnsupportedFeature(format!(
723 "point selection has {} points, too many for version-1 encode (u32 count)",
724 ps.points.len()
725 ))
726 })?;
727 let rank_u32 = ps.rank as u32;
728 let payload_bytes: u32 = num_points
729 .checked_mul(4)
730 .and_then(|v| v.checked_mul(rank_u32))
731 .ok_or_else(|| {
732 FormatError::UnsupportedFeature(
733 "point selection payload too large for version-1 encode".into(),
734 )
735 })?;
736 let len = 8u32.checked_add(payload_bytes).ok_or_else(|| {
737 FormatError::UnsupportedFeature(
738 "point selection payload too large for version-1 encode".into(),
739 )
740 })?;
741
742 let mut buf = Vec::with_capacity(24 + payload_bytes as usize);
743 buf.extend_from_slice(&SEL_POINTS.to_le_bytes());
744 buf.extend_from_slice(&POINT_VERSION_1.to_le_bytes());
745 buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&len.to_le_bytes());
747 buf.extend_from_slice(&rank_u32.to_le_bytes());
748 buf.extend_from_slice(&num_points.to_le_bytes());
749 for p in &ps.points {
750 for &c in p {
751 buf.extend_from_slice(&to_u32(c)?.to_le_bytes());
752 }
753 }
754 Ok(buf)
755}
756
757fn hyperslab_to_block_list(rank: usize, form: &Hyperslab) -> FormatResult<Vec<HyperslabBlock>> {
761 match form {
762 Hyperslab::Blocks(blocks) => {
763 for b in blocks {
764 if b.start.len() != rank || b.end.len() != rank {
765 return Err(FormatError::InvalidData(format!(
766 "hyperslab block coordinate length does not match rank {rank}"
767 )));
768 }
769 }
770 Ok(blocks.clone())
771 }
772 Hyperslab::Regular(r) => {
773 if r.start.len() != rank
774 || r.stride.len() != rank
775 || r.count.len() != rank
776 || r.block.len() != rank
777 {
778 return Err(FormatError::InvalidData(format!(
779 "regular hyperslab field length does not match rank {rank}"
780 )));
781 }
782 regular_hyperslab_to_boxes(r)?
783 .into_iter()
784 .map(|(start, count)| {
785 let end = start
786 .iter()
787 .zip(&count)
788 .map(|(&s, &c)| {
789 s.checked_add(c - 1).ok_or_else(|| {
790 FormatError::InvalidData(
791 "hyperslab box coordinate overflows".into(),
792 )
793 })
794 })
795 .collect::<FormatResult<Vec<u64>>>()?;
796 Ok(HyperslabBlock { start, end })
797 })
798 .collect()
799 }
800 }
801}
802
803fn encode_regular_hyperslab_v2(rank: usize, r: &RegularHyperslab) -> FormatResult<Vec<u8>> {
808 if r.start.len() != rank
809 || r.stride.len() != rank
810 || r.count.len() != rank
811 || r.block.len() != rank
812 {
813 return Err(FormatError::InvalidData(format!(
814 "regular hyperslab field length does not match rank {rank}"
815 )));
816 }
817 let mut buf = Vec::with_capacity(17 + rank * 32);
818 buf.extend_from_slice(&SEL_HYPERSLABS.to_le_bytes());
819 buf.extend_from_slice(&HYPER_VERSION_2.to_le_bytes());
820 buf.push(HYPER_REGULAR_FLAG);
821 let len = 4u32 + 32 * rank as u32;
826 buf.extend_from_slice(&len.to_le_bytes());
827 buf.extend_from_slice(&(rank as u32).to_le_bytes());
828 for d in 0..rank {
829 buf.extend_from_slice(&r.start[d].to_le_bytes());
830 buf.extend_from_slice(&r.stride[d].to_le_bytes());
831 buf.extend_from_slice(&r.count[d].to_le_bytes());
832 buf.extend_from_slice(&r.block[d].to_le_bytes());
833 }
834 Ok(buf)
835}
836
837fn encode_hyperslab(rank: usize, form: &Hyperslab) -> FormatResult<Vec<u8>> {
838 if rank == 0 || rank > MAX_RANK {
839 return Err(FormatError::InvalidData(format!(
840 "invalid hyperslab selection rank {rank}"
841 )));
842 }
843 if let Hyperslab::Regular(r) = form {
850 if r.unlim_dim().is_some() {
851 return encode_regular_hyperslab_v2(rank, r);
852 }
853 }
854 let blocks = hyperslab_to_block_list(rank, form)?;
855 let num_blocks: u32 = blocks.len().try_into().map_err(|_| {
856 FormatError::UnsupportedFeature(format!(
857 "hyperslab selection has {} blocks, too many for version-1 encode (u32 count)",
858 blocks.len()
859 ))
860 })?;
861 let rank_u32 = rank as u32;
862
863 let mut coords: Vec<u32> = Vec::with_capacity(blocks.len() * rank * 2);
864 for b in &blocks {
865 for &s in &b.start {
866 coords.push(to_u32(s)?);
867 }
868 for &e in &b.end {
869 coords.push(to_u32(e)?);
870 }
871 }
872
873 let block_payload = 8u32
874 .checked_mul(rank_u32)
875 .and_then(|v| v.checked_mul(num_blocks))
876 .ok_or_else(|| {
877 FormatError::UnsupportedFeature(
878 "hyperslab selection too large for version-1 encode".into(),
879 )
880 })?;
881 let len = 8u32.checked_add(block_payload).ok_or_else(|| {
882 FormatError::UnsupportedFeature("hyperslab selection too large for version-1 encode".into())
883 })?;
884
885 let mut buf = Vec::with_capacity(24 + coords.len() * 4);
886 buf.extend_from_slice(&SEL_HYPERSLABS.to_le_bytes());
887 buf.extend_from_slice(&HYPER_VERSION_1.to_le_bytes());
888 buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&len.to_le_bytes());
890 buf.extend_from_slice(&rank_u32.to_le_bytes());
891 buf.extend_from_slice(&num_blocks.to_le_bytes());
892 for v in coords {
893 buf.extend_from_slice(&v.to_le_bytes());
894 }
895 Ok(buf)
896}
897
898const MAX_REGULAR_BOXES: u64 = 1 << 20;
906
907fn regular_hyperslab_to_boxes(r: &RegularHyperslab) -> FormatResult<Vec<(Vec<u64>, Vec<u64>)>> {
908 let rank = r.start.len();
909 if r.count.contains(&UNLIMITED) || r.block.contains(&UNLIMITED) {
910 return Err(FormatError::UnsupportedFeature(
911 "unlimited (H5S_UNLIMITED) regular hyperslab dimension".into(),
912 ));
913 }
914 if r.count.contains(&0) || r.block.contains(&0) {
915 return Ok(Vec::new());
916 }
917 let total_boxes = r
918 .count
919 .iter()
920 .try_fold(1u64, |acc, &c| acc.checked_mul(c))
921 .ok_or_else(|| FormatError::InvalidData("regular hyperslab box count overflows".into()))?;
922 if total_boxes > MAX_REGULAR_BOXES {
923 return Err(FormatError::UnsupportedFeature(format!(
924 "regular hyperslab selection expands to {total_boxes} boxes, over the \
925 {MAX_REGULAR_BOXES} cap"
926 )));
927 }
928
929 let mut boxes = Vec::with_capacity(total_boxes as usize);
930 let mut idx = vec![0u64; rank];
931 for _ in 0..total_boxes {
932 let start: Vec<u64> = (0..rank)
933 .map(|d| r.start[d] + idx[d] * r.stride[d])
934 .collect();
935 boxes.push((start, r.block.clone()));
936 for d in (0..rank).rev() {
937 idx[d] += 1;
938 if idx[d] < r.count[d] {
939 break;
940 }
941 idx[d] = 0;
942 }
943 }
944 Ok(boxes)
945}
946
947fn decode_all_none_body(buf: &[u8]) -> FormatResult<usize> {
948 if buf.len() < 4 + 8 {
949 return Err(FormatError::BufferTooShort {
950 needed: 4 + 8,
951 available: buf.len(),
952 });
953 }
954 let version = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
955 if version != ALL_NONE_VERSION {
956 return Err(FormatError::InvalidData(format!(
957 "bad version {version} for all/none dataspace selection"
958 )));
959 }
960 Ok(4 + 8)
962}
963
964fn decode_hyperslab_body(buf: &[u8]) -> FormatResult<(usize, Hyperslab, usize)> {
965 let mut pos = 0usize;
966 if buf.len() < 4 {
967 return Err(FormatError::BufferTooShort {
968 needed: 4,
969 available: buf.len(),
970 });
971 }
972 let version = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
973 pos += 4;
974 if !(HYPER_VERSION_1..=HYPER_VERSION_3).contains(&version) {
975 return Err(FormatError::InvalidData(format!(
976 "bad version {version} for hyperslab dataspace selection"
977 )));
978 }
979
980 let mut flags = 0u8;
981 let enc_size: usize;
982 if version >= HYPER_VERSION_2 {
983 if buf.len() < pos + 1 {
984 return Err(FormatError::BufferTooShort {
985 needed: pos + 1,
986 available: buf.len(),
987 });
988 }
989 flags = buf[pos];
990 pos += 1;
991 if flags & !HYPER_REGULAR_FLAG != 0 {
992 return Err(FormatError::InvalidData(format!(
993 "unknown hyperslab selection flag bits in {flags:#x}"
994 )));
995 }
996
997 if version >= HYPER_VERSION_3 {
998 if buf.len() < pos + 1 {
999 return Err(FormatError::BufferTooShort {
1000 needed: pos + 1,
1001 available: buf.len(),
1002 });
1003 }
1004 enc_size = match buf[pos] {
1005 0x02 => 2,
1006 0x04 => 4,
1007 0x08 => 8,
1008 other => {
1009 return Err(FormatError::InvalidData(format!(
1010 "unknown hyperslab selection encoding size tag {other:#x}"
1011 )))
1012 }
1013 };
1014 pos += 1;
1015 } else {
1016 if buf.len() < pos + 4 {
1018 return Err(FormatError::BufferTooShort {
1019 needed: pos + 4,
1020 available: buf.len(),
1021 });
1022 }
1023 pos += 4;
1024 enc_size = 8;
1025 }
1026 } else {
1027 if buf.len() < pos + 8 {
1029 return Err(FormatError::BufferTooShort {
1030 needed: pos + 8,
1031 available: buf.len(),
1032 });
1033 }
1034 pos += 8;
1035 enc_size = 4;
1036 }
1037
1038 if buf.len() < pos + 4 {
1039 return Err(FormatError::BufferTooShort {
1040 needed: pos + 4,
1041 available: buf.len(),
1042 });
1043 }
1044 let rank = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]) as usize;
1045 pos += 4;
1046 if rank == 0 || rank > MAX_RANK {
1047 return Err(FormatError::InvalidData(format!(
1048 "invalid hyperslab selection rank {rank}"
1049 )));
1050 }
1051
1052 if flags & HYPER_REGULAR_FLAG != 0 {
1053 let mut start = Vec::with_capacity(rank);
1054 let mut stride = Vec::with_capacity(rank);
1055 let mut count = Vec::with_capacity(rank);
1056 let mut block = Vec::with_capacity(rank);
1057 for _ in 0..rank {
1058 if buf.len() < pos + 4 * enc_size {
1059 return Err(FormatError::BufferTooShort {
1060 needed: pos + 4 * enc_size,
1061 available: buf.len(),
1062 });
1063 }
1064 start.push(read_plain(&buf[pos..], enc_size));
1065 pos += enc_size;
1066 stride.push(read_plain(&buf[pos..], enc_size));
1067 pos += enc_size;
1068 count.push(read_dim(&buf[pos..], enc_size));
1069 pos += enc_size;
1070 block.push(read_dim(&buf[pos..], enc_size));
1071 pos += enc_size;
1072 }
1073 Ok((
1074 rank,
1075 Hyperslab::Regular(RegularHyperslab {
1076 start,
1077 stride,
1078 count,
1079 block,
1080 }),
1081 pos,
1082 ))
1083 } else {
1084 if buf.len() < pos + enc_size {
1085 return Err(FormatError::BufferTooShort {
1086 needed: pos + enc_size,
1087 available: buf.len(),
1088 });
1089 }
1090 let num_blocks = read_plain(&buf[pos..], enc_size) as usize;
1091 pos += enc_size;
1092
1093 let mut blocks = Vec::new();
1100 for _ in 0..num_blocks {
1101 let mut start = Vec::with_capacity(rank);
1102 let mut end = Vec::with_capacity(rank);
1103 if buf.len() < pos + 2 * rank * enc_size {
1104 return Err(FormatError::BufferTooShort {
1105 needed: pos + 2 * rank * enc_size,
1106 available: buf.len(),
1107 });
1108 }
1109 for _ in 0..rank {
1110 start.push(read_plain(&buf[pos..], enc_size));
1111 pos += enc_size;
1112 }
1113 for _ in 0..rank {
1114 end.push(read_plain(&buf[pos..], enc_size));
1115 pos += enc_size;
1116 }
1117 blocks.push(HyperslabBlock { start, end });
1118 }
1119 Ok((rank, Hyperslab::Blocks(blocks), pos))
1120 }
1121}
1122
1123fn decode_points_body(buf: &[u8]) -> FormatResult<(PointSelection, usize)> {
1124 let mut pos = 0usize;
1125 if buf.len() < 4 {
1126 return Err(FormatError::BufferTooShort {
1127 needed: 4,
1128 available: buf.len(),
1129 });
1130 }
1131 let version = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
1132 pos += 4;
1133 if version != POINT_VERSION_1 && version != POINT_VERSION_2 {
1134 return Err(FormatError::InvalidData(format!(
1135 "bad version {version} for point dataspace selection"
1136 )));
1137 }
1138
1139 let enc_size: usize;
1140 if version >= POINT_VERSION_2 {
1141 if buf.len() < pos + 1 {
1142 return Err(FormatError::BufferTooShort {
1143 needed: pos + 1,
1144 available: buf.len(),
1145 });
1146 }
1147 enc_size = match buf[pos] {
1148 0x02 => 2,
1149 0x04 => 4,
1150 0x08 => 8,
1151 other => {
1152 return Err(FormatError::InvalidData(format!(
1153 "unknown point selection encoding size tag {other:#x}"
1154 )))
1155 }
1156 };
1157 pos += 1;
1158 } else {
1159 if buf.len() < pos + 8 {
1164 return Err(FormatError::BufferTooShort {
1165 needed: pos + 8,
1166 available: buf.len(),
1167 });
1168 }
1169 pos += 8;
1170 enc_size = 4;
1171 }
1172
1173 if buf.len() < pos + 4 {
1174 return Err(FormatError::BufferTooShort {
1175 needed: pos + 4,
1176 available: buf.len(),
1177 });
1178 }
1179 let rank = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]) as usize;
1180 pos += 4;
1181 if rank == 0 || rank > MAX_RANK {
1182 return Err(FormatError::InvalidData(format!(
1183 "invalid point selection rank {rank}"
1184 )));
1185 }
1186
1187 if buf.len() < pos + enc_size {
1188 return Err(FormatError::BufferTooShort {
1189 needed: pos + enc_size,
1190 available: buf.len(),
1191 });
1192 }
1193 let num_points = read_plain(&buf[pos..], enc_size);
1194 pos += enc_size;
1195
1196 let point_bytes = (rank as u64)
1202 .checked_mul(enc_size as u64)
1203 .and_then(|per_point| per_point.checked_mul(num_points))
1204 .ok_or_else(|| {
1205 FormatError::InvalidData("point selection coordinate buffer size overflows".into())
1206 })?;
1207 if (buf.len() as u64) < pos as u64 + point_bytes {
1208 let needed = usize::try_from(point_bytes)
1209 .ok()
1210 .and_then(|b| pos.checked_add(b))
1211 .unwrap_or(usize::MAX);
1212 return Err(FormatError::BufferTooShort {
1213 needed,
1214 available: buf.len(),
1215 });
1216 }
1217
1218 let mut points = Vec::with_capacity(num_points as usize);
1219 for _ in 0..num_points {
1220 let mut coord = Vec::with_capacity(rank);
1221 for _ in 0..rank {
1222 coord.push(read_plain(&buf[pos..], enc_size));
1223 pos += enc_size;
1224 }
1225 points.push(coord);
1226 }
1227
1228 Ok((PointSelection { rank, points }, pos))
1229}
1230
1231fn read_plain(buf: &[u8], n: usize) -> u64 {
1234 crate::format::bytes::read_le_uint(buf, n)
1235}
1236
1237fn read_dim(buf: &[u8], n: usize) -> u64 {
1243 let v = read_plain(buf, n);
1244 let all_ones = if n >= 8 {
1245 u64::MAX
1246 } else {
1247 (1u64 << (n * 8)) - 1
1248 };
1249 if v == all_ones {
1250 UNLIMITED
1251 } else {
1252 v
1253 }
1254}
1255
1256#[cfg(test)]
1259mod tests {
1260 use super::*;
1261
1262 fn strip_h5sencode_envelope(blob: &[u8]) -> &[u8] {
1268 let extent_size = u32::from_le_bytes([blob[3], blob[4], blob[5], blob[6]]) as usize;
1269 &blob[7 + extent_size..]
1270 }
1271
1272 #[test]
1276 fn decode_all_selection() {
1277 let buf = [
1278 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1282 let (sel, consumed) = Selection::decode(&buf).unwrap();
1283 assert_eq!(consumed, buf.len());
1284 assert_eq!(sel, Selection::All);
1285 }
1286
1287 #[test]
1288 fn decode_none_selection() {
1289 let buf = [
1290 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1294 let (sel, consumed) = Selection::decode(&buf).unwrap();
1295 assert_eq!(consumed, buf.len());
1296 assert_eq!(sel, Selection::None);
1297 }
1298
1299 #[test]
1303 fn decode_all_selection_leaves_trailer_untouched() {
1304 let mut buf = vec![0x03, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1305 buf.extend_from_slice(&[0xAA; 4]);
1306 let (sel, consumed) = Selection::decode(&buf).unwrap();
1307 assert_eq!(sel, Selection::All);
1308 assert_eq!(consumed, 16);
1309 assert_eq!(&buf[consumed..], &[0xAA; 4]);
1310 }
1311
1312 #[test]
1317 fn h5py_single_block_selection_is_version_one() {
1318 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();
1327 assert_eq!(consumed, buf.len());
1328 match sel {
1329 Selection::Hyperslab {
1330 rank,
1331 form: Hyperslab::Blocks(blocks),
1332 } => {
1333 assert_eq!(rank, 1);
1334 assert_eq!(
1335 blocks,
1336 vec![HyperslabBlock {
1337 start: vec![4],
1338 end: vec![11],
1339 }]
1340 );
1341 }
1342 other => panic!("expected a version-1 block list, got {other:?}"),
1343 }
1344 }
1345
1346 #[test]
1347 fn decode_hyperslab_block_list_multi_block_2d() {
1348 let mut buf = vec![0x02, 0, 0, 0]; buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&[0u8; 8]);
1351 buf.extend_from_slice(&2u32.to_le_bytes()); buf.extend_from_slice(&2u32.to_le_bytes()); for v in [0u32, 0, 1, 1] {
1355 buf.extend_from_slice(&v.to_le_bytes());
1356 }
1357 for v in [2u32, 2, 3, 3] {
1359 buf.extend_from_slice(&v.to_le_bytes());
1360 }
1361
1362 let (sel, consumed) = Selection::decode(&buf).unwrap();
1363 assert_eq!(consumed, buf.len());
1364 match sel {
1365 Selection::Hyperslab {
1366 rank,
1367 form: Hyperslab::Blocks(blocks),
1368 } => {
1369 assert_eq!(rank, 2);
1370 assert_eq!(blocks.len(), 2);
1371 assert_eq!(blocks[0].start, vec![0, 0]);
1372 assert_eq!(blocks[0].end, vec![1, 1]);
1373 assert_eq!(blocks[1].start, vec![2, 2]);
1374 assert_eq!(blocks[1].end, vec![3, 3]);
1375 }
1376 other => panic!("expected block list, got {other:?}"),
1377 }
1378 }
1379
1380 #[test]
1384 fn decode_regular_hyperslab_v2() {
1385 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();
1396 assert_eq!(consumed, buf.len());
1397 match sel {
1398 Selection::Hyperslab {
1399 rank,
1400 form: Hyperslab::Regular(r),
1401 } => {
1402 assert_eq!(rank, 1);
1403 assert_eq!(r.start, vec![2]);
1404 assert_eq!(r.stride, vec![4]);
1405 assert_eq!(r.count, vec![3]);
1406 assert_eq!(r.block, vec![2]);
1407 }
1408 other => panic!("expected a regular hyperslab, got {other:?}"),
1409 }
1410 }
1411
1412 #[test]
1417 fn decode_regular_hyperslab_v3_unlimited_count() {
1418 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();
1429 assert_eq!(consumed, buf.len());
1430 match sel {
1431 Selection::Hyperslab {
1432 form: Hyperslab::Regular(r),
1433 ..
1434 } => {
1435 assert_eq!(r.count, vec![UNLIMITED]);
1436 assert_eq!(r.block, vec![3]);
1437 }
1438 other => panic!("expected a regular hyperslab, got {other:?}"),
1439 }
1440 }
1441
1442 #[test]
1443 fn decode_points_truncated_header_is_buffer_too_short() {
1444 let buf = [0x01, 0, 0, 0]; let err = Selection::decode(&buf).unwrap_err();
1446 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1447 }
1448
1449 #[test]
1453 fn decode_points_matches_libhdf5_image() {
1454 let blob = include_bytes!("../../tests/fixtures/points4_v1.bin");
1455 let sel_bytes = strip_h5sencode_envelope(blob);
1456 let (sel, consumed) = Selection::decode(sel_bytes).unwrap();
1457 assert_eq!(consumed, sel_bytes.len());
1458 assert_eq!(
1459 sel,
1460 Selection::Points(PointSelection {
1461 rank: 1,
1462 points: vec![vec![1], vec![3], vec![7], vec![15]],
1463 })
1464 );
1465 }
1466
1467 #[test]
1468 fn decode_points_version_2_small_enc_size() {
1469 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());
1476 buf.extend_from_slice(&9u16.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes());
1478
1479 let (sel, consumed) = Selection::decode(&buf).unwrap();
1480 assert_eq!(consumed, buf.len());
1481 assert_eq!(
1482 sel,
1483 Selection::Points(PointSelection {
1484 rank: 2,
1485 points: vec![vec![1, 5], vec![9, 0]],
1486 })
1487 );
1488 }
1489
1490 #[test]
1491 fn decode_points_rejects_bad_version() {
1492 let mut buf = vec![0x01, 0, 0, 0];
1493 buf.extend_from_slice(&3u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1495 assert!(matches!(err, FormatError::InvalidData(_)));
1496 }
1497
1498 #[test]
1499 fn decode_points_rejects_unknown_enc_size_tag() {
1500 let mut buf = vec![0x01, 0, 0, 0];
1501 buf.extend_from_slice(&2u32.to_le_bytes()); buf.push(0x03); let err = Selection::decode(&buf).unwrap_err();
1504 assert!(matches!(err, FormatError::InvalidData(_)));
1505 }
1506
1507 #[test]
1508 fn decode_points_rejects_zero_rank() {
1509 let mut buf = vec![0x01, 0, 0, 0];
1510 buf.extend_from_slice(&1u32.to_le_bytes());
1511 buf.extend_from_slice(&[0u8; 8]);
1512 buf.extend_from_slice(&0u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1514 assert!(matches!(err, FormatError::InvalidData(_)));
1515 }
1516
1517 #[test]
1518 fn decode_points_rejects_rank_over_max() {
1519 let mut buf = vec![0x01, 0, 0, 0];
1520 buf.extend_from_slice(&1u32.to_le_bytes());
1521 buf.extend_from_slice(&[0u8; 8]);
1522 buf.extend_from_slice(&33u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1524 assert!(matches!(err, FormatError::InvalidData(_)));
1525 }
1526
1527 #[test]
1528 fn decode_points_truncated_coordinates() {
1529 let mut buf = vec![0x01, 0, 0, 0];
1531 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());
1536 let err = Selection::decode(&buf).unwrap_err();
1537 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1538 }
1539
1540 #[test]
1545 fn decode_points_huge_num_points_claim_does_not_allocate() {
1546 let mut buf = vec![0x01, 0, 0, 0];
1547 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();
1552 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1553 }
1554
1555 #[test]
1559 fn decode_points_num_points_overflow_does_not_allocate() {
1560 let mut buf = vec![0x01, 0, 0, 0];
1561 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();
1566 assert!(matches!(err, FormatError::InvalidData(_)));
1567 }
1568
1569 #[test]
1570 fn to_boxes_points_each_point_is_its_own_unit_box() {
1571 let sel = Selection::Points(PointSelection {
1572 rank: 2,
1573 points: vec![vec![1, 2], vec![5, 5]],
1574 });
1575 let boxes = sel.to_boxes(&[8, 8]).unwrap();
1576 assert_eq!(
1577 boxes,
1578 vec![(vec![1, 2], vec![1, 1]), (vec![5, 5], vec![1, 1])]
1579 );
1580 }
1581
1582 #[test]
1583 fn to_boxes_points_rejects_rank_mismatch() {
1584 let sel = Selection::Points(PointSelection {
1585 rank: 2,
1586 points: vec![],
1587 });
1588 let err = sel.to_boxes(&[8]).unwrap_err();
1589 assert!(matches!(err, FormatError::InvalidData(_)));
1590 }
1591
1592 #[test]
1593 fn decode_unknown_type_is_invalid() {
1594 let buf = [0x09, 0, 0, 0];
1595 let err = Selection::decode(&buf).unwrap_err();
1596 assert!(matches!(err, FormatError::InvalidData(_)));
1597 }
1598
1599 #[test]
1600 fn decode_bad_all_version() {
1601 let buf = [0x03, 0, 0, 0, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1602 let err = Selection::decode(&buf).unwrap_err();
1603 assert!(matches!(err, FormatError::InvalidData(_)));
1604 }
1605
1606 #[test]
1607 fn decode_bad_hyperslab_version() {
1608 let buf = [0x02, 0, 0, 0, 0x04, 0, 0, 0];
1609 let err = Selection::decode(&buf).unwrap_err();
1610 assert!(matches!(err, FormatError::InvalidData(_)));
1611 }
1612
1613 #[test]
1614 fn decode_hyperslab_rejects_unknown_flag_bits() {
1615 let mut buf = vec![0x02, 0, 0, 0];
1616 buf.extend_from_slice(&2u32.to_le_bytes());
1617 buf.push(0x02); buf.extend_from_slice(&[0u8; 4]);
1619 buf.extend_from_slice(&1u32.to_le_bytes());
1620 let err = Selection::decode(&buf).unwrap_err();
1621 assert!(matches!(err, FormatError::InvalidData(_)));
1622 }
1623
1624 #[test]
1625 fn decode_hyperslab_rejects_zero_rank() {
1626 let mut buf = vec![0x02, 0, 0, 0];
1627 buf.extend_from_slice(&1u32.to_le_bytes());
1628 buf.extend_from_slice(&[0u8; 8]);
1629 buf.extend_from_slice(&0u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1631 assert!(matches!(err, FormatError::InvalidData(_)));
1632 }
1633
1634 #[test]
1635 fn decode_hyperslab_rejects_rank_over_max() {
1636 let mut buf = vec![0x02, 0, 0, 0];
1637 buf.extend_from_slice(&1u32.to_le_bytes());
1638 buf.extend_from_slice(&[0u8; 8]);
1639 buf.extend_from_slice(&33u32.to_le_bytes()); let err = Selection::decode(&buf).unwrap_err();
1641 assert!(matches!(err, FormatError::InvalidData(_)));
1642 }
1643
1644 #[test]
1645 fn decode_truncated_header() {
1646 let buf = [0x03, 0, 0];
1647 let err = Selection::decode(&buf).unwrap_err();
1648 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1649 }
1650
1651 #[test]
1652 fn decode_truncated_hyperslab_blocks() {
1653 let mut buf = vec![0x02, 0, 0, 0];
1655 buf.extend_from_slice(&1u32.to_le_bytes());
1656 buf.extend_from_slice(&[0u8; 8]);
1657 buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&5u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes());
1660 buf.extend_from_slice(&1u32.to_le_bytes());
1661 let err = Selection::decode(&buf).unwrap_err();
1662 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1663 }
1664
1665 #[test]
1670 fn decode_huge_num_blocks_claim_does_not_allocate() {
1671 let mut buf = vec![0x02, 0, 0, 0];
1672 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();
1678 assert!(matches!(err, FormatError::BufferTooShort { .. }));
1679 }
1680
1681 #[test]
1687 fn bounds_cover_every_block_and_point() {
1688 let hyper = Selection::Hyperslab {
1689 rank: 2,
1690 form: Hyperslab::Blocks(vec![
1691 HyperslabBlock {
1692 start: vec![4, 1],
1693 end: vec![5, 3],
1694 },
1695 HyperslabBlock {
1696 start: vec![0, 6],
1697 end: vec![1, 7],
1698 },
1699 ]),
1700 };
1701 assert_eq!(hyper.bounds(), Some((vec![0, 1], vec![5, 7])));
1702 let points = Selection::Points(PointSelection {
1703 rank: 2,
1704 points: vec![vec![3, 9], vec![7, 2]],
1705 });
1706 assert_eq!(points.bounds(), Some((vec![3, 2], vec![7, 9])));
1707 assert_eq!(Selection::All.bounds(), None);
1708 assert_eq!(Selection::None.bounds(), None);
1709 }
1710
1711 #[test]
1715 fn bounds_of_a_regular_hyperslab_cover_its_expansion() {
1716 let mut buf = Vec::new();
1717 buf.extend_from_slice(&SEL_HYPERSLABS.to_le_bytes());
1718 buf.extend_from_slice(&3u32.to_le_bytes());
1719 buf.push(HYPER_REGULAR_FLAG);
1720 buf.push(4); buf.extend_from_slice(&1u32.to_le_bytes()); for v in [2u32, 5, 3, 2] {
1723 buf.extend_from_slice(&v.to_le_bytes());
1725 }
1726 let (selection, consumed) = Selection::decode(&buf).unwrap();
1727 assert_eq!(consumed, buf.len());
1728 assert_eq!(selection.bounds(), Some((vec![2], vec![13])));
1730 }
1731
1732 #[test]
1735 fn bounds_of_an_unlimited_regular_hyperslab_are_absent() {
1736 let selection = Selection::Hyperslab {
1737 rank: 1,
1738 form: Hyperslab::Regular(RegularHyperslab {
1739 start: vec![0],
1740 stride: vec![1],
1741 count: vec![UNLIMITED],
1742 block: vec![1],
1743 }),
1744 };
1745 assert_eq!(selection.bounds(), None);
1746 }
1747
1748 #[test]
1749 fn to_boxes_all_covers_the_full_extent() {
1750 let boxes = Selection::All.to_boxes(&[3, 5]).unwrap();
1751 assert_eq!(boxes, vec![(vec![0, 0], vec![3, 5])]);
1752 }
1753
1754 #[test]
1755 fn to_boxes_none_is_empty() {
1756 assert_eq!(Selection::None.to_boxes(&[3, 5]).unwrap(), vec![]);
1757 }
1758
1759 #[test]
1760 fn to_boxes_single_block_matches_h5py_fixture() {
1761 let sel = Selection::Hyperslab {
1763 rank: 1,
1764 form: Hyperslab::Blocks(vec![HyperslabBlock {
1765 start: vec![4],
1766 end: vec![11],
1767 }]),
1768 };
1769 let boxes = sel.to_boxes(&[20]).unwrap();
1770 assert_eq!(boxes, vec![(vec![4], vec![8])]);
1771 }
1772
1773 #[test]
1774 fn to_boxes_multi_block_2d() {
1775 let sel = Selection::Hyperslab {
1776 rank: 2,
1777 form: Hyperslab::Blocks(vec![
1778 HyperslabBlock {
1779 start: vec![0, 0],
1780 end: vec![1, 1],
1781 },
1782 HyperslabBlock {
1783 start: vec![2, 2],
1784 end: vec![3, 3],
1785 },
1786 ]),
1787 };
1788 let boxes = sel.to_boxes(&[4, 4]).unwrap();
1789 assert_eq!(
1790 boxes,
1791 vec![(vec![0, 0], vec![2, 2]), (vec![2, 2], vec![2, 2])]
1792 );
1793 }
1794
1795 #[test]
1801 fn resolve_orders_runs_by_element_not_by_box() {
1802 let sel = Selection::Hyperslab {
1803 rank: 2,
1804 form: Hyperslab::Regular(RegularHyperslab {
1805 start: vec![0, 0],
1806 stride: vec![2, 2],
1807 count: vec![2, 2],
1808 block: vec![2, 2],
1809 }),
1810 };
1811 let resolved = sel.resolve(&[4, 4]).unwrap();
1812 assert_eq!(resolved.n_elements(), 16);
1813 let mut covered = Vec::new();
1815 for r in &resolved.runs {
1816 for k in 0..r.len {
1817 covered.push(r.offset_in_extent + k);
1818 }
1819 }
1820 assert_eq!(covered, (0..16).collect::<Vec<u64>>());
1821 assert_eq!(resolved.runs[0].box_index, 0);
1824 assert_eq!(resolved.runs[1].box_index, 1);
1825 assert_eq!(resolved.runs[0].len, 2);
1826 assert_eq!(resolved.runs[2].box_index, 0);
1828 assert_eq!(resolved.runs[2].offset_in_box, 2);
1829 }
1830
1831 #[test]
1835 fn resolve_coalesces_a_box_that_fills_its_extent() {
1836 let resolved = Selection::All.resolve(&[3, 4]).unwrap();
1837 assert_eq!(resolved.runs.len(), 1);
1838 assert_eq!(resolved.runs[0].len, 12);
1839 assert_eq!(resolved.n_elements(), 12);
1840 }
1841
1842 #[test]
1845 fn resolve_keeps_one_run_per_row_of_a_partial_box() {
1846 let sel = Selection::Hyperslab {
1847 rank: 2,
1848 form: Hyperslab::Blocks(vec![HyperslabBlock {
1849 start: vec![1, 1],
1850 end: vec![2, 2],
1851 }]),
1852 };
1853 let resolved = sel.resolve(&[4, 4]).unwrap();
1854 assert_eq!(resolved.runs.len(), 2);
1855 assert_eq!(resolved.runs[0].offset_in_extent, 5);
1856 assert_eq!(resolved.runs[0].offset_in_box, 0);
1857 assert_eq!(resolved.runs[1].offset_in_extent, 9);
1858 assert_eq!(resolved.runs[1].offset_in_box, 2);
1859 }
1860
1861 #[test]
1865 fn resolve_keeps_point_selection_order() {
1866 let sel = Selection::Points(PointSelection {
1867 rank: 2,
1868 points: vec![vec![2, 3], vec![0, 1], vec![1, 0]],
1869 });
1870 let resolved = sel.resolve(&[4, 4]).unwrap();
1871 assert_eq!(
1872 resolved
1873 .runs
1874 .iter()
1875 .map(|r| r.offset_in_extent)
1876 .collect::<Vec<u64>>(),
1877 vec![11, 1, 4]
1878 );
1879 assert!(resolved.runs.iter().all(|r| r.len == 1));
1880 }
1881
1882 #[test]
1886 fn resolve_rejects_a_box_past_the_extent() {
1887 let sel = Selection::Hyperslab {
1888 rank: 1,
1889 form: Hyperslab::Blocks(vec![HyperslabBlock {
1890 start: vec![2],
1891 end: vec![5],
1892 }]),
1893 };
1894 let err = sel.resolve(&[4]).unwrap_err();
1895 assert!(
1896 format!("{err}").contains("past the extent"),
1897 "unexpected error: {err}"
1898 );
1899 }
1900
1901 #[test]
1903 fn resolve_of_a_scalar_extent_holds_one_element() {
1904 let all = Selection::All.resolve(&[]).unwrap();
1905 assert_eq!(all.n_elements(), 1);
1906 assert_eq!(all.runs[0].len, 1);
1907 assert_eq!(Selection::None.resolve(&[]).unwrap().n_elements(), 0);
1908 }
1909
1910 #[test]
1911 fn to_boxes_rejects_rank_mismatch() {
1912 let sel = Selection::Hyperslab {
1913 rank: 2,
1914 form: Hyperslab::Blocks(vec![]),
1915 };
1916 let err = sel.to_boxes(&[4]).unwrap_err();
1917 assert!(matches!(err, FormatError::InvalidData(_)));
1918 }
1919
1920 #[test]
1921 fn to_boxes_rejects_inverted_block() {
1922 let sel = Selection::Hyperslab {
1923 rank: 1,
1924 form: Hyperslab::Blocks(vec![HyperslabBlock {
1925 start: vec![5],
1926 end: vec![2],
1927 }]),
1928 };
1929 let err = sel.to_boxes(&[10]).unwrap_err();
1930 assert!(matches!(err, FormatError::InvalidData(_)));
1931 }
1932
1933 #[test]
1937 fn to_boxes_regular_expands_2d_grid() {
1938 let sel = Selection::Hyperslab {
1939 rank: 2,
1940 form: Hyperslab::Regular(RegularHyperslab {
1941 start: vec![0, 0],
1942 stride: vec![4, 4],
1943 count: vec![2, 2],
1944 block: vec![2, 2],
1945 }),
1946 };
1947 let boxes = sel.to_boxes(&[8, 8]).unwrap();
1948 assert_eq!(
1949 boxes,
1950 vec![
1951 (vec![0, 0], vec![2, 2]),
1952 (vec![0, 4], vec![2, 2]),
1953 (vec![4, 0], vec![2, 2]),
1954 (vec![4, 4], vec![2, 2]),
1955 ]
1956 );
1957 }
1958
1959 #[test]
1960 fn to_boxes_regular_single_block_matches_block_list_shape() {
1961 let sel = Selection::Hyperslab {
1964 rank: 1,
1965 form: Hyperslab::Regular(RegularHyperslab {
1966 start: vec![4],
1967 stride: vec![1],
1968 count: vec![1],
1969 block: vec![8],
1970 }),
1971 };
1972 assert_eq!(sel.to_boxes(&[20]).unwrap(), vec![(vec![4], vec![8])]);
1973 }
1974
1975 #[test]
1976 fn to_boxes_regular_zero_count_is_empty() {
1977 let sel = Selection::Hyperslab {
1978 rank: 1,
1979 form: Hyperslab::Regular(RegularHyperslab {
1980 start: vec![0],
1981 stride: vec![1],
1982 count: vec![0],
1983 block: vec![1],
1984 }),
1985 };
1986 assert_eq!(sel.to_boxes(&[10]).unwrap(), vec![]);
1987 }
1988
1989 #[test]
1990 fn to_boxes_regular_rejects_unlimited_count() {
1991 let sel = Selection::Hyperslab {
1992 rank: 1,
1993 form: Hyperslab::Regular(RegularHyperslab {
1994 start: vec![0],
1995 stride: vec![1],
1996 count: vec![UNLIMITED],
1997 block: vec![1],
1998 }),
1999 };
2000 let err = sel.to_boxes(&[10]).unwrap_err();
2001 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2002 }
2003
2004 #[test]
2005 fn to_boxes_regular_rejects_unlimited_block() {
2006 let sel = Selection::Hyperslab {
2007 rank: 1,
2008 form: Hyperslab::Regular(RegularHyperslab {
2009 start: vec![0],
2010 stride: vec![1],
2011 count: vec![1],
2012 block: vec![UNLIMITED],
2013 }),
2014 };
2015 let err = sel.to_boxes(&[10]).unwrap_err();
2016 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2017 }
2018
2019 #[test]
2022 fn to_boxes_regular_rejects_huge_box_count() {
2023 let sel = Selection::Hyperslab {
2024 rank: 2,
2025 form: Hyperslab::Regular(RegularHyperslab {
2026 start: vec![0, 0],
2027 stride: vec![1, 1],
2028 count: vec![1 << 30, 1 << 30],
2029 block: vec![1, 1],
2030 }),
2031 };
2032 let err = sel.to_boxes(&[u64::MAX, u64::MAX]).unwrap_err();
2033 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2034 }
2035
2036 #[test]
2050 fn selection_matches_libhdf5_image() {
2051 let cases: Vec<(&[u8], Selection, Selection)> = vec![
2052 (
2053 include_bytes!("../../tests/fixtures/all_v1.bin"),
2054 Selection::All,
2055 Selection::All,
2056 ),
2057 (
2058 include_bytes!("../../tests/fixtures/none_v1.bin"),
2059 Selection::None,
2060 Selection::None,
2061 ),
2062 (
2063 include_bytes!("../../tests/fixtures/hyperslab_single_block_v1.bin"),
2064 Selection::Hyperslab {
2065 rank: 1,
2066 form: Hyperslab::Blocks(vec![HyperslabBlock {
2067 start: vec![4],
2068 end: vec![11],
2069 }]),
2070 },
2071 Selection::Hyperslab {
2072 rank: 1,
2073 form: Hyperslab::Blocks(vec![HyperslabBlock {
2074 start: vec![4],
2075 end: vec![11],
2076 }]),
2077 },
2078 ),
2079 (
2080 include_bytes!("../../tests/fixtures/hyperslab_regular_3blocks_v1.bin"),
2081 Selection::Hyperslab {
2087 rank: 1,
2088 form: Hyperslab::Regular(RegularHyperslab {
2089 start: vec![0],
2090 stride: vec![5],
2091 count: vec![3],
2092 block: vec![2],
2093 }),
2094 },
2095 Selection::Hyperslab {
2096 rank: 1,
2097 form: Hyperslab::Blocks(vec![
2098 HyperslabBlock {
2099 start: vec![0],
2100 end: vec![1],
2101 },
2102 HyperslabBlock {
2103 start: vec![5],
2104 end: vec![6],
2105 },
2106 HyperslabBlock {
2107 start: vec![10],
2108 end: vec![11],
2109 },
2110 ]),
2111 },
2112 ),
2113 (
2114 include_bytes!("../../tests/fixtures/hyperslab_2d_regular_v1.bin"),
2115 Selection::Hyperslab {
2116 rank: 2,
2117 form: Hyperslab::Regular(RegularHyperslab {
2118 start: vec![0, 0],
2119 stride: vec![4, 4],
2120 count: vec![2, 2],
2121 block: vec![2, 2],
2122 }),
2123 },
2124 Selection::Hyperslab {
2125 rank: 2,
2126 form: Hyperslab::Blocks(vec![
2127 HyperslabBlock {
2128 start: vec![0, 0],
2129 end: vec![1, 1],
2130 },
2131 HyperslabBlock {
2132 start: vec![0, 4],
2133 end: vec![1, 5],
2134 },
2135 HyperslabBlock {
2136 start: vec![4, 0],
2137 end: vec![5, 1],
2138 },
2139 HyperslabBlock {
2140 start: vec![4, 4],
2141 end: vec![5, 5],
2142 },
2143 ]),
2144 },
2145 ),
2146 (
2147 include_bytes!("../../tests/fixtures/points4_v1.bin"),
2148 Selection::Points(PointSelection {
2149 rank: 1,
2150 points: vec![vec![1], vec![3], vec![7], vec![15]],
2151 }),
2152 Selection::Points(PointSelection {
2153 rank: 1,
2154 points: vec![vec![1], vec![3], vec![7], vec![15]],
2155 }),
2156 ),
2157 ];
2158
2159 for (blob, sel, want_decoded) in cases {
2160 let expected = strip_h5sencode_envelope(blob);
2161 let encoded = sel.encode().unwrap();
2162 assert_eq!(
2163 encoded, expected,
2164 "encode() mismatch for {sel:?}: got {encoded:02x?}, want {expected:02x?}"
2165 );
2166
2167 let (decoded, consumed) = Selection::decode(expected).unwrap();
2171 assert_eq!(consumed, expected.len());
2172 assert_eq!(decoded, want_decoded);
2173 }
2174 }
2175
2176 #[test]
2181 fn encode_decode_round_trips() {
2182 let values = vec![
2183 Selection::None,
2184 Selection::All,
2185 Selection::Points(PointSelection {
2186 rank: 2,
2187 points: vec![vec![0, 0], vec![3, 5], vec![9, 1]],
2188 }),
2189 Selection::Hyperslab {
2190 rank: 2,
2191 form: Hyperslab::Blocks(vec![
2192 HyperslabBlock {
2193 start: vec![0, 0],
2194 end: vec![1, 1],
2195 },
2196 HyperslabBlock {
2197 start: vec![4, 4],
2198 end: vec![5, 6],
2199 },
2200 ]),
2201 },
2202 ];
2203 for sel in values {
2204 let encoded = sel.encode().unwrap();
2205 let (decoded, consumed) = Selection::decode(&encoded).unwrap();
2206 assert_eq!(consumed, encoded.len());
2207 assert_eq!(decoded, sel);
2208 }
2209 }
2210
2211 #[test]
2216 fn encode_decode_round_trips_regular_hyperslab_to_its_block_list() {
2217 let sel = Selection::Hyperslab {
2218 rank: 2,
2219 form: Hyperslab::Regular(RegularHyperslab {
2220 start: vec![1, 2],
2221 stride: vec![3, 3],
2222 count: vec![2, 3],
2223 block: vec![1, 2],
2224 }),
2225 };
2226 let encoded = sel.encode().unwrap();
2227 let (decoded, consumed) = Selection::decode(&encoded).unwrap();
2228 assert_eq!(consumed, encoded.len());
2229 let Selection::Hyperslab {
2230 form: Hyperslab::Blocks(blocks),
2231 ..
2232 } = decoded
2233 else {
2234 panic!("expected a decoded block list");
2235 };
2236 assert_eq!(blocks.len(), 6);
2239 assert_eq!(blocks[0].start, vec![1, 2]);
2240 assert_eq!(blocks[0].end, vec![1, 3]);
2241 assert_eq!(blocks[5].start, vec![4, 8]);
2242 assert_eq!(blocks[5].end, vec![4, 9]);
2243 }
2244
2245 #[test]
2246 fn encode_points_rejects_zero_rank() {
2247 let sel = Selection::Points(PointSelection {
2248 rank: 0,
2249 points: vec![],
2250 });
2251 let err = sel.encode().unwrap_err();
2252 assert!(matches!(err, FormatError::InvalidData(_)));
2253 }
2254
2255 #[test]
2256 fn encode_points_rejects_coordinate_length_mismatch() {
2257 let sel = Selection::Points(PointSelection {
2258 rank: 2,
2259 points: vec![vec![1, 2, 3]],
2260 });
2261 let err = sel.encode().unwrap_err();
2262 assert!(matches!(err, FormatError::InvalidData(_)));
2263 }
2264
2265 #[test]
2266 fn encode_points_rejects_coordinate_over_u32() {
2267 let sel = Selection::Points(PointSelection {
2268 rank: 1,
2269 points: vec![vec![1u64 << 40]],
2270 });
2271 let err = sel.encode().unwrap_err();
2272 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2273 }
2274
2275 #[test]
2276 fn encode_hyperslab_rejects_zero_rank() {
2277 let sel = Selection::Hyperslab {
2278 rank: 0,
2279 form: Hyperslab::Blocks(vec![]),
2280 };
2281 let err = sel.encode().unwrap_err();
2282 assert!(matches!(err, FormatError::InvalidData(_)));
2283 }
2284
2285 #[test]
2286 fn encode_hyperslab_rejects_block_length_mismatch() {
2287 let sel = Selection::Hyperslab {
2288 rank: 2,
2289 form: Hyperslab::Blocks(vec![HyperslabBlock {
2290 start: vec![0],
2291 end: vec![1],
2292 }]),
2293 };
2294 let err = sel.encode().unwrap_err();
2295 assert!(matches!(err, FormatError::InvalidData(_)));
2296 }
2297
2298 #[test]
2299 fn encode_hyperslab_rejects_regular_field_length_mismatch() {
2300 let sel = Selection::Hyperslab {
2301 rank: 2,
2302 form: Hyperslab::Regular(RegularHyperslab {
2303 start: vec![0],
2304 stride: vec![1],
2305 count: vec![1],
2306 block: vec![1],
2307 }),
2308 };
2309 let err = sel.encode().unwrap_err();
2310 assert!(matches!(err, FormatError::InvalidData(_)));
2311 }
2312
2313 #[test]
2314 fn encode_hyperslab_rejects_block_coordinate_over_u32() {
2315 let sel = Selection::Hyperslab {
2316 rank: 1,
2317 form: Hyperslab::Blocks(vec![HyperslabBlock {
2318 start: vec![0],
2319 end: vec![1u64 << 40],
2320 }]),
2321 };
2322 let err = sel.encode().unwrap_err();
2323 assert!(matches!(err, FormatError::UnsupportedFeature(_)));
2324 }
2325
2326 const LIBHDF5_UNLIMITED_HYPERSLAB: &[u8] = &[
2335 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, ];
2351
2352 fn unlimited_rows() -> Selection {
2353 Selection::Hyperslab {
2354 rank: 2,
2355 form: Hyperslab::Regular(RegularHyperslab {
2356 start: vec![0, 0],
2357 stride: vec![1, 1],
2358 count: vec![UNLIMITED, 1],
2359 block: vec![1, 2],
2360 }),
2361 }
2362 }
2363
2364 #[test]
2368 fn unlimited_hyperslab_encodes_as_the_libhdf5_version_2_image() {
2369 assert_eq!(
2370 unlimited_rows().encode().unwrap(),
2371 LIBHDF5_UNLIMITED_HYPERSLAB
2372 );
2373 }
2374
2375 #[test]
2378 fn unlimited_hyperslab_decodes_from_the_libhdf5_image() {
2379 let (sel, used) = Selection::decode(LIBHDF5_UNLIMITED_HYPERSLAB).unwrap();
2380 assert_eq!(used, LIBHDF5_UNLIMITED_HYPERSLAB.len());
2381 assert_eq!(sel, unlimited_rows());
2382 assert_eq!(sel.unlim_dim(), Some(0));
2383 }
2384
2385 #[test]
2388 fn an_unlimited_selection_has_no_bounds() {
2389 assert_eq!(unlimited_rows().bounds(), None);
2390 }
2391
2392 #[test]
2395 fn clip_unlimited_cuts_the_unlimited_dimension_to_the_extent() {
2396 let clipped = unlimited_rows().clip_unlimited(3).unwrap();
2397 assert_eq!(
2398 clipped.to_boxes(&[3, 2]).unwrap(),
2402 vec![(vec![0, 0], vec![3, 2])]
2403 );
2404 assert_eq!(unlimited_rows().clip_unlimited(0).unwrap(), Selection::None);
2407 assert_eq!(Selection::All.clip_unlimited(7).unwrap(), Selection::All);
2409 }
2410
2411 #[test]
2415 fn clip_unlimited_truncates_the_block_the_extent_cuts_through() {
2416 let sel = Selection::Hyperslab {
2417 rank: 1,
2418 form: Hyperslab::Regular(RegularHyperslab {
2419 start: vec![1],
2420 stride: vec![4],
2421 count: vec![UNLIMITED],
2422 block: vec![3],
2423 }),
2424 };
2425 assert_eq!(
2427 sel.clip_unlimited(7).unwrap().to_boxes(&[7]).unwrap(),
2428 vec![(vec![1], vec![3]), (vec![5], vec![2])]
2429 );
2430 }
2431
2432 #[test]
2436 fn clip_extent_matches_the_slices_the_source_supplies() {
2437 let Selection::Hyperslab {
2438 form: Hyperslab::Regular(rows),
2439 ..
2440 } = unlimited_rows()
2441 else {
2442 unreachable!()
2443 };
2444 for extent in [0u64, 1, 6, 10] {
2446 assert_eq!(rows.num_slices(extent), extent);
2447 assert_eq!(rows.clip_extent(extent, false), extent);
2448 }
2449
2450 let strided = RegularHyperslab {
2453 start: vec![0],
2454 stride: vec![4],
2455 count: vec![UNLIMITED],
2456 block: vec![3],
2457 };
2458 assert_eq!(strided.num_slices(10), 8);
2459 assert_eq!(strided.clip_extent(8, false), 10);
2460 assert_eq!(strided.clip_extent(6, false), 7);
2463 assert_eq!(strided.clip_extent(6, true), 8);
2464 assert_eq!(strided.clip_extent(0, false), 0);
2465 }
2466
2467 #[test]
2471 fn unlimited_slice_shape_and_block_extraction() {
2472 let Selection::Hyperslab {
2473 form: Hyperslab::Regular(rows),
2474 ..
2475 } = unlimited_rows()
2476 else {
2477 unreachable!()
2478 };
2479 assert_eq!(rows.num_elem_non_unlim(), Some(2));
2480 let third = rows.unlim_block(3);
2481 assert_eq!(third.start, vec![3, 0]);
2482 assert_eq!(third.count, vec![1, 1]);
2483 assert_eq!(third.block, vec![1, 2]);
2484 assert_eq!(third.unlim_dim(), None);
2485 }
2486}