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