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