1#![deny(
27 clippy::unwrap_used,
28 clippy::expect_used,
29 clippy::indexing_slicing,
30 clippy::panic,
31 clippy::unreachable
32)]
33
34use std::io::{self, Read, Seek};
35
36use flate2::read::ZlibDecoder;
37
38const MAX_RAW: u64 = 256 * 1024 * 1024;
40const DATA_HEADER: usize = 18;
42const ZSTD_MAGIC: [u8; 4] = [0x28, 0xB5, 0x2F, 0xFD];
44const ZSTD_SLACK: usize = 64;
46const MAX_ZSTD_WINDOW: u64 = 32 * 1024 * 1024;
49const MAX_ZSTD_FRAMES: usize = 64;
51pub const ARRAY: u32 = 0x3000_0002;
53pub const ELEM_U32: u32 = 0;
55pub const ELEM_KEYS: u32 = 0x1000_0000;
57
58fn bad(what: &str) -> io::Error {
59 io::Error::new(io::ErrorKind::InvalidData, format!("Inventor file: {what}"))
60}
61
62pub(crate) fn u16_at(data: &[u8], at: usize) -> Option<u16> {
63 Some(u16::from_le_bytes(
64 data.get(at..at.checked_add(2)?)?.try_into().ok()?,
65 ))
66}
67
68pub(crate) fn u32_at(data: &[u8], at: usize) -> Option<u32> {
69 Some(u32::from_le_bytes(
70 data.get(at..at.checked_add(4)?)?.try_into().ok()?,
71 ))
72}
73
74pub(crate) fn f64_at(data: &[u8], at: usize) -> Option<f64> {
75 Some(f64::from_le_bytes(
76 data.get(at..at.checked_add(8)?)?.try_into().ok()?,
77 ))
78}
79
80fn usize_at(data: &[u8], at: usize) -> Option<usize> {
81 usize::try_from(u32_at(data, at)?).ok()
82}
83
84#[derive(Debug)]
86pub struct Segment {
87 lengths: Vec<usize>,
89 pub classes: Vec<u32>,
91 data: Vec<u8>,
92}
93
94#[derive(Clone, Copy, Debug)]
96pub struct Section<'a> {
97 pub index: usize,
99 pub class: u32,
101 pub payload: &'a [u8],
102}
103
104impl Segment {
105 pub fn sections(&self) -> Sections<'_> {
108 Sections {
109 segment: self,
110 index: 0,
111 at: 0,
112 }
113 }
114
115 pub fn element_size(&self, elem: u32) -> Option<usize> {
117 match elem {
118 ELEM_U32 | ELEM_KEYS => Some(4),
119 e if e & 0xFFFF_FF00 == 0x100 => {
120 let class = *self.classes.get(usize::try_from(e & 0xFF).ok()?)?;
121 match class {
122 POINT3F | VECTOR3F => Some(12),
123 POINT2F => Some(8),
124 _ => None,
125 }
126 }
127 _ => None,
128 }
129 }
130
131 pub fn element_class(&self, elem: u32) -> Option<u32> {
133 if elem & 0xFFFF_FF00 == 0x100 {
134 self.classes
135 .get(usize::try_from(elem & 0xFF).ok()?)
136 .copied()
137 } else {
138 Some(elem)
139 }
140 }
141}
142
143pub const POINT3F: u32 = 0xF272_10C1;
145pub const VECTOR3F: u32 = 0x8D9D_0B25;
147pub const POINT2F: u32 = 0x68FC_ABB1;
149
150pub struct Sections<'a> {
152 segment: &'a Segment,
153 index: usize,
154 at: usize,
155}
156
157impl<'a> Iterator for Sections<'a> {
158 type Item = io::Result<Section<'a>>;
159
160 fn next(&mut self) -> Option<Self::Item> {
161 let len = *self.segment.lengths.get(self.index)?;
162 let data = &self.segment.data;
163 let result = (|| {
164 let word = u32_at(data, self.at).ok_or_else(|| bad("section cut short"))?;
165 let start = self.at + 4;
166 let end = start
167 .checked_add(len)
168 .filter(|&e| e <= data.len())
169 .ok_or_else(|| bad("section cut short"))?;
170 if usize_at(data, end) != Some(len) {
171 return Err(bad("section length check failed"));
172 }
173 match data.get(end + 4) {
175 None | Some(0) => {}
176 Some(_) => return Err(bad("unsupported section trailer")),
177 }
178 let class = usize::try_from(word & 0xFF)
179 .ok()
180 .and_then(|k| self.segment.classes.get(k).copied())
181 .unwrap_or(0);
182 let section = Section {
183 index: self.index,
184 class,
185 payload: data.get(start..end).unwrap_or_default(),
186 };
187 self.at = end + 5;
188 Ok(section)
189 })();
190 self.index += 1;
191 if result.is_err() {
192 self.index = usize::MAX;
194 }
195 Some(result)
196 }
197}
198
199pub struct Cursor<'a> {
201 pub data: &'a [u8],
202 pub at: usize,
203}
204
205#[derive(Clone, Copy, Debug)]
207pub struct Array<'a> {
208 pub elem: u32,
209 pub count: usize,
210 pub bytes: &'a [u8],
211}
212
213impl<'a> Cursor<'a> {
214 pub fn new(data: &'a [u8], at: usize) -> Cursor<'a> {
215 Cursor { data, at }
216 }
217
218 pub fn u32(&mut self) -> Option<u32> {
219 let v = u32_at(self.data, self.at)?;
220 self.at += 4;
221 Some(v)
222 }
223
224 pub fn array(&mut self, segment: &Segment) -> Option<Array<'a>> {
227 if u32_at(self.data, self.at)? != ARRAY {
228 return None;
229 }
230 let count = usize_at(self.data, self.at + 4)?;
231 if count == 0 {
232 self.at += 8;
233 return Some(Array {
234 elem: ELEM_U32,
235 count: 0,
236 bytes: &[],
237 });
238 }
239 let elem = u32_at(self.data, self.at + 12)?;
240 let size = segment.element_size(elem)?;
241 let start = self.at + 16;
242 let end = start.checked_add(count.checked_mul(size)?)?;
243 let bytes = self.data.get(start..end)?;
244 self.at = end;
245 Some(Array { elem, count, bytes })
246 }
247}
248
249pub fn segment<F: Read + Seek>(
252 ole: &mut cfb::CompoundFile<F>,
253 name: &str,
254 limit: u64,
255) -> io::Result<Option<Segment>> {
256 let metas: Vec<(String, std::path::PathBuf)> = match ole.read_storage("/RSeStorage") {
257 Ok(entries) => entries
258 .filter(|e| e.is_stream() && e.name().starts_with('M') && e.len() <= MAX_RAW)
259 .map(|e| {
260 let id = e.name().get(1..).unwrap_or_default().to_string();
261 (id, e.path().to_path_buf())
262 })
263 .collect(),
264 Err(_) => return Ok(None),
265 };
266 for (id, path) in metas {
267 let raw = read_stream(ole, &path, MAX_RAW)?;
268 let Some((seg_name, body_at)) = meta_head(&raw) else {
269 continue;
270 };
271 if seg_name != name {
272 continue;
273 }
274 let body = unpack(&raw, body_at, limit)?;
275 let (lengths, classes) = meta_body(&body).ok_or_else(|| bad("damaged meta stream"))?;
276 let data_path = std::path::PathBuf::from(format!("/RSeStorage/B{id}"));
277 let raw = read_stream(ole, &data_path, MAX_RAW)?;
278 let data = unpack(&raw, DATA_HEADER, limit)?;
279 return Ok(Some(Segment {
280 lengths,
281 classes,
282 data,
283 }));
284 }
285 Ok(None)
286}
287
288pub fn read_stream<F: Read + Seek>(
290 ole: &mut cfb::CompoundFile<F>,
291 path: &std::path::Path,
292 limit: u64,
293) -> io::Result<Vec<u8>> {
294 let stream = ole.open_stream(path)?;
295 if stream.len() > limit {
296 return Err(bad("stream too large"));
297 }
298 let mut out = Vec::new();
299 stream.take(limit).read_to_end(&mut out)?;
300 Ok(out)
301}
302
303fn unpack(raw: &[u8], at: usize, limit: u64) -> io::Result<Vec<u8>> {
307 let body = raw.get(at..).unwrap_or_default();
308 if body.starts_with(&ZSTD_MAGIC) {
309 return unzstd(body, limit);
310 }
311 if !zlib_header(body) {
312 let from = at.saturating_sub(ZSTD_SLACK);
313 let to = at
314 .saturating_add(ZSTD_SLACK + ZSTD_MAGIC.len())
315 .min(raw.len());
316 if let Some(near) = raw.get(from..to).and_then(|w| {
317 w.windows(ZSTD_MAGIC.len())
318 .position(|m| m == ZSTD_MAGIC)
319 .and_then(|k| raw.get(from + k..))
320 }) {
321 return unzstd(near, limit);
322 }
323 }
324 inflate(body, limit)
325}
326
327fn zlib_header(data: &[u8]) -> bool {
330 match data {
331 [cmf, flg, ..] => {
332 cmf & 0x0F == 8 && cmf >> 4 <= 7 && (u16::from(*cmf) << 8 | u16::from(*flg)) % 31 == 0
333 }
334 _ => false,
335 }
336}
337
338fn inflate(data: &[u8], limit: u64) -> io::Result<Vec<u8>> {
339 let mut out = Vec::new();
340 ZlibDecoder::new(data)
341 .take(limit.saturating_add(1))
342 .read_to_end(&mut out)
343 .map_err(|_| bad("damaged compressed data"))?;
344 if out.len() as u64 > limit {
345 return Err(bad("segment larger than the preview reads"));
346 }
347 Ok(out)
348}
349
350fn unzstd(data: &[u8], limit: u64) -> io::Result<Vec<u8>> {
353 let mut out = Vec::new();
354 let mut rest = data;
355 for _ in 0..MAX_ZSTD_FRAMES {
356 if !rest.starts_with(&ZSTD_MAGIC) {
357 break;
358 }
359 let left = limit.saturating_add(1).saturating_sub(out.len() as u64);
360 let mut decoder = ruzstd::decoding::StreamingDecoder::new_with_max_window_size(
361 &mut rest,
362 MAX_ZSTD_WINDOW.min(limit.max(1 << 20)),
363 )
364 .map_err(|_| bad("damaged compressed data"))?;
365 (&mut decoder)
366 .take(left)
367 .read_to_end(&mut out)
368 .map_err(|_| bad("damaged compressed data"))?;
369 if out.len() as u64 > limit {
370 return Err(bad("segment larger than the preview reads"));
371 }
372 if !decoder.decoder.is_finished() {
373 break;
375 }
376 }
377 Ok(out)
378}
379
380fn meta_head(raw: &[u8]) -> Option<(String, usize)> {
382 const VERSION: &[u8] = b"RSe Meta Stream Version ";
383 let n = usize_at(raw, 0)?;
384 if !raw.get(4..4 + n)?.starts_with(VERSION) {
385 return None;
386 }
387 let mut at = 4 + n + 2 + 16;
389 let chars = usize_at(raw, at)?;
390 if chars > 256 {
391 return None;
392 }
393 at += 4;
394 let units: Vec<u16> = raw
395 .get(at..at + 2 * chars)?
396 .chunks_exact(2)
397 .filter_map(|c| c.try_into().ok().map(u16::from_le_bytes))
398 .collect();
399 let name = String::from_utf16(&units).ok()?;
400 at += 2 * chars + 16 + 12;
401 for _ in 0..2 {
402 let len = usize_at(raw, at)?;
403 if len > 256 {
404 return None;
405 }
406 at += 4 + len;
407 }
408 Some((name, at + 1))
409}
410
411fn meta_body(body: &[u8]) -> Option<(Vec<usize>, Vec<u32>)> {
413 let mut at = 14;
414 let n = usize_at(body, at)?;
415 at += 4;
416 if n.checked_mul(4)? > body.len() {
417 return None;
418 }
419 let mut lengths = Vec::with_capacity(n);
420 for k in 0..n {
421 let word = u32_at(body, at + 4 * k)?;
422 lengths.push(usize::try_from(word & 0x7FFF_FFFF).ok()?);
423 }
424 at += 4 * n + 4;
425 for size in [10usize, 28] {
426 let count = usize_at(body, at)?;
427 at = at.checked_add(4)?.checked_add(count.checked_mul(size)?)?;
428 at = at.checked_add(4)?;
429 if at > body.len() {
430 return None;
431 }
432 }
433 let count = usize_at(body, at)?;
434 at += 4;
435 if count > 256 || at.checked_add(count.checked_mul(28)?)? > body.len() {
436 return None;
437 }
438 let classes = (0..count)
439 .map(|k| u32_at(body, at + 28 * k))
440 .collect::<Option<Vec<u32>>>()?;
441 Some((lengths, classes))
442}
443
444#[cfg(test)]
445pub(crate) mod tests {
446 use super::*;
447
448 pub(crate) type SegmentSpec<'a> = (&'a str, &'a [u32], Vec<(u8, Vec<u8>)>);
450
451 pub(crate) fn file(segments: &[SegmentSpec], extra: &[(&str, Vec<u8>)]) -> Vec<u8> {
454 use std::io::Write;
455 let mut ole = cfb::CompoundFile::create(std::io::Cursor::new(Vec::new())).unwrap();
456 ole.create_storage("/RSeStorage").unwrap();
457 for (k, (name, classes, sections)) in segments.iter().enumerate() {
458 let mut meta = Vec::new();
459 let version = b"RSe Meta Stream Version 8";
460 meta.extend((version.len() as u32).to_le_bytes());
461 meta.extend(version);
462 meta.extend([8, 0]);
463 meta.extend([0; 16]);
464 let units: Vec<u16> = name.encode_utf16().collect();
465 meta.extend((units.len() as u32).to_le_bytes());
466 for u in units {
467 meta.extend(u.to_le_bytes());
468 }
469 meta.extend([0; 16 + 12]);
470 for _ in 0..2 {
471 meta.extend(19u32.to_le_bytes());
472 meta.extend(b"01/01/2026 00:00:00");
473 }
474 meta.push(1);
475 let mut body = vec![0u8; 14];
476 body.extend((sections.len() as u32).to_le_bytes());
477 for (_, p) in sections {
478 body.extend((0x8000_0000 | p.len() as u32).to_le_bytes());
479 }
480 body.extend((4 * sections.len() as u32 + 4).to_le_bytes());
481 for _ in 0..2 {
482 body.extend(0u32.to_le_bytes());
483 body.extend(4u32.to_le_bytes());
484 }
485 body.extend((classes.len() as u32).to_le_bytes());
486 for c in classes.iter() {
487 body.extend(c.to_le_bytes());
488 body.extend([0; 24]);
489 }
490 meta.extend(zlib(&body));
491 let mut data = Vec::new();
492 for (class, p) in sections {
493 data.extend([*class, 1, 0, 0]);
494 data.extend(p);
495 data.extend((p.len() as u32).to_le_bytes());
496 data.push(0);
497 }
498 let mut stream = vec![0u8; DATA_HEADER];
499 stream.extend(zlib(&data));
500 ole.create_stream(format!("/RSeStorage/Mseg{k}"))
501 .unwrap()
502 .write_all(&meta)
503 .unwrap();
504 ole.create_stream(format!("/RSeStorage/Bseg{k}"))
505 .unwrap()
506 .write_all(&stream)
507 .unwrap();
508 }
509 for (name, bytes) in extra {
510 ole.create_stream(name).unwrap().write_all(bytes).unwrap();
511 }
512 ole.flush().unwrap();
513 ole.into_inner().into_inner()
514 }
515
516 pub(crate) fn zlib(data: &[u8]) -> Vec<u8> {
517 use std::io::Write;
518 let mut z = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::fast());
519 z.write_all(data).unwrap();
520 z.finish().unwrap()
521 }
522
523 pub(crate) fn zstd(data: &[u8]) -> Vec<u8> {
524 ruzstd::encoding::compress_to_vec(data, ruzstd::encoding::CompressionLevel::Fastest)
525 }
526
527 pub(crate) fn as_zstd(bytes: &[u8], shift: isize) -> Vec<u8> {
531 use std::io::Write;
532 let mut ole = cfb::CompoundFile::open(std::io::Cursor::new(bytes.to_vec())).unwrap();
533 let streams: Vec<(String, std::path::PathBuf)> = ole
534 .read_storage("/RSeStorage")
535 .unwrap()
536 .filter(|e| e.is_stream())
537 .map(|e| (e.name().to_string(), e.path().to_path_buf()))
538 .collect();
539 for (name, path) in streams {
540 let at = match name.chars().next() {
541 Some('M') => {
542 meta_head(&read_stream(&mut ole, &path, MAX_RAW).unwrap())
543 .unwrap()
544 .1
545 }
546 Some('B') => DATA_HEADER,
547 _ => continue,
548 };
549 let raw = read_stream(&mut ole, &path, MAX_RAW).unwrap();
550 let body = unpack(&raw, at, MAX_RAW).unwrap();
551 let mut out = raw[..at.saturating_add_signed(shift.min(0))].to_vec();
552 out.extend(vec![0u8; shift.max(0) as usize]);
553 out.extend(zstd(&body));
554 ole.create_stream(&path).unwrap().write_all(&out).unwrap();
555 }
556 ole.flush().unwrap();
557 ole.into_inner().into_inner()
558 }
559
560 pub(crate) fn array(elem: u32, count: usize, bytes: &[u8]) -> Vec<u8> {
562 let mut out = ARRAY.to_le_bytes().to_vec();
563 out.extend((count as u32).to_le_bytes());
564 if count > 0 {
565 out.extend((count as u32).to_le_bytes());
566 out.extend(elem.to_le_bytes());
567 out.extend(bytes);
568 }
569 out
570 }
571
572 fn open(bytes: Vec<u8>) -> cfb::CompoundFile<std::io::Cursor<Vec<u8>>> {
573 cfb::CompoundFile::open(std::io::Cursor::new(bytes)).unwrap()
574 }
575
576 #[test]
577 fn segments_are_found_by_name_and_walked() {
578 let bytes = file(
579 &[
580 ("PmAppSegment", &[1], vec![(0, vec![9; 3])]),
581 (
582 "PmGraphicsSegment",
583 &[0xAAAA_0001, POINT3F],
584 vec![(1, vec![1, 2]), (0, array(0x101, 1, &[0; 12]))],
585 ),
586 ],
587 &[],
588 );
589 let mut ole = open(bytes);
590 let seg = segment(&mut ole, "PmGraphicsSegment", 1 << 20)
591 .unwrap()
592 .unwrap();
593 let sections: Vec<Section> = seg.sections().collect::<io::Result<_>>().unwrap();
594 assert_eq!(sections.len(), 2);
595 assert_eq!(sections[0].class, POINT3F);
596 assert_eq!(sections[0].payload, [1, 2]);
597 assert_eq!(sections[1].class, 0xAAAA_0001);
598 let a = Cursor::new(sections[1].payload, 0).array(&seg).unwrap();
599 assert_eq!((a.count, a.bytes.len()), (1, 12));
600 assert_eq!(seg.element_class(a.elem), Some(POINT3F));
601 assert!(segment(&mut ole, "AmRxSegment", 1 << 20).unwrap().is_none());
602 }
603
604 #[test]
605 fn damaged_segments_are_errors_not_panics() {
606 let bytes = file(
607 &[(
608 "PmGraphicsSegment",
609 &[1],
610 vec![(0, vec![7; 40]), (0, vec![8; 4])],
611 )],
612 &[],
613 );
614 for cut in (0..bytes.len()).step_by(97) {
616 if let Ok(mut ole) =
617 cfb::CompoundFile::open(std::io::Cursor::new(bytes[..cut].to_vec()))
618 {
619 if let Ok(Some(seg)) = segment(&mut ole, "PmGraphicsSegment", 1 << 20) {
620 for s in seg.sections() {
621 let _ = s;
622 }
623 }
624 }
625 }
626 let seg = Segment {
628 lengths: vec![2, 2],
629 classes: vec![1],
630 data: vec![0, 1, 0, 0, 5, 5, 3, 0, 0, 0, 0],
631 };
632 let all: Vec<_> = seg.sections().collect();
633 assert_eq!(all.len(), 1);
634 assert!(all[0].is_err());
635 let mut ole = open(file(
637 &[("PmGraphicsSegment", &[1], vec![(0, vec![0; 5000])])],
638 &[],
639 ));
640 assert!(segment(&mut ole, "PmGraphicsSegment", 100).is_err());
641 }
642
643 fn two_segments() -> Vec<u8> {
645 file(
646 &[
647 ("PmAppSegment", &[1], vec![(0, vec![9; 3])]),
648 (
649 "PmGraphicsSegment",
650 &[0xAAAA_0001, POINT3F],
651 vec![(1, vec![1, 2]), (0, array(0x101, 1, &[0; 12]))],
652 ),
653 ],
654 &[],
655 )
656 }
657
658 fn sections_of(bytes: Vec<u8>) -> io::Result<Vec<(u32, Vec<u8>)>> {
659 let mut ole = open(bytes);
660 let seg =
661 segment(&mut ole, "PmGraphicsSegment", 1 << 20)?.ok_or_else(|| bad("no segment"))?;
662 seg.sections()
663 .map(|s| s.map(|s| (s.class, s.payload.to_vec())))
664 .collect()
665 }
666
667 #[test]
668 fn zstandard_segments_read_like_zlib_ones() {
669 let zlib = sections_of(two_segments()).unwrap();
670 assert_eq!(zlib.len(), 2);
671 for shift in [0, 1, 4, 16, -2, -1] {
673 let packed = as_zstd(&two_segments(), shift);
674 assert_eq!(sections_of(packed).unwrap(), zlib, "shift {shift}");
675 }
676 }
677
678 #[test]
679 fn zstandard_bodies_are_told_apart_from_zlib_by_their_magic() {
680 let data = b"PmGraphicsSegment ".repeat(50);
681 assert_eq!(unpack(&zlib(&data), 0, 1 << 20).unwrap(), data);
682 assert_eq!(unpack(&zstd(&data), 0, 1 << 20).unwrap(), data);
683 let mut two = zstd(&data[..300]);
685 two.extend(zstd(&data[300..]));
686 assert_eq!(unpack(&two, 0, 1 << 20).unwrap(), data);
687 let mut late = vec![2u8];
689 late.extend(zstd(&data));
690 assert_eq!(unpack(&late, 0, 1 << 20).unwrap(), data);
691 let mut far = vec![0u8; ZSTD_SLACK + 1];
693 far.extend(zstd(&data));
694 assert!(unpack(&far, 0, 1 << 20).is_err());
695 assert!(zlib_header(&zlib(&data)) && !zlib_header(&zstd(&data)));
696 }
697
698 #[test]
702 fn nist_parts_repacked_as_zstandard_unpack_to_the_same_segments() {
703 let dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/cad/nist");
704 let zlib = std::fs::read(format!(
705 "{dir}/ftc-11/inventor-2021/INV_nist_ftc_11_asme1_2021.ipt"
706 ))
707 .unwrap();
708 let zstd = as_zstd(&zlib, 0);
709 let (mut a, mut b) = (open(zlib), open(zstd));
710 for name in ["PmGraphicsSegment", "PmAppSegment", "PmBRepSegment"] {
711 let x = segment(&mut a, name, MAX_RAW).unwrap();
712 let y = segment(&mut b, name, MAX_RAW).unwrap();
713 let (x, y) = (x.expect(name), y.expect(name));
714 assert_eq!(x.lengths, y.lengths, "{name}");
715 assert_eq!(x.classes, y.classes, "{name}");
716 assert!(x.data == y.data, "{name}: data differs");
717 }
718 }
719
720 #[test]
721 fn damaged_zstandard_segments_are_errors_not_panics() {
722 let data: Vec<u8> = (0..2_000u32)
723 .flat_map(|k| (k % 251).to_le_bytes())
724 .collect();
725 let packed = zstd(&data);
726 assert!(unpack(&packed, 0, 1000).is_err());
728 assert_eq!(unpack(&packed, 0, data.len() as u64).unwrap(), data);
729 for cut in 0..packed.len() {
731 let _ = unpack(&packed[..cut], 0, 1 << 20);
732 }
733 for at in 0..packed.len() {
734 let mut bad = packed.clone();
735 bad[at] ^= 0x5A;
736 let _ = unpack(&bad, 0, 1 << 20);
737 }
738 let mut huge = ZSTD_MAGIC.to_vec();
740 huge.extend([0x00, 0xF8, 0, 0, 0, 0]);
741 assert!(unpack(&huge, 0, 1 << 30).is_err());
742 let bytes = as_zstd(&two_segments(), 0);
744 for cut in (0..bytes.len()).step_by(61) {
745 if let Ok(mut ole) =
746 cfb::CompoundFile::open(std::io::Cursor::new(bytes[..cut].to_vec()))
747 {
748 if let Ok(Some(seg)) = segment(&mut ole, "PmGraphicsSegment", 1 << 20) {
749 for s in seg.sections() {
750 let _ = s;
751 }
752 }
753 }
754 }
755 }
756
757 #[test]
758 fn arrays_with_unknown_elements_or_short_data_end_the_read() {
759 let seg = Segment {
760 lengths: vec![],
761 classes: vec![POINT3F, 0x1234_5678],
762 data: vec![],
763 };
764 let ok = array(0x100, 2, &[0; 24]);
765 assert_eq!(Cursor::new(&ok, 0).array(&seg).unwrap().count, 2);
766 assert!(Cursor::new(&ok[..30], 0).array(&seg).is_none());
767 assert!(Cursor::new(&array(0x101, 1, &[0; 12]), 0)
768 .array(&seg)
769 .is_none());
770 let mut huge = array(0x100, 1, &[0; 12]);
771 huge[4..8].copy_from_slice(&u32::MAX.to_le_bytes());
772 assert!(Cursor::new(&huge, 0).array(&seg).is_none());
773 let empty = array(0, 0, &[]);
774 let mut c = Cursor::new(&empty, 0);
775 assert_eq!(c.array(&seg).unwrap().count, 0);
776 assert_eq!(c.at, 8);
777 }
778}