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