hey it compiles again
[pspp] / rust / src / raw.rs
1 use crate::endian::{Endian, Parse, ToBytes};
2
3 use encoding_rs::{mem::decode_latin1, DecoderResult, Encoding};
4 use flate2::read::ZlibDecoder;
5 use num::Integer;
6 use std::{
7     borrow::Cow,
8     cell::RefCell,
9     cmp::Ordering,
10     collections::VecDeque,
11     fmt::{Debug, Display, Formatter, Result as FmtResult},
12     io::{Error as IoError, Read, Seek, SeekFrom},
13     iter::repeat,
14     mem::take,
15     ops::Range,
16     rc::Rc,
17     str::from_utf8,
18 };
19 use thiserror::Error as ThisError;
20
21 #[derive(ThisError, Debug)]
22 pub enum Error {
23     #[error("Not an SPSS system file")]
24     NotASystemFile,
25
26     #[error("Invalid magic number {0:?}")]
27     BadMagic([u8; 4]),
28
29     #[error("I/O error ({0})")]
30     Io(#[from] IoError),
31
32     #[error("Invalid SAV compression code {0}")]
33     InvalidSavCompression(u32),
34
35     #[error("Invalid ZSAV compression code {0}")]
36     InvalidZsavCompression(u32),
37
38     #[error("Variable record at offset {offset:#x} specifies width {width} not in valid range [-1,255).")]
39     BadVariableWidth { offset: u64, width: i32 },
40
41     #[error("Document record at offset {offset:#x} has document line count ({n}) greater than the maximum number {max}.")]
42     BadDocumentLength { offset: u64, n: usize, max: usize },
43
44     #[error("At offset {offset:#x}, unrecognized record type {rec_type}.")]
45     BadRecordType { offset: u64, rec_type: u32 },
46
47     #[error("In variable record starting at offset {start_offset:#x}, variable label code {code} at offset {code_offset:#x} is not 0 or 1.")]
48     BadVariableLabelCode {
49         start_offset: u64,
50         code_offset: u64,
51         code: u32,
52     },
53
54     #[error(
55         "At offset {offset:#x}, numeric missing value code ({code}) is not -3, -2, 0, 1, 2, or 3."
56     )]
57     BadNumericMissingValueCode { offset: u64, code: i32 },
58
59     #[error("At offset {offset:#x}, string missing value code ({code}) is not 0, 1, 2, or 3.")]
60     BadStringMissingValueCode { offset: u64, code: i32 },
61
62     #[error("At offset {offset:#x}, number of value labels ({n}) is greater than the maximum number {max}.")]
63     BadNumberOfValueLabels { offset: u64, n: u32, max: u32 },
64
65     #[error("At offset {offset:#x}, following value label record, found record type {rec_type} instead of expected type 4 for variable index record")]
66     ExpectedVarIndexRecord { offset: u64, rec_type: u32 },
67
68     #[error("At offset {offset:#x}, number of variables indexes for value labels ({n}) is greater than the maximum number ({max}).")]
69     TooManyVarIndexes { offset: u64, n: u32, max: u32 },
70
71     #[error("At offset {offset:#x}, at least one valid variable index for value labels is required but none were specified.")]
72     NoVarIndexes { offset: u64 },
73
74     #[error("At offset {offset:#x}, the first variable index is for a {var_type} variable but the following variable indexes are for {} variables: {wrong_types:?}", var_type.opposite())]
75     MixedVarTypes {
76         offset: u64,
77         var_type: VarType,
78         wrong_types: Vec<u32>,
79     },
80
81     #[error("At offset {offset:#x}, one or more variable indexes for value labels were not in the valid range [1,{max}]: {invalid:?}")]
82     InvalidVarIndexes {
83         offset: u64,
84         max: usize,
85         invalid: Vec<u32>,
86     },
87
88     #[error("At offset {offset:#x}, record type 7 subtype {subtype} is too large with element size {size} and {count} elements.")]
89     ExtensionRecordTooLarge {
90         offset: u64,
91         subtype: u32,
92         size: u32,
93         count: u32,
94     },
95
96     #[error("Unexpected end of file at offset {offset:#x}, {case_ofs} bytes into a {case_len}-byte case.")]
97     EofInCase {
98         offset: u64,
99         case_ofs: u64,
100         case_len: usize,
101     },
102
103     #[error(
104         "Unexpected end of file at offset {offset:#x}, {case_ofs} bytes into a compressed case."
105     )]
106     EofInCompressedCase { offset: u64, case_ofs: u64 },
107
108     #[error("Data ends at offset {offset:#x}, {case_ofs} bytes into a compressed case.")]
109     PartialCompressedCase { offset: u64, case_ofs: u64 },
110
111     #[error("At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a string was found where a number was expected.")]
112     CompressedNumberExpected { offset: u64, case_ofs: u64 },
113
114     #[error("At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a number was found where a string was expected.")]
115     CompressedStringExpected { offset: u64, case_ofs: u64 },
116
117     #[error("Block count {n_blocks} in ZLIB trailer at offset {offset:#x} differs from expected block count {expected_n_blocks} calculated from trailer length {ztrailer_len}.")]
118     BadZlibTrailerNBlocks {
119         offset: u64,
120         n_blocks: u32,
121         expected_n_blocks: u64,
122         ztrailer_len: u64,
123     },
124
125     #[error("At offset {offset:#x}, {record} has bad size {size} bytes instead of the expected {expected_size}.")]
126     BadRecordSize {
127         offset: u64,
128         record: String,
129         size: u32,
130         expected_size: u32,
131     },
132
133     #[error("At offset {offset:#x}, {record} has bad count {count} instead of the expected {expected_count}.")]
134     BadRecordCount {
135         offset: u64,
136         record: String,
137         count: u32,
138         expected_count: u32,
139     },
140
141     #[error("In long string missing values record starting at offset {record_offset:#x}, value length at offset {offset:#x} is {value_len} instead of the expected 8.")]
142     BadLongMissingValueLength {
143         record_offset: u64,
144         offset: u64,
145         value_len: u32,
146     },
147
148     #[error("The encoding record at offset {offset:#x} contains an encoding name that is not valid UTF-8.")]
149     BadEncodingName { offset: u64 },
150
151     // XXX This is risky because `text` might be arbitarily long.
152     #[error("Text string contains invalid bytes for {encoding} encoding: {text}")]
153     MalformedString { encoding: String, text: String },
154
155     #[error("Details TBD")]
156     TBD,
157 }
158
159 #[derive(Clone, Debug)]
160 pub enum Record {
161     Header(HeaderRecord<RawString>),
162     Variable(VariableRecord<RawString, RawStr<8>>),
163     ValueLabel(ValueLabelRecord<RawStr<8>, RawString>),
164     Document(DocumentRecord),
165     IntegerInfo(IntegerInfoRecord),
166     FloatInfo(FloatInfoRecord),
167     VariableSets(TextRecord),
168     VarDisplay(VarDisplayRecord),
169     MultipleResponse(MultipleResponseRecord),
170     LongStringValueLabels(LongStringValueLabelRecord),
171     LongStringMissingValues(LongStringMissingValueRecord),
172     Encoding(EncodingRecord),
173     NumberOfCases(NumberOfCasesRecord),
174     ProductInfo(TextRecord),
175     LongNames(TextRecord),
176     VeryLongStrings(TextRecord),
177     FileAttributes(TextRecord),
178     VariableAttributes(TextRecord),
179     OtherExtension(Extension),
180     EndOfHeaders(u32),
181     ZHeader(ZHeader),
182     ZTrailer(ZTrailer),
183     Cases(Rc<RefCell<Cases>>),
184 }
185
186 impl Record {
187     fn read<R>(
188         reader: &mut R,
189         endian: Endian,
190         var_types: &[VarType],
191         warn: &Box<dyn Fn(Error)>,
192     ) -> Result<Option<Record>, Error>
193     where
194         R: Read + Seek,
195     {
196         let rec_type: u32 = endian.parse(read_bytes(reader)?);
197         match rec_type {
198             2 => Ok(Some(VariableRecord::read(reader, endian)?)),
199             3 => Ok(ValueLabelRecord::read(reader, endian, var_types, warn)?),
200             6 => Ok(Some(DocumentRecord::read(reader, endian)?)),
201             7 => Extension::read(reader, endian, warn),
202             999 => Ok(Some(Record::EndOfHeaders(
203                 endian.parse(read_bytes(reader)?),
204             ))),
205             _ => Err(Error::BadRecordType {
206                 offset: reader.stream_position()?,
207                 rec_type,
208             }),
209         }
210     }
211 }
212
213 // If `s` is valid UTF-8, returns it decoded as UTF-8, otherwise returns it
214 // decoded as Latin-1 (actually bytes interpreted as Unicode code points).
215 fn default_decode(s: &[u8]) -> Cow<str> {
216     from_utf8(s).map_or_else(|_| decode_latin1(s), Cow::from)
217 }
218
219 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
220 pub enum Compression {
221     Simple,
222     ZLib,
223 }
224
225 trait Header {
226     fn offsets(&self) -> Range<u64>;
227 }
228
229 #[derive(Clone)]
230 pub struct HeaderRecord<S>
231 where
232     S: Debug,
233 {
234     /// Offset in file.
235     pub offsets: Range<u64>,
236
237     /// Magic number.
238     pub magic: Magic,
239
240     /// Eye-catcher string, product name, in the file's encoding.  Padded
241     /// on the right with spaces.
242     pub eye_catcher: S,
243
244     /// Layout code, normally either 2 or 3.
245     pub layout_code: u32,
246
247     /// Number of variable positions, or `None` if the value in the file is
248     /// questionably trustworthy.
249     pub nominal_case_size: Option<u32>,
250
251     /// Compression type, if any,
252     pub compression: Option<Compression>,
253
254     /// 1-based variable index of the weight variable, or `None` if the file is
255     /// unweighted.
256     pub weight_index: Option<u32>,
257
258     /// Claimed number of cases, if known.
259     pub n_cases: Option<u32>,
260
261     /// Compression bias, usually 100.0.
262     pub bias: f64,
263
264     /// `dd mmm yy` in the file's encoding.
265     pub creation_date: S,
266
267     /// `HH:MM:SS` in the file's encoding.
268     pub creation_time: S,
269
270     /// File label, in the file's encoding.  Padded on the right with spaces.
271     pub file_label: S,
272
273     /// Endianness of the data in the file header.
274     pub endian: Endian,
275 }
276
277 impl<S> HeaderRecord<S>
278 where
279     S: Debug,
280 {
281     fn debug_field<T>(&self, f: &mut Formatter, name: &str, value: T) -> FmtResult
282     where
283         T: Debug,
284     {
285         writeln!(f, "{name:>17}: {:?}", value)
286     }
287 }
288
289 impl<S> Debug for HeaderRecord<S>
290 where
291     S: Debug,
292 {
293     fn fmt(&self, f: &mut Formatter) -> FmtResult {
294         writeln!(f, "File header record:")?;
295         self.debug_field(f, "Magic", self.magic)?;
296         self.debug_field(f, "Product name", &self.eye_catcher)?;
297         self.debug_field(f, "Layout code", self.layout_code)?;
298         self.debug_field(f, "Nominal case size", self.nominal_case_size)?;
299         self.debug_field(f, "Compression", self.compression)?;
300         self.debug_field(f, "Weight index", self.weight_index)?;
301         self.debug_field(f, "Number of cases", self.n_cases)?;
302         self.debug_field(f, "Compression bias", self.bias)?;
303         self.debug_field(f, "Creation date", &self.creation_date)?;
304         self.debug_field(f, "Creation time", &self.creation_time)?;
305         self.debug_field(f, "File label", &self.file_label)?;
306         self.debug_field(f, "Endianness", self.endian)
307     }
308 }
309
310 impl HeaderRecord<RawString> {
311     fn read<R: Read + Seek>(r: &mut R) -> Result<Self, Error> {
312         let start = r.stream_position()?;
313
314         let magic: [u8; 4] = read_bytes(r)?;
315         let magic: Magic = magic.try_into().map_err(|_| Error::NotASystemFile)?;
316
317         let eye_catcher = RawString(read_vec(r, 60)?);
318         let layout_code: [u8; 4] = read_bytes(r)?;
319         let endian = Endian::identify_u32(2, layout_code)
320             .or_else(|| Endian::identify_u32(2, layout_code))
321             .ok_or_else(|| Error::NotASystemFile)?;
322         let layout_code = endian.parse(layout_code);
323
324         let nominal_case_size: u32 = endian.parse(read_bytes(r)?);
325         let nominal_case_size =
326             (nominal_case_size <= i32::MAX as u32 / 16).then_some(nominal_case_size);
327
328         let compression_code: u32 = endian.parse(read_bytes(r)?);
329         let compression = match (magic, compression_code) {
330             (Magic::Zsav, 2) => Some(Compression::ZLib),
331             (Magic::Zsav, code) => return Err(Error::InvalidZsavCompression(code)),
332             (_, 0) => None,
333             (_, 1) => Some(Compression::Simple),
334             (_, code) => return Err(Error::InvalidSavCompression(code)),
335         };
336
337         let weight_index: u32 = endian.parse(read_bytes(r)?);
338         let weight_index = (weight_index > 0).then_some(weight_index);
339
340         let n_cases: u32 = endian.parse(read_bytes(r)?);
341         let n_cases = (n_cases < i32::MAX as u32 / 2).then_some(n_cases);
342
343         let bias: f64 = endian.parse(read_bytes(r)?);
344
345         let creation_date = RawString(read_vec(r, 9)?);
346         let creation_time = RawString(read_vec(r, 8)?);
347         let file_label = RawString(read_vec(r, 64)?);
348         let _: [u8; 3] = read_bytes(r)?;
349
350         Ok(HeaderRecord {
351             offsets: start..r.stream_position()?,
352             magic,
353             layout_code,
354             nominal_case_size,
355             compression,
356             weight_index,
357             n_cases,
358             bias,
359             creation_date,
360             creation_time,
361             eye_catcher,
362             file_label,
363             endian,
364         })
365     }
366
367     fn decode<'a>(&'a self, decoder: &Decoder) -> HeaderRecord<Cow<'a, str>> {
368         let eye_catcher = decoder.decode(&self.eye_catcher);
369         let file_label = decoder.decode(&self.file_label);
370         let creation_date = decoder.decode(&self.creation_date);
371         let creation_time = decoder.decode(&self.creation_time);
372         HeaderRecord {
373             eye_catcher,
374             weight_index: self.weight_index,
375             n_cases: self.n_cases,
376             file_label,
377             offsets: self.offsets.clone(),
378             magic: self.magic,
379             layout_code: self.layout_code,
380             nominal_case_size: self.nominal_case_size,
381             compression: self.compression,
382             bias: self.bias,
383             creation_date,
384             creation_time,
385             endian: self.endian,
386         }
387     }
388 }
389
390 struct Decoder {
391     encoding: &'static Encoding,
392     warn: Box<dyn Fn(Error)>,
393 }
394
395 impl Decoder {
396     fn decode<'a>(&self, input: &'a RawString) -> Cow<'a, str> {
397         let (output, malformed) = self.encoding.decode_without_bom_handling(&input.0);
398         if malformed {
399             (self.warn)(Error::MalformedString {
400                 encoding: self.encoding.name().into(),
401                 text: output.clone().into(),
402             });
403         }
404         output
405     }
406
407     /// Returns `input` decoded from `self.encoding` into UTF-8 such that
408     /// re-encoding the result back into `self.encoding` will have exactly the
409     /// same length in bytes.
410     ///
411     /// XXX warn about errors?
412     fn decode_exact_length<'a>(&self, input: &'a [u8]) -> Cow<'a, str> {
413         if let (s, false) = self.encoding.decode_without_bom_handling(input) {
414             // This is the common case.  Usually there will be no errors.
415             s
416         } else {
417             // Unusual case.  Don't bother to optimize it much.
418             let mut decoder = self.encoding.new_decoder_without_bom_handling();
419             let mut output = String::with_capacity(
420                 decoder
421                     .max_utf8_buffer_length_without_replacement(input.len())
422                     .unwrap(),
423             );
424             let mut rest = input;
425             while !rest.is_empty() {
426                 match decoder.decode_to_string_without_replacement(rest, &mut output, true) {
427                     (DecoderResult::InputEmpty, _) => break,
428                     (DecoderResult::OutputFull, _) => unreachable!(),
429                     (DecoderResult::Malformed(a, b), consumed) => {
430                         let skipped = a as usize + b as usize;
431                         output.extend(repeat('?').take(skipped));
432                         rest = &rest[consumed..];
433                     }
434                 }
435             }
436             assert_eq!(self.encoding.encode(&output).0.len(), input.len());
437             output.into()
438         }
439     }
440 }
441
442 impl<S> Header for HeaderRecord<S>
443 where
444     S: Debug,
445 {
446     fn offsets(&self) -> Range<u64> {
447         self.offsets.clone()
448     }
449 }
450
451 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
452 pub enum Magic {
453     /// Regular system file.
454     Sav,
455
456     /// System file with Zlib-compressed data.
457     Zsav,
458
459     /// EBCDIC-encoded system file.
460     Ebcdic,
461 }
462
463 impl Magic {
464     /// Magic number for a regular system file.
465     pub const SAV: [u8; 4] = *b"$FL2";
466
467     /// Magic number for a system file that contains zlib-compressed data.
468     pub const ZSAV: [u8; 4] = *b"$FL3";
469
470     /// Magic number for an EBCDIC-encoded system file.  This is `$FL2` encoded
471     /// in EBCDIC.
472     pub const EBCDIC: [u8; 4] = [0x5b, 0xc6, 0xd3, 0xf2];
473 }
474
475 impl Debug for Magic {
476     fn fmt(&self, f: &mut Formatter) -> FmtResult {
477         let s = match *self {
478             Magic::Sav => "$FL2",
479             Magic::Zsav => "$FL3",
480             Magic::Ebcdic => "($FL2 in EBCDIC)",
481         };
482         write!(f, "{s}")
483     }
484 }
485
486 impl TryFrom<[u8; 4]> for Magic {
487     type Error = Error;
488
489     fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
490         match value {
491             Magic::SAV => Ok(Magic::Sav),
492             Magic::ZSAV => Ok(Magic::Zsav),
493             Magic::EBCDIC => Ok(Magic::Ebcdic),
494             _ => Err(Error::BadMagic(value)),
495         }
496     }
497 }
498
499 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
500 pub enum VarType {
501     Numeric,
502     String,
503 }
504
505 impl VarType {
506     fn from_width(width: i32) -> VarType {
507         match width {
508             0 => VarType::Numeric,
509             _ => VarType::String,
510         }
511     }
512
513     fn opposite(self) -> VarType {
514         match self {
515             Self::Numeric => Self::String,
516             Self::String => Self::Numeric,
517         }
518     }
519 }
520
521 impl Display for VarType {
522     fn fmt(&self, f: &mut Formatter) -> FmtResult {
523         match self {
524             VarType::Numeric => write!(f, "numeric"),
525             VarType::String => write!(f, "string"),
526         }
527     }
528 }
529
530 #[derive(Copy, Clone)]
531 pub enum Value<S>
532 where
533     S: Debug,
534 {
535     Number(Option<f64>),
536     String(S),
537 }
538
539 type RawValue = Value<RawStr<8>>;
540
541 impl<S> Debug for Value<S>
542 where
543     S: Debug,
544 {
545     fn fmt(&self, f: &mut Formatter) -> FmtResult {
546         match self {
547             Value::Number(Some(number)) => write!(f, "{number:?}"),
548             Value::Number(None) => write!(f, "SYSMIS"),
549             Value::String(s) => write!(f, "{:?}", s),
550         }
551     }
552 }
553
554 impl RawValue {
555     fn read<R: Read>(r: &mut R, var_type: VarType, endian: Endian) -> Result<Self, IoError> {
556         Ok(Self::from_raw(
557             &UntypedValue(read_bytes(r)?),
558             var_type,
559             endian,
560         ))
561     }
562
563     pub fn from_raw(raw: &UntypedValue, var_type: VarType, endian: Endian) -> Self {
564         match var_type {
565             VarType::String => Value::String(RawStr(raw.0)),
566             VarType::Numeric => {
567                 let number: f64 = endian.parse(raw.0);
568                 Value::Number((number != -f64::MAX).then_some(number))
569             }
570         }
571     }
572
573     fn read_case<R: Read + Seek>(
574         reader: &mut R,
575         var_types: &[VarType],
576         endian: Endian,
577     ) -> Result<Option<Vec<Self>>, Error> {
578         let case_start = reader.stream_position()?;
579         let mut values = Vec::with_capacity(var_types.len());
580         for (i, &var_type) in var_types.iter().enumerate() {
581             let Some(raw) = try_read_bytes(reader)? else {
582                 if i == 0 {
583                     return Ok(None);
584                 } else {
585                     let offset = reader.stream_position()?;
586                     return Err(Error::EofInCase {
587                         offset,
588                         case_ofs: offset - case_start,
589                         case_len: var_types.len() * 8,
590                     });
591                 }
592             };
593             values.push(Value::from_raw(&UntypedValue(raw), var_type, endian));
594         }
595         Ok(Some(values))
596     }
597
598     fn read_compressed_case<R: Read + Seek>(
599         reader: &mut R,
600         var_types: &[VarType],
601         codes: &mut VecDeque<u8>,
602         endian: Endian,
603         bias: f64,
604     ) -> Result<Option<Vec<Self>>, Error> {
605         let case_start = reader.stream_position()?;
606         let mut values = Vec::with_capacity(var_types.len());
607         for (i, &var_type) in var_types.iter().enumerate() {
608             let value = loop {
609                 let Some(code) = codes.pop_front() else {
610                     let Some(new_codes): Option<[u8; 8]> = try_read_bytes(reader)? else {
611                         if i == 0 {
612                             return Ok(None);
613                         } else {
614                             let offset = reader.stream_position()?;
615                             return Err(Error::EofInCompressedCase {
616                                 offset,
617                                 case_ofs: offset - case_start,
618                             });
619                         }
620                     };
621                     codes.extend(new_codes.into_iter());
622                     continue;
623                 };
624                 match code {
625                     0 => (),
626                     1..=251 => match var_type {
627                         VarType::Numeric => break Self::Number(Some(code as f64 - bias)),
628                         VarType::String => {
629                             break Self::String(RawStr(endian.to_bytes(code as f64 - bias)))
630                         }
631                     },
632                     252 => {
633                         if i == 0 {
634                             return Ok(None);
635                         } else {
636                             let offset = reader.stream_position()?;
637                             return Err(Error::PartialCompressedCase {
638                                 offset,
639                                 case_ofs: offset - case_start,
640                             });
641                         }
642                     }
643                     253 => {
644                         break Self::from_raw(&UntypedValue(read_bytes(reader)?), var_type, endian)
645                     }
646                     254 => match var_type {
647                         VarType::String => break Self::String(RawStr(*b"        ")), // XXX EBCDIC
648                         VarType::Numeric => {
649                             return Err(Error::CompressedStringExpected {
650                                 offset: case_start,
651                                 case_ofs: reader.stream_position()? - case_start,
652                             })
653                         }
654                     },
655                     255 => match var_type {
656                         VarType::Numeric => break Self::Number(None),
657                         VarType::String => {
658                             return Err(Error::CompressedNumberExpected {
659                                 offset: case_start,
660                                 case_ofs: reader.stream_position()? - case_start,
661                             })
662                         }
663                     },
664                 }
665             };
666             values.push(value);
667         }
668         Ok(Some(values))
669     }
670
671     fn decode(&self, decoder: &Decoder) -> Value<String> {
672         match self {
673             Self::Number(x) => Value::Number(*x),
674             Self::String(s) => Value::String(decoder.decode_exact_length(&s.0).into()),
675         }
676     }
677 }
678
679 struct ZlibDecodeMultiple<R>
680 where
681     R: Read + Seek,
682 {
683     reader: Option<ZlibDecoder<R>>,
684 }
685
686 impl<R> ZlibDecodeMultiple<R>
687 where
688     R: Read + Seek,
689 {
690     fn new(reader: R) -> ZlibDecodeMultiple<R> {
691         ZlibDecodeMultiple {
692             reader: Some(ZlibDecoder::new(reader)),
693         }
694     }
695 }
696
697 impl<R> Read for ZlibDecodeMultiple<R>
698 where
699     R: Read + Seek,
700 {
701     fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
702         loop {
703             match self.reader.as_mut().unwrap().read(buf)? {
704                 0 => {
705                     let inner = self.reader.take().unwrap().into_inner();
706                     self.reader = Some(ZlibDecoder::new(inner));
707                 }
708                 n => return Ok(n),
709             };
710         }
711     }
712 }
713
714 impl<R> Seek for ZlibDecodeMultiple<R>
715 where
716     R: Read + Seek,
717 {
718     fn seek(&mut self, pos: SeekFrom) -> Result<u64, IoError> {
719         self.reader.as_mut().unwrap().get_mut().seek(pos)
720     }
721 }
722
723 enum ReaderState {
724     Start,
725     Headers,
726     ZlibHeader,
727     ZlibTrailer {
728         ztrailer_offset: u64,
729         ztrailer_len: u64,
730     },
731     Cases,
732     End,
733 }
734
735 pub struct Reader<R>
736 where
737     R: Read + Seek + 'static,
738 {
739     reader: Option<R>,
740     warn: Box<dyn Fn(Error)>,
741
742     header: HeaderRecord<RawString>,
743     var_types: Vec<VarType>,
744
745     state: ReaderState,
746 }
747
748 impl<R> Reader<R>
749 where
750     R: Read + Seek + 'static,
751 {
752     pub fn new<F>(mut reader: R, warn: F) -> Result<Self, Error>
753     where
754         F: Fn(Error) + 'static,
755     {
756         let header = HeaderRecord::read(&mut reader)?;
757         Ok(Self {
758             reader: Some(reader),
759             warn: Box::new(warn),
760             header,
761             var_types: Vec::new(),
762             state: ReaderState::Start,
763         })
764     }
765     fn cases(&mut self) -> Cases {
766         self.state = ReaderState::End;
767         Cases::new(
768             self.reader.take().unwrap(),
769             take(&mut self.var_types),
770             &self.header,
771         )
772     }
773 }
774
775 impl<R> Iterator for Reader<R>
776 where
777     R: Read + Seek + 'static,
778 {
779     type Item = Result<Record, Error>;
780
781     fn next(&mut self) -> Option<Self::Item> {
782         match self.state {
783             ReaderState::Start => {
784                 self.state = ReaderState::Headers;
785                 Some(Ok(Record::Header(self.header.clone())))
786             }
787             ReaderState::Headers => {
788                 let record = loop {
789                     match Record::read(
790                         self.reader.as_mut().unwrap(),
791                         self.header.endian,
792                         self.var_types.as_slice(),
793                         &self.warn,
794                     ) {
795                         Ok(Some(record)) => break record,
796                         Ok(None) => (),
797                         Err(error) => return Some(Err(error)),
798                     }
799                 };
800                 match record {
801                     Record::Variable(VariableRecord { width, .. }) => {
802                         self.var_types.push(VarType::from_width(width));
803                     }
804                     Record::EndOfHeaders(_) => {
805                         self.state = if let Some(Compression::ZLib) = self.header.compression {
806                             ReaderState::ZlibHeader
807                         } else {
808                             ReaderState::Cases
809                         };
810                     }
811                     _ => (),
812                 };
813                 Some(Ok(record))
814             }
815             ReaderState::ZlibHeader => {
816                 let zheader = match ZHeader::read(self.reader.as_mut().unwrap(), self.header.endian)
817                 {
818                     Ok(zheader) => zheader,
819                     Err(error) => return Some(Err(error)),
820                 };
821                 self.state = ReaderState::ZlibTrailer {
822                     ztrailer_offset: zheader.ztrailer_offset,
823                     ztrailer_len: zheader.ztrailer_len,
824                 };
825                 Some(Ok(Record::ZHeader(zheader)))
826             }
827             ReaderState::ZlibTrailer {
828                 ztrailer_offset,
829                 ztrailer_len,
830             } => {
831                 match ZTrailer::read(
832                     self.reader.as_mut().unwrap(),
833                     self.header.endian,
834                     ztrailer_offset,
835                     ztrailer_len,
836                 ) {
837                     Ok(None) => Some(Ok(Record::Cases(Rc::new(RefCell::new(self.cases()))))),
838                     Ok(Some(ztrailer)) => Some(Ok(Record::ZTrailer(ztrailer))),
839                     Err(error) => Some(Err(error)),
840                 }
841             }
842             ReaderState::Cases => Some(Ok(Record::Cases(Rc::new(RefCell::new(self.cases()))))),
843             ReaderState::End => None,
844         }
845     }
846 }
847
848 trait ReadSeek: Read + Seek {}
849 impl<T> ReadSeek for T where T: Read + Seek {}
850
851 pub struct Cases {
852     reader: Box<dyn ReadSeek>,
853     var_types: Vec<VarType>,
854     compression: Option<Compression>,
855     bias: f64,
856     endian: Endian,
857     codes: VecDeque<u8>,
858     eof: bool,
859 }
860
861 impl Debug for Cases {
862     fn fmt(&self, f: &mut Formatter) -> FmtResult {
863         write!(f, "Cases")
864     }
865 }
866
867 impl Cases {
868     fn new<R>(reader: R, var_types: Vec<VarType>, header: &HeaderRecord<RawString>) -> Self
869     where
870         R: Read + Seek + 'static,
871     {
872         Self {
873             reader: if header.compression == Some(Compression::ZLib) {
874                 Box::new(ZlibDecodeMultiple::new(reader))
875             } else {
876                 Box::new(reader)
877             },
878             var_types,
879             compression: header.compression,
880             bias: header.bias,
881             endian: header.endian,
882             codes: VecDeque::with_capacity(8),
883             eof: false,
884         }
885     }
886 }
887
888 impl Iterator for Cases {
889     type Item = Result<Vec<RawValue>, Error>;
890
891     fn next(&mut self) -> Option<Self::Item> {
892         if self.eof {
893             return None;
894         }
895
896         let retval = if self.compression.is_some() {
897             Value::read_compressed_case(
898                 &mut self.reader,
899                 &self.var_types,
900                 &mut self.codes,
901                 self.endian,
902                 self.bias,
903             )
904             .transpose()
905         } else {
906             Value::read_case(&mut self.reader, &self.var_types, self.endian).transpose()
907         };
908         self.eof = matches!(retval, None | Some(Err(_)));
909         retval
910     }
911 }
912
913 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
914 pub struct Spec(pub u32);
915
916 impl Debug for Spec {
917     fn fmt(&self, f: &mut Formatter) -> FmtResult {
918         let type_ = format_name(self.0 >> 16);
919         let w = (self.0 >> 8) & 0xff;
920         let d = self.0 & 0xff;
921         write!(f, "{:06x} ({type_}{w}.{d})", self.0)
922     }
923 }
924
925 fn format_name(type_: u32) -> Cow<'static, str> {
926     match type_ {
927         1 => "A",
928         2 => "AHEX",
929         3 => "COMMA",
930         4 => "DOLLAR",
931         5 => "F",
932         6 => "IB",
933         7 => "PIBHEX",
934         8 => "P",
935         9 => "PIB",
936         10 => "PK",
937         11 => "RB",
938         12 => "RBHEX",
939         15 => "Z",
940         16 => "N",
941         17 => "E",
942         20 => "DATE",
943         21 => "TIME",
944         22 => "DATETIME",
945         23 => "ADATE",
946         24 => "JDATE",
947         25 => "DTIME",
948         26 => "WKDAY",
949         27 => "MONTH",
950         28 => "MOYR",
951         29 => "QYR",
952         30 => "WKYR",
953         31 => "PCT",
954         32 => "DOT",
955         33 => "CCA",
956         34 => "CCB",
957         35 => "CCC",
958         36 => "CCD",
959         37 => "CCE",
960         38 => "EDATE",
961         39 => "SDATE",
962         40 => "MTIME",
963         41 => "YMDHMS",
964         _ => return format!("<unknown format {type_}>").into(),
965     }
966     .into()
967 }
968
969 #[derive(Clone)]
970 pub struct MissingValues<S>
971 where
972     S: Debug,
973 {
974     /// Individual missing values, up to 3 of them.
975     pub values: Vec<Value<S>>,
976
977     /// Optional range of missing values.
978     pub range: Option<(Value<S>, Value<S>)>,
979 }
980
981 impl<S> Debug for MissingValues<S>
982 where
983     S: Debug,
984 {
985     fn fmt(&self, f: &mut Formatter) -> FmtResult {
986         for (i, value) in self.values.iter().enumerate() {
987             if i > 0 {
988                 write!(f, ", ")?;
989             }
990             write!(f, "{value:?}")?;
991         }
992
993         if let Some((low, high)) = &self.range {
994             if !self.values.is_empty() {
995                 write!(f, ", ")?;
996             }
997             write!(f, "{low:?} THRU {high:?}")?;
998         }
999
1000         if self.is_empty() {
1001             write!(f, "none")?;
1002         }
1003
1004         Ok(())
1005     }
1006 }
1007
1008 impl<S> MissingValues<S>
1009 where
1010     S: Debug,
1011 {
1012     fn is_empty(&self) -> bool {
1013         self.values.is_empty() && self.range.is_none()
1014     }
1015 }
1016
1017 impl MissingValues<RawStr<8>> {
1018     fn read<R: Read + Seek>(
1019         r: &mut R,
1020         offset: u64,
1021         width: i32,
1022         code: i32,
1023         endian: Endian,
1024     ) -> Result<Self, Error> {
1025         let (n_values, has_range) = match (width, code) {
1026             (_, 0..=3) => (code, false),
1027             (0, -2) => (0, true),
1028             (0, -3) => (1, true),
1029             (0, _) => return Err(Error::BadNumericMissingValueCode { offset, code }),
1030             (_, _) => return Err(Error::BadStringMissingValueCode { offset, code }),
1031         };
1032
1033         let var_type = VarType::from_width(width);
1034
1035         let mut values = Vec::new();
1036         for _ in 0..n_values {
1037             values.push(RawValue::read(r, var_type, endian)?);
1038         }
1039         let range = if has_range {
1040             let low = RawValue::read(r, var_type, endian)?;
1041             let high = RawValue::read(r, var_type, endian)?;
1042             Some((low, high))
1043         } else {
1044             None
1045         };
1046         Ok(Self { values, range })
1047     }
1048     fn decode<'a>(&'a self, decoder: &Decoder) -> MissingValues<String> {
1049         MissingValues {
1050             values: self
1051                 .values
1052                 .iter()
1053                 .map(|value| value.decode(decoder))
1054                 .collect(),
1055             range: self
1056                 .range
1057                 .as_ref()
1058                 .map(|(low, high)| (low.decode(decoder), high.decode(decoder))),
1059         }
1060     }
1061 }
1062
1063 #[derive(Clone)]
1064 pub struct VariableRecord<S, V>
1065 where
1066     S: Debug,
1067     V: Debug,
1068 {
1069     /// Range of offsets in file.
1070     pub offsets: Range<u64>,
1071
1072     /// Variable width, in the range -1..=255.
1073     pub width: i32,
1074
1075     /// Variable name, padded on the right with spaces.
1076     pub name: S,
1077
1078     /// Print format.
1079     pub print_format: Spec,
1080
1081     /// Write format.
1082     pub write_format: Spec,
1083
1084     /// Missing values.
1085     pub missing_values: MissingValues<V>,
1086
1087     /// Optional variable label.
1088     pub label: Option<S>,
1089 }
1090
1091 impl<S, V> Debug for VariableRecord<S, V>
1092 where
1093     S: Debug,
1094     V: Debug,
1095 {
1096     fn fmt(&self, f: &mut Formatter) -> FmtResult {
1097         writeln!(
1098             f,
1099             "Width: {} ({})",
1100             self.width,
1101             match self.width.cmp(&0) {
1102                 Ordering::Greater => "string",
1103                 Ordering::Equal => "numeric",
1104                 Ordering::Less => "long string continuation record",
1105             }
1106         )?;
1107         writeln!(f, "Print format: {:?}", self.print_format)?;
1108         writeln!(f, "Write format: {:?}", self.write_format)?;
1109         writeln!(f, "Name: {:?}", &self.name)?;
1110         writeln!(f, "Variable label: {:?}", self.label)?;
1111         writeln!(f, "Missing values: {:?}", self.missing_values)
1112     }
1113 }
1114
1115 impl VariableRecord<RawString, RawStr<8>> {
1116     fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<Record, Error> {
1117         let start_offset = r.stream_position()?;
1118         let width: i32 = endian.parse(read_bytes(r)?);
1119         let code_offset = r.stream_position()?;
1120         let has_variable_label: u32 = endian.parse(read_bytes(r)?);
1121         let missing_value_code: i32 = endian.parse(read_bytes(r)?);
1122         let print_format = Spec(endian.parse(read_bytes(r)?));
1123         let write_format = Spec(endian.parse(read_bytes(r)?));
1124         let name = RawString(read_vec(r, 8)?);
1125
1126         let label = match has_variable_label {
1127             0 => None,
1128             1 => {
1129                 let len: u32 = endian.parse(read_bytes(r)?);
1130                 let read_len = len.min(65535) as usize;
1131                 let label = RawString(read_vec(r, read_len)?);
1132
1133                 let padding_bytes = Integer::next_multiple_of(&len, &4) - len;
1134                 let _ = read_vec(r, padding_bytes as usize)?;
1135
1136                 Some(label)
1137             }
1138             _ => {
1139                 return Err(Error::BadVariableLabelCode {
1140                     start_offset,
1141                     code_offset,
1142                     code: has_variable_label,
1143                 })
1144             }
1145         };
1146
1147         let missing_values =
1148             MissingValues::read(r, start_offset, width, missing_value_code, endian)?;
1149
1150         let end_offset = r.stream_position()?;
1151
1152         Ok(Record::Variable(VariableRecord {
1153             offsets: start_offset..end_offset,
1154             width,
1155             name,
1156             print_format,
1157             write_format,
1158             missing_values,
1159             label,
1160         }))
1161     }
1162
1163     fn decode<'a>(&'a self, decoder: &Decoder) -> VariableRecord<Cow<'a, str>, String> {
1164         VariableRecord {
1165             offsets: self.offsets.clone(),
1166             width: self.width,
1167             name: decoder.decode(&self.name),
1168             print_format: self.print_format,
1169             write_format: self.write_format,
1170             missing_values: self.missing_values.decode(decoder),
1171             label: self.label.as_ref().map(|label| decoder.decode(label)),
1172         }
1173     }
1174 }
1175
1176 #[derive(Copy, Clone)]
1177 pub struct UntypedValue(pub [u8; 8]);
1178
1179 impl Debug for UntypedValue {
1180     fn fmt(&self, f: &mut Formatter) -> FmtResult {
1181         let little: f64 = Endian::Little.parse(self.0);
1182         let little = format!("{:?}", little);
1183         let big: f64 = Endian::Big.parse(self.0);
1184         let big = format!("{:?}", big);
1185         let number = if little.len() <= big.len() {
1186             little
1187         } else {
1188             big
1189         };
1190         write!(f, "{number}")?;
1191
1192         let string = default_decode(&self.0);
1193         let string = string
1194             .split(|c: char| c == '\0' || c.is_control())
1195             .next()
1196             .unwrap();
1197         write!(f, "{string:?}")?;
1198         Ok(())
1199     }
1200 }
1201
1202 #[derive(Clone)]
1203 pub struct RawString(pub Vec<u8>);
1204
1205 impl From<Vec<u8>> for RawString {
1206     fn from(source: Vec<u8>) -> Self {
1207         Self(source)
1208     }
1209 }
1210
1211 impl From<&[u8]> for RawString {
1212     fn from(source: &[u8]) -> Self {
1213         Self(source.into())
1214     }
1215 }
1216
1217 impl Debug for RawString {
1218     fn fmt(&self, f: &mut Formatter) -> FmtResult {
1219         write!(f, "{:?}", default_decode(self.0.as_slice()))
1220     }
1221 }
1222
1223 #[derive(Copy, Clone)]
1224 pub struct RawStr<const N: usize>(pub [u8; N]);
1225
1226 impl<const N: usize> From<[u8; N]> for RawStr<N> {
1227     fn from(source: [u8; N]) -> Self {
1228         Self(source)
1229     }
1230 }
1231
1232 impl<const N: usize> Debug for RawStr<N> {
1233     fn fmt(&self, f: &mut Formatter) -> FmtResult {
1234         write!(f, "{:?}", default_decode(&self.0))
1235     }
1236 }
1237
1238 #[derive(Clone, Debug)]
1239 pub struct ValueLabel<V, S>
1240 where
1241     V: Debug,
1242     S: Debug,
1243 {
1244     pub value: Value<V>,
1245     pub label: S,
1246 }
1247
1248 #[derive(Clone)]
1249 pub struct ValueLabelRecord<V, S>
1250 where
1251     V: Debug,
1252     S: Debug,
1253 {
1254     /// Range of offsets in file.
1255     pub offsets: Range<u64>,
1256
1257     /// The labels.
1258     pub labels: Vec<ValueLabel<V, S>>,
1259
1260     /// The 1-based indexes of the variable indexes.
1261     pub dict_indexes: Vec<u32>,
1262
1263     /// The types of the variables.
1264     pub var_type: VarType,
1265 }
1266
1267 impl<V, S> Debug for ValueLabelRecord<V, S>
1268 where
1269     V: Debug,
1270     S: Debug,
1271 {
1272     fn fmt(&self, f: &mut Formatter) -> FmtResult {
1273         writeln!(f, "labels: ")?;
1274         for label in self.labels.iter() {
1275             writeln!(f, "{label:?}")?;
1276         }
1277         write!(f, "apply to {} variables", self.var_type)?;
1278         for dict_index in self.dict_indexes.iter() {
1279             write!(f, " #{dict_index}")?;
1280         }
1281         Ok(())
1282     }
1283 }
1284
1285 impl<V, S> Header for ValueLabelRecord<V, S>
1286 where
1287     V: Debug,
1288     S: Debug,
1289 {
1290     fn offsets(&self) -> Range<u64> {
1291         self.offsets.clone()
1292     }
1293 }
1294
1295 impl<V, S> ValueLabelRecord<V, S>
1296 where
1297     V: Debug,
1298     S: Debug,
1299 {
1300     /// Maximum number of value labels in a record.
1301     pub const MAX_LABELS: u32 = u32::MAX / 8;
1302
1303     /// Maximum number of variable indexes in a record.
1304     pub const MAX_INDEXES: u32 = u32::MAX / 8;
1305 }
1306
1307 impl ValueLabelRecord<RawStr<8>, RawString> {
1308     fn read<R: Read + Seek>(
1309         r: &mut R,
1310         endian: Endian,
1311         var_types: &[VarType],
1312         warn: &Box<dyn Fn(Error)>,
1313     ) -> Result<Option<Record>, Error> {
1314         let label_offset = r.stream_position()?;
1315         let n: u32 = endian.parse(read_bytes(r)?);
1316         if n > Self::MAX_LABELS {
1317             return Err(Error::BadNumberOfValueLabels {
1318                 offset: label_offset,
1319                 n,
1320                 max: Self::MAX_LABELS,
1321             });
1322         }
1323
1324         let mut labels = Vec::new();
1325         for _ in 0..n {
1326             let value = UntypedValue(read_bytes(r)?);
1327             let label_len: u8 = endian.parse(read_bytes(r)?);
1328             let label_len = label_len as usize;
1329             let padded_len = Integer::next_multiple_of(&(label_len + 1), &8);
1330
1331             let mut label = read_vec(r, padded_len - 1)?;
1332             label.truncate(label_len);
1333             labels.push((value, RawString(label)));
1334         }
1335
1336         let index_offset = r.stream_position()?;
1337         let rec_type: u32 = endian.parse(read_bytes(r)?);
1338         if rec_type != 4 {
1339             return Err(Error::ExpectedVarIndexRecord {
1340                 offset: index_offset,
1341                 rec_type,
1342             });
1343         }
1344
1345         let n: u32 = endian.parse(read_bytes(r)?);
1346         if n > Self::MAX_INDEXES {
1347             return Err(Error::TooManyVarIndexes {
1348                 offset: index_offset,
1349                 n,
1350                 max: Self::MAX_INDEXES,
1351             });
1352         }
1353
1354         let index_offset = r.stream_position()?;
1355         let mut dict_indexes = Vec::with_capacity(n as usize);
1356         let mut invalid_indexes = Vec::new();
1357         for _ in 0..n {
1358             let index: u32 = endian.parse(read_bytes(r)?);
1359             if index == 0 || index as usize > var_types.len() {
1360                 dict_indexes.push(index);
1361             } else {
1362                 invalid_indexes.push(index);
1363             }
1364         }
1365         if !invalid_indexes.is_empty() {
1366             warn(Error::InvalidVarIndexes {
1367                 offset: index_offset,
1368                 max: var_types.len(),
1369                 invalid: invalid_indexes,
1370             });
1371         }
1372
1373         let Some(&first_index) = dict_indexes.first() else {
1374             warn(Error::NoVarIndexes {
1375                 offset: index_offset,
1376             });
1377             return Ok(None);
1378         };
1379         let var_type = var_types[first_index as usize - 1];
1380         let mut wrong_type_indexes = Vec::new();
1381         dict_indexes.retain(|&index| {
1382             if var_types[index as usize - 1] != var_type {
1383                 wrong_type_indexes.push(index);
1384                 false
1385             } else {
1386                 true
1387             }
1388         });
1389         if !wrong_type_indexes.is_empty() {
1390             warn(Error::MixedVarTypes {
1391                 offset: index_offset,
1392                 var_type,
1393                 wrong_types: wrong_type_indexes,
1394             });
1395         }
1396
1397         let labels = labels
1398             .into_iter()
1399             .map(|(value, label)| ValueLabel {
1400                 value: Value::from_raw(&value, var_type, endian),
1401                 label,
1402             })
1403             .collect();
1404
1405         let end_offset = r.stream_position()?;
1406         Ok(Some(Record::ValueLabel(ValueLabelRecord {
1407             offsets: label_offset..end_offset,
1408             labels,
1409             dict_indexes,
1410             var_type,
1411         })))
1412     }
1413 }
1414
1415 #[derive(Clone, Debug)]
1416 pub struct DocumentRecord {
1417     pub offsets: Range<u64>,
1418
1419     /// The document, as an array of 80-byte lines.
1420     pub lines: Vec<DocumentLine>,
1421 }
1422
1423 pub type DocumentLine = RawStr<{ DocumentRecord::LINE_LEN }>;
1424
1425 impl DocumentRecord {
1426     /// Length of a line in a document.  Document lines are fixed-length and
1427     /// padded on the right with spaces.
1428     pub const LINE_LEN: usize = 80;
1429
1430     /// Maximum number of lines we will accept in a document.  This is simply
1431     /// the maximum number that will fit in a 32-bit space.
1432     pub const MAX_LINES: usize = i32::MAX as usize / Self::LINE_LEN;
1433
1434     fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<Record, Error> {
1435         let start_offset = r.stream_position()?;
1436         let n: u32 = endian.parse(read_bytes(r)?);
1437         let n = n as usize;
1438         if n > Self::MAX_LINES {
1439             Err(Error::BadDocumentLength {
1440                 offset: start_offset,
1441                 n,
1442                 max: Self::MAX_LINES,
1443             })
1444         } else {
1445             let mut lines = Vec::with_capacity(n);
1446             for _ in 0..n {
1447                 lines.push(RawStr::<{ DocumentRecord::LINE_LEN }>(read_bytes(r)?));
1448             }
1449             let end_offset = r.stream_position()?;
1450             Ok(Record::Document(DocumentRecord {
1451                 offsets: start_offset..end_offset,
1452                 lines,
1453             }))
1454         }
1455     }
1456 }
1457
1458 impl Header for DocumentRecord {
1459     fn offsets(&self) -> Range<u64> {
1460         self.offsets.clone()
1461     }
1462 }
1463
1464 trait ExtensionRecord {
1465     const SUBTYPE: u32;
1466     const SIZE: Option<u32>;
1467     const COUNT: Option<u32>;
1468     const NAME: &'static str;
1469     fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error>;
1470 }
1471
1472 #[derive(Clone, Debug)]
1473 pub struct IntegerInfoRecord {
1474     pub offsets: Range<u64>,
1475     pub version: (i32, i32, i32),
1476     pub machine_code: i32,
1477     pub floating_point_rep: i32,
1478     pub compression_code: i32,
1479     pub endianness: i32,
1480     pub character_code: i32,
1481 }
1482
1483 impl ExtensionRecord for IntegerInfoRecord {
1484     const SUBTYPE: u32 = 3;
1485     const SIZE: Option<u32> = Some(4);
1486     const COUNT: Option<u32> = Some(8);
1487     const NAME: &'static str = "integer record";
1488
1489     fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1490         ext.check_size::<Self>()?;
1491
1492         let mut input = &ext.data[..];
1493         let data: Vec<i32> = (0..8)
1494             .map(|_| endian.parse(read_bytes(&mut input).unwrap()))
1495             .collect();
1496         Ok(Record::IntegerInfo(IntegerInfoRecord {
1497             offsets: ext.offsets.clone(),
1498             version: (data[0], data[1], data[2]),
1499             machine_code: data[3],
1500             floating_point_rep: data[4],
1501             compression_code: data[5],
1502             endianness: data[6],
1503             character_code: data[7],
1504         }))
1505     }
1506 }
1507
1508 #[derive(Clone, Debug)]
1509 pub struct FloatInfoRecord {
1510     pub sysmis: f64,
1511     pub highest: f64,
1512     pub lowest: f64,
1513 }
1514
1515 impl ExtensionRecord for FloatInfoRecord {
1516     const SUBTYPE: u32 = 4;
1517     const SIZE: Option<u32> = Some(8);
1518     const COUNT: Option<u32> = Some(3);
1519     const NAME: &'static str = "floating point record";
1520
1521     fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1522         ext.check_size::<Self>()?;
1523
1524         let mut input = &ext.data[..];
1525         let data: Vec<f64> = (0..3)
1526             .map(|_| endian.parse(read_bytes(&mut input).unwrap()))
1527             .collect();
1528         Ok(Record::FloatInfo(FloatInfoRecord {
1529             sysmis: data[0],
1530             highest: data[1],
1531             lowest: data[2],
1532         }))
1533     }
1534 }
1535
1536 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1537 pub enum CategoryLabels {
1538     VarLabels,
1539     CountedValues,
1540 }
1541
1542 #[derive(Clone, Debug)]
1543 pub enum MultipleResponseType {
1544     MultipleDichotomy {
1545         value: RawString,
1546         labels: CategoryLabels,
1547     },
1548     MultipleCategory,
1549 }
1550
1551 impl MultipleResponseType {
1552     fn parse(input: &[u8]) -> Result<(MultipleResponseType, &[u8]), Error> {
1553         let (mr_type, input) = match input.split_first() {
1554             Some((b'C', input)) => (MultipleResponseType::MultipleCategory, input),
1555             Some((b'D', input)) => {
1556                 let (value, input) = parse_counted_string(input)?;
1557                 (
1558                     MultipleResponseType::MultipleDichotomy {
1559                         value,
1560                         labels: CategoryLabels::VarLabels,
1561                     },
1562                     input,
1563                 )
1564             }
1565             Some((b'E', input)) => {
1566                 let (labels, input) = if let Some(rest) = input.strip_prefix(b" 1 ") {
1567                     (CategoryLabels::CountedValues, rest)
1568                 } else if let Some(rest) = input.strip_prefix(b" 11 ") {
1569                     (CategoryLabels::VarLabels, rest)
1570                 } else {
1571                     return Err(Error::TBD);
1572                 };
1573                 let (value, input) = parse_counted_string(input)?;
1574                 (
1575                     MultipleResponseType::MultipleDichotomy { value, labels },
1576                     input,
1577                 )
1578             }
1579             _ => return Err(Error::TBD),
1580         };
1581         Ok((mr_type, input))
1582     }
1583 }
1584
1585 #[derive(Clone, Debug)]
1586 pub struct MultipleResponseSet {
1587     pub name: RawString,
1588     pub label: RawString,
1589     pub mr_type: MultipleResponseType,
1590     pub short_names: Vec<RawString>,
1591 }
1592
1593 impl MultipleResponseSet {
1594     fn parse(input: &[u8]) -> Result<(MultipleResponseSet, &[u8]), Error> {
1595         let Some(equals) = input.iter().position(|&b| b == b'=') else {
1596             return Err(Error::TBD);
1597         };
1598         let (name, input) = input.split_at(equals);
1599         let (mr_type, input) = MultipleResponseType::parse(input)?;
1600         let Some(input) = input.strip_prefix(b" ") else {
1601             return Err(Error::TBD);
1602         };
1603         let (label, mut input) = parse_counted_string(input)?;
1604         let mut vars = Vec::new();
1605         while input.first() != Some(&b'\n') {
1606             match input.split_first() {
1607                 Some((b' ', rest)) => {
1608                     let Some(length) = rest.iter().position(|b| b" \n".contains(b)) else {
1609                         return Err(Error::TBD);
1610                     };
1611                     let (var, rest) = rest.split_at(length);
1612                     if !var.is_empty() {
1613                         vars.push(var.into());
1614                     }
1615                     input = rest;
1616                 }
1617                 _ => return Err(Error::TBD),
1618             }
1619         }
1620         while input.first() == Some(&b'\n') {
1621             input = &input[1..];
1622         }
1623         Ok((
1624             MultipleResponseSet {
1625                 name: name.into(),
1626                 label,
1627                 mr_type,
1628                 short_names: vars,
1629             },
1630             input,
1631         ))
1632     }
1633 }
1634
1635 #[derive(Clone, Debug)]
1636 pub struct MultipleResponseRecord(pub Vec<MultipleResponseSet>);
1637
1638 impl ExtensionRecord for MultipleResponseRecord {
1639     const SUBTYPE: u32 = 7;
1640     const SIZE: Option<u32> = Some(1);
1641     const COUNT: Option<u32> = None;
1642     const NAME: &'static str = "multiple response set record";
1643
1644     fn parse(ext: &Extension, _endian: Endian) -> Result<Record, Error> {
1645         ext.check_size::<Self>()?;
1646
1647         let mut input = &ext.data[..];
1648         let mut sets = Vec::new();
1649         while !input.is_empty() {
1650             let (set, rest) = MultipleResponseSet::parse(input)?;
1651             sets.push(set);
1652             input = rest;
1653         }
1654         Ok(Record::MultipleResponse(MultipleResponseRecord(sets)))
1655     }
1656 }
1657
1658 fn parse_counted_string(input: &[u8]) -> Result<(RawString, &[u8]), Error> {
1659     let Some(space) = input.iter().position(|&b| b == b' ') else {
1660         return Err(Error::TBD);
1661     };
1662     let Ok(length) = from_utf8(&input[..space]) else {
1663         return Err(Error::TBD);
1664     };
1665     let Ok(length): Result<usize, _> = length.parse() else {
1666         return Err(Error::TBD);
1667     };
1668
1669     let input = &input[space + 1..];
1670     if input.len() < length {
1671         return Err(Error::TBD);
1672     };
1673
1674     let (string, rest) = input.split_at(length);
1675     Ok((string.into(), rest))
1676 }
1677
1678 #[derive(Clone, Debug)]
1679 pub struct VarDisplayRecord(pub Vec<u32>);
1680
1681 impl ExtensionRecord for VarDisplayRecord {
1682     const SUBTYPE: u32 = 11;
1683     const SIZE: Option<u32> = Some(4);
1684     const COUNT: Option<u32> = None;
1685     const NAME: &'static str = "variable display record";
1686
1687     fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1688         ext.check_size::<Self>()?;
1689
1690         let mut input = &ext.data[..];
1691         let display = (0..ext.count)
1692             .map(|_| endian.parse(read_bytes(&mut input).unwrap()))
1693             .collect();
1694         Ok(Record::VarDisplay(VarDisplayRecord(display)))
1695     }
1696 }
1697
1698 #[derive(Clone, Debug)]
1699 pub struct LongStringMissingValues {
1700     /// Variable name.
1701     pub var_name: RawString,
1702
1703     /// Missing values.
1704     pub missing_values: MissingValues<RawStr<8>>,
1705 }
1706
1707 #[derive(Clone, Debug)]
1708 pub struct LongStringMissingValueRecord(pub Vec<LongStringMissingValues>);
1709
1710 impl ExtensionRecord for LongStringMissingValueRecord {
1711     const SUBTYPE: u32 = 22;
1712     const SIZE: Option<u32> = Some(1);
1713     const COUNT: Option<u32> = None;
1714     const NAME: &'static str = "long string missing values record";
1715
1716     fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1717         ext.check_size::<Self>()?;
1718
1719         let mut input = &ext.data[..];
1720         let mut missing_value_set = Vec::new();
1721         while !input.is_empty() {
1722             let var_name = read_string(&mut input, endian)?;
1723             let n_missing_values: u8 = endian.parse(read_bytes(&mut input)?);
1724             let value_len: u32 = endian.parse(read_bytes(&mut input)?);
1725             if value_len != 8 {
1726                 let offset = (ext.data.len() - input.len() - 8) as u64 + ext.offsets.start;
1727                 return Err(Error::BadLongMissingValueLength {
1728                     record_offset: ext.offsets.start,
1729                     offset,
1730                     value_len,
1731                 });
1732             }
1733             let mut values = Vec::new();
1734             for i in 0..n_missing_values {
1735                 let value: [u8; 8] = read_bytes(&mut input)?;
1736                 let numeric_value: u64 = endian.parse(value);
1737                 let value = if i > 0 && numeric_value == 8 {
1738                     // Tolerate files written by old, buggy versions of PSPP
1739                     // where we believed that the value_length was repeated
1740                     // before each missing value.
1741                     read_bytes(&mut input)?
1742                 } else {
1743                     value
1744                 };
1745                 values.push(Value::String(RawStr(value)));
1746             }
1747             let missing_values = MissingValues {
1748                 values,
1749                 range: None,
1750             };
1751             missing_value_set.push(LongStringMissingValues {
1752                 var_name,
1753                 missing_values,
1754             });
1755         }
1756         Ok(Record::LongStringMissingValues(
1757             LongStringMissingValueRecord(missing_value_set),
1758         ))
1759     }
1760 }
1761
1762 #[derive(Clone, Debug)]
1763 pub struct EncodingRecord(pub String);
1764
1765 impl ExtensionRecord for EncodingRecord {
1766     const SUBTYPE: u32 = 20;
1767     const SIZE: Option<u32> = Some(1);
1768     const COUNT: Option<u32> = None;
1769     const NAME: &'static str = "encoding record";
1770
1771     fn parse(ext: &Extension, _endian: Endian) -> Result<Record, Error> {
1772         ext.check_size::<Self>()?;
1773
1774         Ok(Record::Encoding(EncodingRecord(
1775             String::from_utf8(ext.data.clone()).map_err(|_| Error::BadEncodingName {
1776                 offset: ext.offsets.start,
1777             })?,
1778         )))
1779     }
1780 }
1781
1782 #[derive(Copy, Clone, Debug)]
1783 pub struct NumberOfCasesRecord {
1784     /// Always observed as 1.
1785     pub one: u64,
1786
1787     /// Number of cases.
1788     pub n_cases: u64,
1789 }
1790
1791 impl ExtensionRecord for NumberOfCasesRecord {
1792     const SUBTYPE: u32 = 16;
1793     const SIZE: Option<u32> = Some(8);
1794     const COUNT: Option<u32> = Some(2);
1795     const NAME: &'static str = "extended number of cases record";
1796
1797     fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1798         ext.check_size::<Self>()?;
1799
1800         let mut input = &ext.data[..];
1801         let one = endian.parse(read_bytes(&mut input)?);
1802         let n_cases = endian.parse(read_bytes(&mut input)?);
1803
1804         Ok(Record::NumberOfCases(NumberOfCasesRecord { one, n_cases }))
1805     }
1806 }
1807
1808 #[derive(Clone, Debug)]
1809 pub struct TextRecord {
1810     pub offsets: Range<u64>,
1811
1812     /// The text content of the record.
1813     pub text: RawString,
1814 }
1815
1816 impl From<Extension> for TextRecord {
1817     fn from(source: Extension) -> Self {
1818         TextRecord {
1819             offsets: source.offsets,
1820             text: source.data.into(),
1821         }
1822     }
1823 }
1824
1825 #[derive(Clone, Debug)]
1826 pub struct Extension {
1827     pub offsets: Range<u64>,
1828
1829     /// Record subtype.
1830     pub subtype: u32,
1831
1832     /// Size of each data element.
1833     pub size: u32,
1834
1835     /// Number of data elements.
1836     pub count: u32,
1837
1838     /// `size * count` bytes of data.
1839     pub data: Vec<u8>,
1840 }
1841
1842 impl Extension {
1843     fn check_size<E: ExtensionRecord>(&self) -> Result<(), Error> {
1844         if let Some(expected_size) = E::SIZE {
1845             if self.size != expected_size {
1846                 return Err(Error::BadRecordSize {
1847                     offset: self.offsets.start,
1848                     record: E::NAME.into(),
1849                     size: self.size,
1850                     expected_size,
1851                 });
1852             }
1853         }
1854         if let Some(expected_count) = E::COUNT {
1855             if self.count != expected_count {
1856                 return Err(Error::BadRecordCount {
1857                     offset: self.offsets.start,
1858                     record: E::NAME.into(),
1859                     count: self.count,
1860                     expected_count,
1861                 });
1862             }
1863         }
1864         Ok(())
1865     }
1866
1867     fn read<R: Read + Seek>(
1868         r: &mut R,
1869         endian: Endian,
1870         warn: &Box<dyn Fn(Error)>,
1871     ) -> Result<Option<Record>, Error> {
1872         let subtype = endian.parse(read_bytes(r)?);
1873         let header_offset = r.stream_position()?;
1874         let size: u32 = endian.parse(read_bytes(r)?);
1875         let count = endian.parse(read_bytes(r)?);
1876         let Some(product) = size.checked_mul(count) else {
1877             return Err(Error::ExtensionRecordTooLarge {
1878                 offset: header_offset,
1879                 subtype,
1880                 size,
1881                 count,
1882             });
1883         };
1884         let start_offset = r.stream_position()?;
1885         let data = read_vec(r, product as usize)?;
1886         let end_offset = start_offset + product as u64;
1887         let extension = Extension {
1888             offsets: start_offset..end_offset,
1889             subtype,
1890             size,
1891             count,
1892             data,
1893         };
1894         let result = match subtype {
1895             IntegerInfoRecord::SUBTYPE => IntegerInfoRecord::parse(&extension, endian),
1896             FloatInfoRecord::SUBTYPE => FloatInfoRecord::parse(&extension, endian),
1897             VarDisplayRecord::SUBTYPE => VarDisplayRecord::parse(&extension, endian),
1898             MultipleResponseRecord::SUBTYPE | 19 => {
1899                 MultipleResponseRecord::parse(&extension, endian)
1900             }
1901             LongStringValueLabelRecord::SUBTYPE => {
1902                 LongStringValueLabelRecord::parse(&extension, endian)
1903             }
1904             EncodingRecord::SUBTYPE => EncodingRecord::parse(&extension, endian),
1905             NumberOfCasesRecord::SUBTYPE => NumberOfCasesRecord::parse(&extension, endian),
1906             5 => Ok(Record::VariableSets(extension.into())),
1907             10 => Ok(Record::ProductInfo(extension.into())),
1908             13 => Ok(Record::LongNames(extension.into())),
1909             14 => Ok(Record::VeryLongStrings(extension.into())),
1910             17 => Ok(Record::FileAttributes(extension.into())),
1911             18 => Ok(Record::VariableAttributes(extension.into())),
1912             _ => Ok(Record::OtherExtension(extension)),
1913         };
1914         match result {
1915             Ok(result) => Ok(Some(result)),
1916             Err(error) => {
1917                 warn(error);
1918                 Ok(None)
1919             }
1920         }
1921     }
1922 }
1923
1924 #[derive(Clone, Debug)]
1925 pub struct ZHeader {
1926     /// File offset to the start of the record.
1927     pub offset: u64,
1928
1929     /// File offset to the ZLIB data header.
1930     pub zheader_offset: u64,
1931
1932     /// File offset to the ZLIB trailer.
1933     pub ztrailer_offset: u64,
1934
1935     /// Length of the ZLIB trailer in bytes.
1936     pub ztrailer_len: u64,
1937 }
1938
1939 impl ZHeader {
1940     fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<ZHeader, Error> {
1941         let offset = r.stream_position()?;
1942         let zheader_offset: u64 = endian.parse(read_bytes(r)?);
1943         let ztrailer_offset: u64 = endian.parse(read_bytes(r)?);
1944         let ztrailer_len: u64 = endian.parse(read_bytes(r)?);
1945
1946         Ok(ZHeader {
1947             offset,
1948             zheader_offset,
1949             ztrailer_offset,
1950             ztrailer_len,
1951         })
1952     }
1953 }
1954
1955 #[derive(Clone, Debug)]
1956 pub struct ZTrailer {
1957     /// File offset to the start of the record.
1958     pub offset: u64,
1959
1960     /// Compression bias as a negative integer, e.g. -100.
1961     pub int_bias: i64,
1962
1963     /// Always observed as zero.
1964     pub zero: u64,
1965
1966     /// Uncompressed size of each block, except possibly the last.  Only
1967     /// `0x3ff000` has been observed so far.
1968     pub block_size: u32,
1969
1970     /// Block descriptors, always `(ztrailer_len - 24) / 24)` of them.
1971     pub blocks: Vec<ZBlock>,
1972 }
1973
1974 #[derive(Clone, Debug)]
1975 pub struct ZBlock {
1976     /// Offset of block of data if simple compression were used.
1977     pub uncompressed_ofs: u64,
1978
1979     /// Actual offset within the file of the compressed data block.
1980     pub compressed_ofs: u64,
1981
1982     /// The number of bytes in this data block after decompression.  This is
1983     /// `block_size` in every data block but the last, which may be smaller.
1984     pub uncompressed_size: u32,
1985
1986     /// The number of bytes in this data block, as stored compressed in this
1987     /// file.
1988     pub compressed_size: u32,
1989 }
1990
1991 impl ZBlock {
1992     fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<ZBlock, Error> {
1993         Ok(ZBlock {
1994             uncompressed_ofs: endian.parse(read_bytes(r)?),
1995             compressed_ofs: endian.parse(read_bytes(r)?),
1996             uncompressed_size: endian.parse(read_bytes(r)?),
1997             compressed_size: endian.parse(read_bytes(r)?),
1998         })
1999     }
2000 }
2001
2002 impl ZTrailer {
2003     fn read<R: Read + Seek>(
2004         reader: &mut R,
2005         endian: Endian,
2006         ztrailer_ofs: u64,
2007         ztrailer_len: u64,
2008     ) -> Result<Option<ZTrailer>, Error> {
2009         let start_offset = reader.stream_position()?;
2010         if reader.seek(SeekFrom::Start(ztrailer_ofs)).is_err() {
2011             return Ok(None);
2012         }
2013         let int_bias = endian.parse(read_bytes(reader)?);
2014         let zero = endian.parse(read_bytes(reader)?);
2015         let block_size = endian.parse(read_bytes(reader)?);
2016         let n_blocks: u32 = endian.parse(read_bytes(reader)?);
2017         let expected_n_blocks = (ztrailer_len - 24) / 24;
2018         if n_blocks as u64 != expected_n_blocks {
2019             return Err(Error::BadZlibTrailerNBlocks {
2020                 offset: ztrailer_ofs,
2021                 n_blocks,
2022                 expected_n_blocks,
2023                 ztrailer_len,
2024             });
2025         }
2026         let blocks = (0..n_blocks)
2027             .map(|_| ZBlock::read(reader, endian))
2028             .collect::<Result<Vec<_>, _>>()?;
2029         reader.seek(SeekFrom::Start(start_offset))?;
2030         Ok(Some(ZTrailer {
2031             offset: ztrailer_ofs,
2032             int_bias,
2033             zero,
2034             block_size,
2035             blocks,
2036         }))
2037     }
2038 }
2039
2040 fn try_read_bytes<const N: usize, R: Read>(r: &mut R) -> Result<Option<[u8; N]>, IoError> {
2041     let mut buf = [0; N];
2042     let n = r.read(&mut buf)?;
2043     if n > 0 {
2044         if n < N {
2045             r.read_exact(&mut buf[n..])?;
2046         }
2047         Ok(Some(buf))
2048     } else {
2049         Ok(None)
2050     }
2051 }
2052
2053 fn read_bytes<const N: usize, R: Read>(r: &mut R) -> Result<[u8; N], IoError> {
2054     let mut buf = [0; N];
2055     r.read_exact(&mut buf)?;
2056     Ok(buf)
2057 }
2058
2059 fn read_vec<R: Read>(r: &mut R, n: usize) -> Result<Vec<u8>, IoError> {
2060     let mut vec = vec![0; n];
2061     r.read_exact(&mut vec)?;
2062     Ok(vec)
2063 }
2064
2065 fn read_string<R: Read>(r: &mut R, endian: Endian) -> Result<RawString, IoError> {
2066     let length: u32 = endian.parse(read_bytes(r)?);
2067     Ok(read_vec(r, length as usize)?.into())
2068 }
2069
2070 #[derive(Clone, Debug)]
2071 pub struct LongStringValueLabels {
2072     pub var_name: RawString,
2073     pub width: u32,
2074
2075     /// `(value, label)` pairs, where each value is `width` bytes.
2076     pub labels: Vec<(RawString, RawString)>,
2077 }
2078
2079 #[derive(Clone, Debug)]
2080 pub struct LongStringValueLabelRecord(pub Vec<LongStringValueLabels>);
2081
2082 impl ExtensionRecord for LongStringValueLabelRecord {
2083     const SUBTYPE: u32 = 21;
2084     const SIZE: Option<u32> = Some(1);
2085     const COUNT: Option<u32> = None;
2086     const NAME: &'static str = "long string value labels record";
2087
2088     fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
2089         ext.check_size::<Self>()?;
2090
2091         let mut input = &ext.data[..];
2092         let mut label_set = Vec::new();
2093         while !input.is_empty() {
2094             let var_name = read_string(&mut input, endian)?;
2095             let width: u32 = endian.parse(read_bytes(&mut input)?);
2096             let n_labels: u32 = endian.parse(read_bytes(&mut input)?);
2097             let mut labels = Vec::new();
2098             for _ in 0..n_labels {
2099                 let value = read_string(&mut input, endian)?;
2100                 let label = read_string(&mut input, endian)?;
2101                 labels.push((value, label));
2102             }
2103             label_set.push(LongStringValueLabels {
2104                 var_name,
2105                 width,
2106                 labels,
2107             })
2108         }
2109         Ok(Record::LongStringValueLabels(LongStringValueLabelRecord(
2110             label_set,
2111         )))
2112     }
2113 }