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