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