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