omf/data/
read_checks.rs

1use std::collections::HashSet;
2
3use crate::{
4    SubblockMode,
5    array::Constraint,
6    error::{Error, InvalidData},
7    file::{ReadAt, SubFile},
8    pqarray::read::{MultiIter, NullableGroupIter, NullableIter, SimpleIter},
9};
10
11use super::{
12    FloatType, NumberType,
13    write_checks::{subblock_is_octree_compat, valid_subblock_sizes},
14};
15
16/// Iterator for reading scalar data, supporting `f32` and `f64` types generically.
17///
18/// When used for tensor block model sizes this checks that all sizes are greater than zero.
19#[derive(Debug)]
20pub struct GenericScalars<T: FloatType, R: ReadAt> {
21    inner: SimpleIter<T, SubFile<R>>,
22    is_size: bool,
23}
24
25impl<T: FloatType, R: ReadAt> GenericScalars<T, R> {
26    pub(crate) fn new(inner: SimpleIter<T, SubFile<R>>, constraint: &Constraint) -> Self {
27        Self {
28            inner,
29            is_size: matches!(constraint, Constraint::Size),
30        }
31    }
32}
33
34impl<T: FloatType, R: ReadAt> Iterator for GenericScalars<T, R> {
35    type Item = Result<T, Error>;
36
37    fn next(&mut self) -> Option<Self::Item> {
38        let item = self.inner.next()?;
39        if self.is_size {
40            if let Ok(value) = item {
41                if value <= T::default() {
42                    return Some(Err(InvalidData::SizeZeroOrLess {
43                        value: value.into(),
44                    }
45                    .into()));
46                }
47            }
48        }
49        Some(item)
50    }
51}
52
53/// Iterator for reading nullable indices.
54///
55/// Checks that all indices are within range.
56#[derive(Debug)]
57pub struct Indices<R: ReadAt> {
58    inner: NullableIter<u32, SubFile<R>>,
59    category_count: u64,
60}
61
62impl<R: ReadAt> Indices<R> {
63    pub(crate) fn new(inner: NullableIter<u32, SubFile<R>>, constraint: &Constraint) -> Self {
64        let &Constraint::Index(category_count) = constraint else {
65            panic!("invalid constraint");
66        };
67        Self {
68            inner,
69            category_count,
70        }
71    }
72}
73
74impl<R: ReadAt> Iterator for Indices<R> {
75    type Item = Result<Option<u32>, Error>;
76
77    fn next(&mut self) -> Option<Self::Item> {
78        let item = self.inner.next()?;
79        if let Ok(Some(i)) = &item {
80            let index: u64 = (*i).into();
81            if index >= self.category_count {
82                return Some(Err(InvalidData::IndexOutOfRange {
83                    value: index,
84                    maximum: self.category_count.saturating_add(1),
85                }
86                .into()));
87            }
88        }
89        Some(item)
90    }
91}
92
93/// Iterator for reading segments or triangles.
94///
95/// Checks that all vertex indices are within range.
96#[derive(Debug)]
97pub struct GenericPrimitives<const N: usize, R: ReadAt> {
98    inner: MultiIter<u32, SubFile<R>, N>,
99    vertex_count: u64,
100}
101
102impl<const N: usize, R: ReadAt> GenericPrimitives<N, R> {
103    pub(crate) fn new(inner: MultiIter<u32, SubFile<R>, N>, constraint: &Constraint) -> Self {
104        let vertex_count = match constraint {
105            Constraint::Segment(n) | Constraint::Triangle(n) => *n,
106            _ => panic!("invalid constraint"),
107        };
108        Self {
109            inner,
110            vertex_count,
111        }
112    }
113}
114
115impl<const N: usize, R: ReadAt> Iterator for GenericPrimitives<N, R> {
116    type Item = Result<[u32; N], Error>;
117
118    fn next(&mut self) -> Option<Self::Item> {
119        let item = self.inner.next()?;
120        if let Ok(value) = &item {
121            for i in value {
122                let index: u64 = (*i).into();
123                if index >= self.vertex_count {
124                    return Some(Err(InvalidData::IndexOutOfRange {
125                        value: index,
126                        maximum: self.vertex_count.saturating_add(1),
127                    }
128                    .into()));
129                }
130            }
131        }
132        Some(item)
133    }
134}
135
136/// Iterator for reading boundary values.
137///
138/// Checks that the values are increasing.
139#[derive(Debug)]
140pub(super) struct BoundaryValues<T: NumberType, R: ReadAt> {
141    inner: SimpleIter<T, SubFile<R>>,
142    previous: Option<T>,
143}
144
145impl<T: NumberType, R: ReadAt> BoundaryValues<T, R> {
146    pub(crate) fn new(inner: SimpleIter<T, SubFile<R>>) -> Self {
147        Self {
148            inner,
149            previous: None,
150        }
151    }
152}
153
154impl<T: NumberType, R: ReadAt> Iterator for BoundaryValues<T, R> {
155    type Item = Result<T, Error>;
156
157    fn next(&mut self) -> Option<Self::Item> {
158        let item = self.inner.next()?;
159        if let (Ok(v), Some(p)) = (&item, &self.previous) {
160            if v < p {
161                return Some(Err(InvalidData::BoundaryDecreases.into()));
162            }
163        }
164        Some(item)
165    }
166}
167
168/// Iterator for reading free-form sub-blocks, supporting `f32` or `f64` generically.
169///
170/// Checks that the parent index and corners are all valid.
171#[derive(Debug)]
172pub struct GenericFreeformSubblocks<T: FloatType, R: ReadAt> {
173    parents: MultiIter<u32, SubFile<R>, 3>,
174    corners: MultiIter<T, SubFile<R>, 6>,
175    block_count: [u32; 3],
176}
177
178impl<T: FloatType, R: ReadAt> GenericFreeformSubblocks<T, R> {
179    pub(crate) fn new(
180        parents: MultiIter<u32, SubFile<R>, 3>,
181        corners: MultiIter<T, SubFile<R>, 6>,
182        constraint: &Constraint,
183    ) -> Self {
184        let block_count = match constraint {
185            &Constraint::RegularSubblock { block_count, .. }
186            | &Constraint::FreeformSubblock { block_count } => block_count,
187            _ => panic!("invalid constraint"),
188        };
189        Self {
190            parents,
191            corners,
192            block_count,
193        }
194    }
195}
196
197impl<T: FloatType, R: ReadAt> Iterator for GenericFreeformSubblocks<T, R> {
198    type Item = Result<([u32; 3], [T; 6]), Error>;
199
200    fn next(&mut self) -> Option<Self::Item> {
201        match (self.parents.next(), self.corners.next()) {
202            (None, _) | (_, None) => None,
203            (Some(Err(e)), _) | (_, Some(Err(e))) => Some(Err(e)),
204            (Some(Ok(parent)), Some(Ok(corners))) => {
205                if let Err(e) = check_freeform_corners(corners) {
206                    Some(Err(e))
207                } else if let Err(e) = check_parent(self.block_count, parent) {
208                    Some(Err(e))
209                } else {
210                    Some(Ok((parent, corners)))
211                }
212            }
213        }
214    }
215}
216
217/// Iterator for reading regular sub-blocks.
218///
219/// Checks that the parent index and corners are all valid.
220#[derive(Debug)]
221pub struct RegularSubblocks<R: ReadAt> {
222    parents: MultiIter<u32, SubFile<R>, 3>,
223    corners: MultiIter<u32, SubFile<R>, 6>,
224    block_count: [u32; 3],
225    subblock_count: [u32; 3],
226    mode: Option<(SubblockMode, HashSet<[u32; 3]>)>,
227}
228
229impl<R: ReadAt> RegularSubblocks<R> {
230    pub(crate) fn new(
231        parents: MultiIter<u32, SubFile<R>, 3>,
232        corners: MultiIter<u32, SubFile<R>, 6>,
233        constraint: &Constraint,
234    ) -> Self {
235        let &Constraint::RegularSubblock {
236            block_count,
237            subblock_count,
238            mode,
239        } = constraint
240        else {
241            panic!("invalid constraint");
242        };
243        Self {
244            parents,
245            corners,
246            block_count,
247            subblock_count,
248            mode: mode.map(|m| (m, valid_subblock_sizes(m, subblock_count))),
249        }
250    }
251}
252
253impl<R: ReadAt> Iterator for RegularSubblocks<R> {
254    type Item = Result<([u32; 3], [u32; 6]), Error>;
255
256    fn next(&mut self) -> Option<Self::Item> {
257        match (self.parents.next(), self.corners.next()) {
258            (None, _) | (_, None) => None,
259            (Some(Err(e)), _) | (_, Some(Err(e))) => Some(Err(e)),
260            (Some(Ok(parent)), Some(Ok(corners))) => {
261                if let Err(e) = check_regular_corners(self.subblock_count, &self.mode, corners) {
262                    Some(Err(e))
263                } else if let Err(e) = check_parent(self.block_count, parent) {
264                    Some(Err(e))
265                } else {
266                    Some(Ok((parent, corners)))
267                }
268            }
269        }
270    }
271}
272
273#[inline]
274fn check_parent(block_count: [u32; 3], parent: [u32; 3]) -> Result<(), Error> {
275    for (count, index) in block_count.into_iter().zip(parent) {
276        if index >= count {
277            return Err(InvalidData::BlockIndexOutOfRange {
278                value: parent.map(Into::into),
279                maximum: block_count,
280            }
281            .into());
282        }
283    }
284    Ok(())
285}
286
287#[inline]
288fn check_regular_corners(
289    subblock_count: [u32; 3],
290    mode_and_sizes: &Option<(SubblockMode, HashSet<[u32; 3]>)>,
291    corners: [u32; 6],
292) -> Result<(), Error> {
293    let corners: [u32; 6] = corners.map(Into::into);
294    for i in 0..3 {
295        if corners[i] >= corners[i + 3] {
296            return Err(InvalidData::RegularSubblockZeroSize { corners }.into());
297        }
298        if corners[i + 3] > subblock_count[i] {
299            return Err(InvalidData::RegularSubblockOutsideParent {
300                corners,
301                maximum: subblock_count,
302            }
303            .into());
304        }
305    }
306    if let Some((mode, valid_sizes)) = &mode_and_sizes {
307        let size = std::array::from_fn(|i| corners[i + 3] - corners[i]);
308        if !valid_sizes.contains(&size) {
309            return Err(InvalidData::RegularSubblockNotInMode {
310                corners,
311                mode: *mode,
312            }
313            .into());
314        }
315        if *mode == SubblockMode::Octree && !subblock_is_octree_compat(&corners) {
316            return Err(InvalidData::RegularSubblockNotInMode {
317                corners,
318                mode: *mode,
319            }
320            .into());
321        }
322    }
323    Ok(())
324}
325
326#[inline]
327fn check_freeform_corners<T: FloatType>(corners: [T; 6]) -> Result<(), Error> {
328    for i in 0..3 {
329        if corners[i] < T::ZERO {
330            return Err(InvalidData::FreeformSubblockOutsideParent {
331                corners: corners.map(Into::into),
332            }
333            .into());
334        }
335        if corners[i + 3] > T::ONE {
336            return Err(InvalidData::FreeformSubblockOutsideParent {
337                corners: corners.map(Into::into),
338            }
339            .into());
340        }
341        if corners[i] >= corners[i + 3] {
342            return Err(InvalidData::FreeformSubblockZeroSize {
343                corners: corners.map(Into::into),
344            }
345            .into());
346        }
347    }
348    Ok(())
349}
350
351/// Iterator for reading numbers, supporting `f32`, `f64`, `i64`, date, and date-time generically.
352#[derive(Debug)]
353pub struct GenericNumbers<T: NumberType, R: ReadAt>(pub(crate) NullableIter<T, SubFile<R>>);
354
355impl<T: NumberType, R: ReadAt> Iterator for GenericNumbers<T, R> {
356    type Item = Result<Option<T>, Error>;
357
358    fn next(&mut self) -> Option<Self::Item> {
359        self.0.next()
360    }
361}
362
363/// Iterator for reading small fixed-size arrays, like vertices and texture coordinates.
364#[derive(Debug)]
365pub struct GenericArrays<T: NumberType, const N: usize, R: ReadAt>(
366    pub(crate) MultiIter<T, SubFile<R>, N>,
367);
368
369impl<T: NumberType, const N: usize, R: ReadAt> Iterator for GenericArrays<T, N, R> {
370    type Item = Result<[T; N], Error>;
371
372    fn next(&mut self) -> Option<Self::Item> {
373        self.0.next()
374    }
375}
376
377/// Iterator for reading non-nullable colors, such as category legends.
378#[derive(Debug)]
379pub struct Gradient<R: ReadAt>(pub(crate) MultiIter<u8, SubFile<R>, 4>);
380
381impl<R: ReadAt> Iterator for Gradient<R> {
382    type Item = Result<[u8; 4], Error>;
383
384    fn next(&mut self) -> Option<Self::Item> {
385        self.0.next()
386    }
387}
388
389/// Iterator for reading small optional fixed-size arrays, like 2D and 3D vectors.
390#[derive(Debug)]
391pub struct GenericOptionalArrays<T: NumberType, const N: usize, R: ReadAt>(
392    pub(crate) NullableGroupIter<T, SubFile<R>, N>,
393);
394
395impl<T: NumberType, const N: usize, R: ReadAt> Iterator for GenericOptionalArrays<T, N, R> {
396    type Item = Result<Option<[T; N]>, Error>;
397
398    fn next(&mut self) -> Option<Self::Item> {
399        self.0.next()
400    }
401}
402
403/// Iterator for reading nullable colors.
404#[derive(Debug)]
405pub struct Colors<R: ReadAt>(pub(crate) NullableGroupIter<u8, SubFile<R>, 4>);
406
407impl<R: ReadAt> Iterator for Colors<R> {
408    type Item = Result<Option<[u8; 4]>, Error>;
409
410    fn next(&mut self) -> Option<Self::Item> {
411        self.0.next()
412    }
413}
414
415/// Iterator for reading nullable booleans.
416#[derive(Debug)]
417pub struct Booleans<R: ReadAt>(pub(crate) NullableIter<bool, SubFile<R>>);
418
419impl<R: ReadAt> Iterator for Booleans<R> {
420    type Item = Result<Option<bool>, Error>;
421
422    fn next(&mut self) -> Option<Self::Item> {
423        self.0.next()
424    }
425}
426
427/// Iterator for reading non-nullable strings, such as category names.
428#[derive(Debug)]
429pub struct Names<R: ReadAt>(pub(crate) SimpleIter<String, SubFile<R>>);
430
431impl<R: ReadAt> Iterator for Names<R> {
432    type Item = Result<String, Error>;
433
434    fn next(&mut self) -> Option<Self::Item> {
435        self.0.next()
436    }
437}
438
439/// Iterator for reading nullable strings.
440#[derive(Debug)]
441pub struct Text<R: ReadAt>(pub(crate) NullableIter<String, SubFile<R>>);
442
443impl<R: ReadAt> Iterator for Text<R> {
444    type Item = Result<Option<String>, Error>;
445
446    fn next(&mut self) -> Option<Self::Item> {
447        self.0.next()
448    }
449}