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