f1e362376b0e9625811b363721d5e1163f31dcea
[pspp] / rust / src / raw.rs
1 use crate::endian::{Endian, Parse, ToBytes};
2 use crate::Error;
3
4 use flate2::read::ZlibDecoder;
5 use num::Integer;
6 use num_derive::FromPrimitive;
7 use std::{
8     collections::VecDeque,
9     io::{Error as IoError, Read, Seek, SeekFrom},
10     iter::FusedIterator,
11 };
12
13 #[derive(Copy, Clone, Debug)]
14 pub enum Compression {
15     Simple,
16     ZLib,
17 }
18
19 pub enum Record {
20     Header(Header),
21     Document(Document),
22     Variable(Variable),
23     ValueLabel(ValueLabel),
24     VarIndexes(VarIndexes),
25     Extension(Extension),
26     EndOfHeaders,
27     ZHeader(ZHeader),
28     ZTrailer(ZTrailer),
29     Case(Vec<Value>),
30 }
31
32 pub struct Header {
33     /// Magic number.
34     pub magic: Magic,
35
36     /// Eye-catcher string, product name, in the file's encoding.  Padded
37     /// on the right with spaces.
38     pub eye_catcher: [u8; 60],
39
40     /// Layout code, normally either 2 or 3.
41     pub layout_code: u32,
42
43     /// Number of variable positions, or `None` if the value in the file is
44     /// questionably trustworthy.
45     pub nominal_case_size: Option<u32>,
46
47     /// Compression type, if any,
48     pub compression: Option<Compression>,
49
50     /// 0-based variable index of the weight variable, or `None` if the file is
51     /// unweighted.
52     pub weight_index: Option<u32>,
53
54     /// Claimed number of cases, if known.
55     pub n_cases: Option<u32>,
56
57     /// Compression bias, usually 100.0.
58     pub bias: f64,
59
60     /// `dd mmm yy` in the file's encoding.
61     pub creation_date: [u8; 9],
62
63     /// `HH:MM:SS` in the file's encoding.
64     pub creation_time: [u8; 8],
65
66     /// File label, in the file's encoding.  Padded on the right with spaces.
67     pub file_label: [u8; 64],
68
69     /// Endianness of the data in the file header.
70     pub endian: Endian,
71 }
72
73 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
74 pub struct Magic([u8; 4]);
75
76 impl Magic {
77     /// Magic number for a regular system file.
78     pub const SAV: Magic = Magic(*b"$FL2");
79
80     /// Magic number for a system file that contains zlib-compressed data.
81     pub const ZSAV: Magic = Magic(*b"$FL3");
82
83     /// Magic number for an EBDIC-encoded system file.  This is `$FL2` encoded
84     /// in EBCDIC.
85     pub const EBCDIC: Magic = Magic([0x5b, 0xc6, 0xd3, 0xf2]);
86 }
87
88 impl TryFrom<[u8; 4]> for Magic {
89     type Error = Error;
90
91     fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
92         let magic = Magic(value);
93         match magic {
94             Magic::SAV | Magic::ZSAV | Magic::EBCDIC => Ok(magic),
95             _ => Err(Error::BadMagic(value)),
96         }
97     }
98 }
99
100 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
101 pub enum VarType {
102     Number,
103     String,
104 }
105
106 impl VarType {
107     fn from_width(width: i32) -> VarType {
108         match width {
109             0 => VarType::Number,
110             _ => VarType::String,
111         }
112     }
113 }
114
115 trait State {
116     #[allow(clippy::type_complexity)]
117     fn read(self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error>;
118 }
119
120 struct Start<R: Read + Seek> {
121     reader: R,
122 }
123
124 struct CommonState<R: Read + Seek> {
125     reader: R,
126     endian: Endian,
127     bias: f64,
128     compression: Option<Compression>,
129     var_types: Vec<VarType>,
130 }
131
132 impl<R: Read + Seek + 'static> State for Start<R> {
133     fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
134         let header = read_header(&mut self.reader)?;
135         let next_state = Headers(CommonState {
136             reader: self.reader,
137             endian: header.endian,
138             bias: header.bias,
139             compression: header.compression,
140             var_types: Vec::new(),
141         });
142         Ok(Some((Record::Header(header), Box::new(next_state))))
143     }
144 }
145
146 struct Headers<R: Read + Seek>(CommonState<R>);
147
148 impl<R: Read + Seek + 'static> State for Headers<R> {
149     fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
150         let endian = self.0.endian;
151         let rec_type: u32 = endian.parse(read_bytes(&mut self.0.reader)?);
152         let record = match rec_type {
153             2 => {
154                 let variable = read_variable_record(&mut self.0.reader, endian)?;
155                 self.0.var_types.push(VarType::from_width(variable.width));
156                 Record::Variable(variable)
157             }
158             3 => Record::ValueLabel(read_value_label_record(&mut self.0.reader, endian)?),
159             4 => Record::VarIndexes(read_var_indexes_record(&mut self.0.reader, endian)?),
160             6 => Record::Document(read_document_record(&mut self.0.reader, endian)?),
161             7 => Record::Extension(read_extension_record(&mut self.0.reader, endian)?),
162             999 => {
163                 let _: [u8; 4] = read_bytes(&mut self.0.reader)?;
164                 let next_state: Box<dyn State> = match self.0.compression {
165                     None => Box::new(Data(self.0)),
166                     Some(Compression::Simple) => Box::new(CompressedData::new(self.0)),
167                     Some(Compression::ZLib) => Box::new(ZlibHeader(self.0)),
168                 };
169                 return Ok(Some((Record::EndOfHeaders, next_state)));
170             }
171             _ => {
172                 return Err(Error::BadRecordType {
173                     offset: self.0.reader.stream_position()?,
174                     rec_type,
175                 })
176             }
177         };
178         Ok(Some((record, self)))
179     }
180 }
181
182 struct ZlibHeader<R: Read + Seek>(CommonState<R>);
183
184 impl<R: Read + Seek + 'static> State for ZlibHeader<R> {
185     fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
186         let zheader = read_zheader(&mut self.0.reader, self.0.endian)?;
187         Ok(Some((Record::ZHeader(zheader), self)))
188     }
189 }
190
191 struct ZlibTrailer<R: Read + Seek>(CommonState<R>, ZHeader);
192
193 impl<R: Read + Seek + 'static> State for ZlibTrailer<R> {
194     fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
195         let retval = read_ztrailer(&mut self.0.reader, self.0.endian, self.1.ztrailer_offset, self.1.ztrailer_len)?;
196         let next_state = Box::new(CompressedData::new(CommonState {
197             reader: ZlibDecodeMultiple::new(self.0.reader),
198             endian: self.0.endian,
199             bias: self.0.bias,
200             compression: self.0.compression,
201             var_types: self.0.var_types
202         }));
203         match retval {
204             None => next_state.read(),
205             Some(ztrailer) => Ok(Some((Record::ZTrailer(ztrailer), next_state)))
206         }        
207     }
208 }
209
210 struct Data<R: Read + Seek>(CommonState<R>);
211
212 impl<R: Read + Seek + 'static> State for Data<R> {
213     fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
214         let case_start = self.0.reader.stream_position()?;
215         let mut values = Vec::with_capacity(self.0.var_types.len());
216         for (i, &var_type) in self.0.var_types.iter().enumerate() {
217             let Some(raw) = try_read_bytes(&mut self.0.reader)? else {
218                 if i == 0 {
219                     return Ok(None);
220                 } else {
221                     let offset = self.0.reader.stream_position()?;
222                     return Err(Error::EofInCase {
223                         offset,
224                         case_ofs: offset - case_start,
225                         case_len: self.0.var_types.len() * 8,
226                     });
227                 }
228             };
229             values.push(Value::from_raw(var_type, raw, self.0.endian));
230         }
231         Ok(Some((Record::Case(values), self)))
232     }
233 }
234
235 struct CompressedData<R: Read + Seek> {
236     common: CommonState<R>,
237     codes: VecDeque<u8>,
238 }
239
240 impl<R: Read + Seek + 'static> CompressedData<R> {
241     fn new(common: CommonState<R>) -> CompressedData<R> {
242         CompressedData { common, codes: VecDeque::new() }
243     }
244 }
245
246 impl<R: Read + Seek + 'static> State for CompressedData<R> {
247     fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
248         let case_start = self.common.reader.stream_position()?;
249         let mut values = Vec::with_capacity(self.common.var_types.len());
250         for (i, &var_type) in self.common.var_types.iter().enumerate() {
251             let value = loop {
252                 let Some(code) = self.codes.pop_front() else {
253                     let Some(new_codes): Option<[u8; 8]> = try_read_bytes(&mut self.common.reader)?
254                     else {
255                         if i == 0 {
256                             return Ok(None);
257                         } else {
258                             let offset = self.common.reader.stream_position()?;
259                             return Err(Error::EofInCompressedCase {
260                                 offset,
261                                 case_ofs: offset - case_start,
262                             });
263                         }
264                     };
265                     self.codes.extend(new_codes.into_iter());
266                     continue;
267                 };
268                 match code {
269                     0 => (),
270                     1..=251 => match var_type {
271                         VarType::Number => break Value::Number(Some(code as f64 - self.common.bias)),
272                         VarType::String => {
273                             break Value::String(self.common.endian.to_bytes(code as f64 - self.common.bias))
274                         }
275                     },
276                     252 => {
277                         if i == 0 {
278                             return Ok(None);
279                         } else {
280                             let offset = self.common.reader.stream_position()?;
281                             return Err(Error::PartialCompressedCase {
282                                 offset,
283                                 case_ofs: offset - case_start,
284                             });
285                         }
286                     }
287                     253 => {
288                         break Value::from_raw(
289                             var_type,
290                             read_bytes(&mut self.common.reader)?,
291                             self.common.endian,
292                         )
293                     }
294                     254 => match var_type {
295                         VarType::String => break Value::String(*b"        "), // XXX EBCDIC
296                         VarType::Number => {
297                             return Err(Error::CompressedStringExpected {
298                                 offset: case_start,
299                                 case_ofs: self.common.reader.stream_position()? - case_start,
300                             })
301                         }
302                     },
303                     255 => match var_type {
304                         VarType::Number => break Value::Number(None),
305                         VarType::String => {
306                             return Err(Error::CompressedNumberExpected {
307                                 offset: case_start,
308                                 case_ofs: self.common.reader.stream_position()? - case_start,
309                             })
310                         }
311                     },
312                 }
313             };
314             values.push(value);
315         }
316         Ok(Some((Record::Case(values), self)))
317     }
318 }
319
320 struct ZlibDecodeMultiple<R>
321 where
322     R: Read + Seek,
323 {
324     reader: Option<ZlibDecoder<R>>,
325 }
326
327 impl<R> ZlibDecodeMultiple<R>
328 where
329     R: Read + Seek,
330 {
331     fn new(reader: R) -> ZlibDecodeMultiple<R> {
332         ZlibDecodeMultiple {
333             reader: Some(ZlibDecoder::new(reader)),
334         }
335     }
336 }
337
338 impl<R> Read for ZlibDecodeMultiple<R>
339 where
340     R: Read + Seek,
341 {
342     fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
343         loop {
344             match self.reader.as_mut().unwrap().read(buf)? {
345                 0 => {
346                     let inner = self.reader.take().unwrap().into_inner();
347                     self.reader = Some(ZlibDecoder::new(inner));
348                 }
349                 n => return Ok(n),
350             };
351         }
352     }
353 }
354
355 impl<R> Seek for ZlibDecodeMultiple<R>
356 where
357     R: Read + Seek,
358 {
359     fn seek(&mut self, pos: SeekFrom) -> Result<u64, IoError> {
360         self.reader.as_mut().unwrap().get_mut().seek(pos)
361     }
362 }
363
364 #[derive(Copy, Clone)]
365 pub enum Value {
366     Number(Option<f64>),
367     String([u8; 8]),
368 }
369
370 impl Value {
371     pub fn from_raw(var_type: VarType, raw: [u8; 8], endian: Endian) -> Value {
372         match var_type {
373             VarType::String => Value::String(raw),
374             VarType::Number => {
375                 let number: f64 = endian.parse(raw);
376                 Value::Number((number != -f64::MAX).then_some(number))
377             }
378         }
379     }
380 }
381
382 pub struct Reader {
383     state: Option<Box<dyn State>>,
384 }
385
386 impl Reader {
387     pub fn new<R: Read + Seek + 'static>(reader: R) -> Result<Reader, Error> {
388         Ok(Reader {
389             state: Some(Box::new(Start { reader })),
390         })
391     }
392 }
393
394 impl Iterator for Reader {
395     type Item = Result<Record, Error>;
396
397     fn next(&mut self) -> Option<Self::Item> {
398         match self.state.take()?.read() {
399             Ok(Some((record, next_state))) => {
400                 self.state = Some(next_state);
401                 Some(Ok(record))
402             }
403             Ok(None) => None,
404             Err(error) => Some(Err(error)),
405         }
406     }
407 }
408
409 impl FusedIterator for Reader {}
410
411 fn read_header<R: Read>(r: &mut R) -> Result<Header, Error> {
412     let magic: [u8; 4] = read_bytes(r)?;
413     let magic: Magic = magic.try_into().map_err(|_| Error::NotASystemFile)?;
414
415     let eye_catcher: [u8; 60] = read_bytes(r)?;
416     let layout_code: [u8; 4] = read_bytes(r)?;
417     let endian = Endian::identify_u32(2, layout_code)
418         .or_else(|| Endian::identify_u32(2, layout_code))
419         .ok_or_else(|| Error::NotASystemFile)?;
420     let layout_code = endian.parse(layout_code);
421
422     let nominal_case_size: u32 = endian.parse(read_bytes(r)?);
423     let nominal_case_size =
424         (nominal_case_size <= i32::MAX as u32 / 16).then_some(nominal_case_size);
425
426     let compression_code: u32 = endian.parse(read_bytes(r)?);
427     let compression = match (magic, compression_code) {
428         (Magic::ZSAV, 2) => Some(Compression::ZLib),
429         (Magic::ZSAV, code) => return Err(Error::InvalidZsavCompression(code)),
430         (_, 0) => None,
431         (_, 1) => Some(Compression::Simple),
432         (_, code) => return Err(Error::InvalidSavCompression(code)),
433     };
434
435     let weight_index: u32 = endian.parse(read_bytes(r)?);
436     let weight_index = (weight_index > 0).then_some(weight_index - 1);
437
438     let n_cases: u32 = endian.parse(read_bytes(r)?);
439     let n_cases = (n_cases < i32::MAX as u32 / 2).then_some(n_cases);
440
441     let bias: f64 = endian.parse(read_bytes(r)?);
442
443     let creation_date: [u8; 9] = read_bytes(r)?;
444     let creation_time: [u8; 8] = read_bytes(r)?;
445     let file_label: [u8; 64] = read_bytes(r)?;
446     let _: [u8; 3] = read_bytes(r)?;
447
448     Ok(Header {
449         magic,
450         layout_code,
451         nominal_case_size,
452         compression,
453         weight_index,
454         n_cases,
455         bias,
456         creation_date,
457         creation_time,
458         eye_catcher,
459         file_label,
460         endian,
461     })
462 }
463
464 pub struct Variable {
465     /// Offset from the start of the file to the start of the record.
466     pub offset: u64,
467
468     /// Variable width, in the range -1..=255.
469     pub width: i32,
470
471     /// Variable name, padded on the right with spaces.
472     pub name: [u8; 8],
473
474     /// Print format.
475     pub print_format: u32,
476
477     /// Write format.
478     pub write_format: u32,
479
480     /// Missing value code, one of -3, -2, 0, 1, 2, or 3.
481     pub missing_value_code: i32,
482
483     /// Raw missing values, up to 3 of them.
484     pub missing: Vec<[u8; 8]>,
485
486     /// Optional variable label.
487     pub label: Option<Vec<u8>>,
488 }
489
490 fn read_variable_record<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<Variable, Error> {
491     let offset = r.stream_position()?;
492     let width: i32 = endian.parse(read_bytes(r)?);
493     let has_variable_label: u32 = endian.parse(read_bytes(r)?);
494     let missing_value_code: i32 = endian.parse(read_bytes(r)?);
495     let print_format: u32 = endian.parse(read_bytes(r)?);
496     let write_format: u32 = endian.parse(read_bytes(r)?);
497     let name: [u8; 8] = read_bytes(r)?;
498
499     let label = match has_variable_label {
500         0 => None,
501         1 => {
502             let len: u32 = endian.parse(read_bytes(r)?);
503             let read_len = len.min(65535) as usize;
504             let label = Some(read_vec(r, read_len)?);
505
506             let padding_bytes = Integer::next_multiple_of(&len, &4) - len;
507             let _ = read_vec(r, padding_bytes as usize)?;
508
509             label
510         }
511         _ => {
512             return Err(Error::BadVariableLabelCode {
513                 offset,
514                 code: has_variable_label,
515             })
516         }
517     };
518
519     let mut missing = Vec::new();
520     if missing_value_code != 0 {
521         match (width, missing_value_code) {
522             (0, -3 | -2 | 1 | 2 | 3) => (),
523             (0, _) => {
524                 return Err(Error::BadNumericMissingValueCode {
525                     offset,
526                     code: missing_value_code,
527                 })
528             }
529             (_, 0..=3) => (),
530             (_, _) => {
531                 return Err(Error::BadStringMissingValueCode {
532                     offset,
533                     code: missing_value_code,
534                 })
535             }
536         }
537
538         for _ in 0..missing_value_code.abs() {
539             missing.push(read_bytes(r)?);
540         }
541     }
542
543     Ok(Variable {
544         offset,
545         width,
546         name,
547         print_format,
548         write_format,
549         missing_value_code,
550         missing,
551         label,
552     })
553 }
554
555 pub struct ValueLabel {
556     /// Offset from the start of the file to the start of the record.
557     pub offset: u64,
558
559     /// The labels.
560     pub labels: Vec<([u8; 8], Vec<u8>)>,
561 }
562
563 impl ValueLabel {
564     /// Maximum number of value labels in a record.
565     pub const MAX: u32 = u32::MAX / 8;
566 }
567
568 fn read_value_label_record<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<ValueLabel, Error> {
569     let offset = r.stream_position()?;
570     let n: u32 = endian.parse(read_bytes(r)?);
571     if n > ValueLabel::MAX {
572         return Err(Error::BadNumberOfValueLabels {
573             offset,
574             n,
575             max: ValueLabel::MAX,
576         });
577     }
578
579     let mut labels = Vec::new();
580     for _ in 0..n {
581         let value: [u8; 8] = read_bytes(r)?;
582         let label_len: u8 = endian.parse(read_bytes(r)?);
583         let label_len = label_len as usize;
584         let padded_len = Integer::next_multiple_of(&(label_len + 1), &8);
585
586         let mut label = read_vec(r, padded_len)?;
587         label.truncate(label_len);
588         labels.push((value, label));
589     }
590     Ok(ValueLabel { offset, labels })
591 }
592
593 pub struct VarIndexes {
594     /// Offset from the start of the file to the start of the record.
595     pub offset: u64,
596
597     /// The 0-based indexes of the variable indexes.
598     pub var_indexes: Vec<u32>,
599 }
600
601 impl VarIndexes {
602     /// Maximum number of variable indexes in a record.
603     pub const MAX: u32 = u32::MAX / 8;
604 }
605
606 fn read_var_indexes_record<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<VarIndexes, Error> {
607     let offset = r.stream_position()?;
608     let n: u32 = endian.parse(read_bytes(r)?);
609     if n > VarIndexes::MAX {
610         return Err(Error::BadNumberOfVarIndexes {
611             offset,
612             n,
613             max: VarIndexes::MAX,
614         });
615     }
616     let mut var_indexes = Vec::with_capacity(n as usize);
617     for _ in 0..n {
618         var_indexes.push(endian.parse(read_bytes(r)?));
619     }
620
621     Ok(VarIndexes {
622         offset,
623         var_indexes,
624     })
625 }
626
627 pub const DOC_LINE_LEN: u32 = 80;
628 pub const DOC_MAX_LINES: u32 = i32::MAX as u32 / DOC_LINE_LEN;
629
630 pub struct Document {
631     /// Offset from the start of the file to the start of the record.
632     pub pos: u64,
633
634     /// The document, as an array of 80-byte lines.
635     pub lines: Vec<[u8; DOC_LINE_LEN as usize]>,
636 }
637
638 fn read_document_record<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<Document, Error> {
639     let offset = r.stream_position()?;
640     let n: u32 = endian.parse(read_bytes(r)?);
641     match n {
642         0..=DOC_MAX_LINES => {
643             let pos = r.stream_position()?;
644             let mut lines = Vec::with_capacity(n as usize);
645             for _ in 0..n {
646                 let line: [u8; 80] = read_bytes(r)?;
647                 lines.push(line);
648             }
649             Ok(Document { pos, lines })
650         }
651         _ => Err(Error::BadDocumentLength {
652             offset,
653             n,
654             max: DOC_MAX_LINES,
655         }),
656     }
657 }
658
659 #[derive(FromPrimitive)]
660 enum ExtensionType {
661     /// Machine integer info.
662     Integer = 3,
663     /// Machine floating-point info.
664     Float = 4,
665     /// Variable sets.
666     VarSets = 5,
667     /// DATE.
668     Date = 6,
669     /// Multiple response sets.
670     Mrsets = 7,
671     /// SPSS Data Entry.
672     DataEntry = 8,
673     /// Extra product info text.
674     ProductInfo = 10,
675     /// Variable display parameters.
676     Display = 11,
677     /// Long variable names.
678     LongNames = 13,
679     /// Long strings.
680     LongStrings = 14,
681     /// Extended number of cases.
682     Ncases = 16,
683     /// Data file attributes.
684     FileAttrs = 17,
685     /// Variable attributes.
686     VarAttrs = 18,
687     /// Multiple response sets (extended).
688     Mrsets2 = 19,
689     /// Character encoding.
690     Encoding = 20,
691     /// Value labels for long strings.
692     LongLabels = 21,
693     /// Missing values for long strings.
694     LongMissing = 22,
695     /// "Format properties in dataview table".
696     Dataview = 24,
697 }
698
699 pub struct Extension {
700     /// Offset from the start of the file to the start of the record.
701     pub offset: u64,
702
703     /// Record subtype.
704     pub subtype: u32,
705
706     /// Size of each data element.
707     pub size: u32,
708
709     /// Number of data elements.
710     pub count: u32,
711
712     /// `size * count` bytes of data.
713     pub data: Vec<u8>,
714 }
715
716 /*
717 fn extension_record_size_requirements(extension: ExtensionType) -> (u32, u32) {
718     match extension {
719         /* Implemented record types. */
720         ExtensionType::Integer => (4, 8),
721         ExtensionType::Float => (8, 3),
722         ExtensionType::VarSets => (1, 0),
723         ExtensionType::Mrsets => (1, 0),
724         ExtensionType::ProductInfo => (1, 0),
725         ExtensionType::Display => (4, 0),
726         ExtensionType::LongNames => (1, 0),
727         ExtensionType::LongStrings => (1, 0),
728         ExtensionType::Ncases => (8, 2),
729         ExtensionType::FileAttrs => (1, 0),
730         ExtensionType::VarAttrs => (1, 0),
731         ExtensionType::Mrsets2 => (1, 0),
732         ExtensionType::Encoding => (1, 0),
733         ExtensionType::LongLabels => (1, 0),
734         ExtensionType::LongMissing => (1, 0),
735
736         /* Ignored record types. */
737         ExtensionType::Date => (0, 0),
738         ExtensionType::DataEntry => (0, 0),
739         ExtensionType::Dataview => (0, 0),
740     }
741 }
742  */
743
744 fn read_extension_record<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<Extension, Error> {
745     let subtype = endian.parse(read_bytes(r)?);
746     let offset = r.stream_position()?;
747     let size: u32 = endian.parse(read_bytes(r)?);
748     let count = endian.parse(read_bytes(r)?);
749     let Some(product) = size.checked_mul(count) else {
750         return Err(Error::ExtensionRecordTooLarge {
751             offset,
752             subtype,
753             size,
754             count,
755         });
756     };
757     let offset = r.stream_position()?;
758     let data = read_vec(r, product as usize)?;
759     Ok(Extension {
760         offset,
761         subtype,
762         size,
763         count,
764         data,
765     })
766 }
767
768 pub struct ZHeader {
769     /// File offset to the start of the record.
770     pub offset: u64,
771
772     /// File offset to the ZLIB data header.
773     pub zheader_offset: u64,
774
775     /// File offset to the ZLIB trailer.
776     pub ztrailer_offset: u64,
777
778     /// Length of the ZLIB trailer in bytes.
779     pub ztrailer_len: u64,
780 }
781
782 fn read_zheader<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<ZHeader, Error> {
783     let offset = r.stream_position()?;
784     let zheader_offset: u64 = endian.parse(read_bytes(r)?);
785     let ztrailer_offset: u64 = endian.parse(read_bytes(r)?);
786     let ztrailer_len: u64 = endian.parse(read_bytes(r)?);
787
788     Ok(ZHeader {
789         offset,
790         zheader_offset,
791         ztrailer_offset,
792         ztrailer_len,
793     })
794 }
795
796 pub struct ZTrailer {
797     /// File offset to the start of the record.
798     pub offset: u64,
799
800     /// Compression bias as a negative integer, e.g. -100.
801     pub int_bias: i64,
802
803     /// Always observed as zero.
804     pub zero: u64,
805
806     /// Uncompressed size of each block, except possibly the last.  Only
807     /// `0x3ff000` has been observed so far.
808     pub block_size: u32,
809
810     /// Block descriptors, always `(ztrailer_len - 24) / 24)` of them.
811     pub blocks: Vec<ZBlock>,
812 }
813
814 pub struct ZBlock {
815     /// Offset of block of data if simple compression were used.
816     pub uncompressed_ofs: u64,
817
818     /// Actual offset within the file of the compressed data block.
819     pub compressed_ofs: u64,
820
821     /// The number of bytes in this data block after decompression.  This is
822     /// `block_size` in every data block but the last, which may be smaller.
823     pub uncompressed_size: u32,
824
825     /// The number of bytes in this data block, as stored compressed in this
826     /// file.
827     pub compressed_size: u32,
828 }
829
830 fn read_ztrailer<R: Read + Seek>(
831     r: &mut R,
832     endian: Endian,
833     ztrailer_ofs: u64,
834     ztrailer_len: u64,
835 ) -> Result<Option<ZTrailer>, Error> {
836     let start_offset = r.stream_position()?;
837     if r.seek(SeekFrom::Start(ztrailer_ofs)).is_err() {
838         return Ok(None);
839     }
840     let int_bias = endian.parse(read_bytes(r)?);
841     let zero = endian.parse(read_bytes(r)?);
842     let block_size = endian.parse(read_bytes(r)?);
843     let n_blocks: u32 = endian.parse(read_bytes(r)?);
844     let expected_n_blocks = (ztrailer_len - 24) / 24;
845     if n_blocks as u64 != expected_n_blocks {
846         return Err(Error::BadZlibTrailerNBlocks {
847             offset: ztrailer_ofs,
848             n_blocks,
849             expected_n_blocks,
850             ztrailer_len,
851         });
852     }
853     let mut blocks = Vec::with_capacity(n_blocks as usize);
854     for _ in 0..n_blocks {
855         let uncompressed_ofs = endian.parse(read_bytes(r)?);
856         let compressed_ofs = endian.parse(read_bytes(r)?);
857         let uncompressed_size = endian.parse(read_bytes(r)?);
858         let compressed_size = endian.parse(read_bytes(r)?);
859         blocks.push(ZBlock {
860             uncompressed_ofs,
861             compressed_ofs,
862             uncompressed_size,
863             compressed_size,
864         });
865     }
866     r.seek(SeekFrom::Start(start_offset))?;
867     Ok(Some(ZTrailer {
868         offset: ztrailer_ofs,
869         int_bias,
870         zero,
871         block_size,
872         blocks,
873     }))
874 }
875
876 fn try_read_bytes<const N: usize, R: Read>(r: &mut R) -> Result<Option<[u8; N]>, IoError> {
877     let mut buf = [0; N];
878     let n = r.read(&mut buf)?;
879     if n > 0 {
880         if n < N {
881             r.read_exact(&mut buf[n..])?;
882         }
883         Ok(Some(buf))
884     } else {
885         Ok(None)
886     }
887 }
888
889 fn read_bytes<const N: usize, R: Read>(r: &mut R) -> Result<[u8; N], IoError> {
890     let mut buf = [0; N];
891     r.read_exact(&mut buf)?;
892     Ok(buf)
893 }
894
895 fn read_vec<R: Read>(r: &mut R, n: usize) -> Result<Vec<u8>, IoError> {
896     let mut vec = vec![0; n];
897     r.read_exact(&mut vec)?;
898     Ok(vec)
899 }