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