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