a8c8ff7b466015cae3bd2734ca52f6c7cc939f5c
[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}, number of variables indexes ({n}) is greater than the maximum number ({max}).")]
59     BadNumberOfVarIndexes { offset: u64, n: u32, max: u32 },
60
61     #[error("At offset {offset:#x}, record type 7 subtype {subtype} is too large with element size {size} and {count} elements.")]
62     ExtensionRecordTooLarge {
63         offset: u64,
64         subtype: u32,
65         size: u32,
66         count: u32,
67     },
68
69     #[error("Unexpected end of file at offset {offset:#x}, {case_ofs} bytes into a {case_len}-byte case.")]
70     EofInCase {
71         offset: u64,
72         case_ofs: u64,
73         case_len: usize,
74     },
75
76     #[error(
77         "Unexpected end of file at offset {offset:#x}, {case_ofs} bytes into a compressed case."
78     )]
79     EofInCompressedCase { offset: u64, case_ofs: u64 },
80
81     #[error("Data ends at offset {offset:#x}, {case_ofs} bytes into a compressed case.")]
82     PartialCompressedCase { offset: u64, case_ofs: u64 },
83
84     #[error("At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a string was found where a number was expected.")]
85     CompressedNumberExpected { offset: u64, case_ofs: u64 },
86
87     #[error("At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a number was found where a string was expected.")]
88     CompressedStringExpected { offset: u64, case_ofs: u64 },
89
90     #[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}.")]
91     BadZlibTrailerNBlocks {
92         offset: u64,
93         n_blocks: u32,
94         expected_n_blocks: u64,
95         ztrailer_len: u64,
96     },
97
98     #[error("At offset {offset:#x}, {record} has bad size {size} bytes instead of the expected {expected_size}.")]
99     BadRecordSize {
100         offset: u64,
101         record: String,
102         size: u32,
103         expected_size: u32,
104     },
105
106     #[error("At offset {offset:#x}, {record} has bad count {count} instead of the expected {expected_count}.")]
107     BadRecordCount {
108         offset: u64,
109         record: String,
110         count: u32,
111         expected_count: u32,
112     },
113
114     #[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.")]
115     BadLongMissingValueLength {
116         record_offset: u64,
117         offset: u64,
118         value_len: u32,
119     },
120
121     #[error("The encoding record at offset {offset:#x} contains an encoding name that is not valid UTF-8.")]
122     BadEncodingName { offset: u64 },
123
124     #[error("Details TBD")]
125     TBD,
126 }
127
128 #[derive(Clone, Debug)]
129 pub enum Record {
130     Header(HeaderRecord),
131     Variable(VariableRecord),
132     ValueLabel(ValueLabelRecord),
133     VarIndexes(VarIndexRecord),
134     Document(DocumentRecord),
135     IntegerInfo(IntegerInfoRecord),
136     FloatInfo(FloatInfoRecord),
137     VariableSets(TextRecord),
138     VarDisplay(VarDisplayRecord),
139     MultipleResponse(MultipleResponseRecord),
140     LongStringValueLabels(LongStringValueLabelRecord),
141     Encoding(EncodingRecord),
142     NumberOfCases(NumberOfCasesRecord),
143     ProductInfo(TextRecord),
144     LongNames(TextRecord),
145     LongStrings(TextRecord),
146     FileAttributes(TextRecord),
147     VariableAttributes(TextRecord),
148     OtherExtension(Extension),
149     EndOfHeaders(u32),
150     ZHeader(ZHeader),
151     ZTrailer(ZTrailer),
152     Case(Vec<Value>),
153 }
154
155 impl Record {
156     fn read<R: Read + Seek>(reader: &mut R, endian: Endian) -> Result<Record, Error> {
157         let rec_type: u32 = endian.parse(read_bytes(reader)?);
158         match rec_type {
159             2 => Ok(Record::Variable(VariableRecord::read(reader, endian)?)),
160             3 => Ok(Record::ValueLabel(ValueLabelRecord::read(reader, endian)?)),
161             4 => Ok(Record::VarIndexes(VarIndexRecord::read(reader, endian)?)),
162             6 => Ok(Record::Document(DocumentRecord::read(reader, endian)?)),
163             7 => Ok(Extension::read(reader, endian)?),
164             999 => Ok(Record::EndOfHeaders(endian.parse(read_bytes(reader)?))),
165             _ => Err(Error::BadRecordType {
166                 offset: reader.stream_position()?,
167                 rec_type,
168             }),
169         }
170     }
171 }
172
173 // If `s` is valid UTF-8, returns it decoded as UTF-8, otherwise returns it
174 // decoded as Latin-1 (actually bytes interpreted as Unicode code points).
175 fn default_decode<'a>(s: &'a [u8]) -> Cow<'a, str> {
176     from_utf8(s).map_or_else(|_| decode_latin1(s), Cow::from)
177 }
178
179 #[derive(Copy, Clone, Debug)]
180 pub enum Compression {
181     Simple,
182     ZLib,
183 }
184
185 #[derive(Clone)]
186 pub struct HeaderRecord {
187     /// Magic number.
188     pub magic: Magic,
189
190     /// Eye-catcher string, product name, in the file's encoding.  Padded
191     /// on the right with spaces.
192     pub eye_catcher: UnencodedStr<60>,
193
194     /// Layout code, normally either 2 or 3.
195     pub layout_code: u32,
196
197     /// Number of variable positions, or `None` if the value in the file is
198     /// questionably trustworthy.
199     pub nominal_case_size: Option<u32>,
200
201     /// Compression type, if any,
202     pub compression: Option<Compression>,
203
204     /// 1-based variable index of the weight variable, or `None` if the file is
205     /// unweighted.
206     pub weight_index: Option<u32>,
207
208     /// Claimed number of cases, if known.
209     pub n_cases: Option<u32>,
210
211     /// Compression bias, usually 100.0.
212     pub bias: f64,
213
214     /// `dd mmm yy` in the file's encoding.
215     pub creation_date: UnencodedStr<9>,
216
217     /// `HH:MM:SS` in the file's encoding.
218     pub creation_time: UnencodedStr<8>,
219
220     /// File label, in the file's encoding.  Padded on the right with spaces.
221     pub file_label: UnencodedStr<64>,
222
223     /// Endianness of the data in the file header.
224     pub endian: Endian,
225 }
226
227 impl HeaderRecord {
228     fn debug_field<T: Debug>(&self, f: &mut Formatter, name: &str, value: T) -> FmtResult {
229         writeln!(f, "{name:>17}: {:?}", value)
230     }
231 }
232
233 impl Debug for HeaderRecord {
234     fn fmt(&self, f: &mut Formatter) -> FmtResult {
235         writeln!(f, "File header record:")?;
236         self.debug_field(f, "Magic", self.magic)?;
237         self.debug_field(f, "Product name", &self.eye_catcher)?;
238         self.debug_field(f, "Layout code", self.layout_code)?;
239         self.debug_field(f, "Nominal case size", self.nominal_case_size)?;
240         self.debug_field(f, "Compression", self.compression)?;
241         self.debug_field(f, "Weight index", self.weight_index)?;
242         self.debug_field(f, "Number of cases", self.n_cases)?;
243         self.debug_field(f, "Compression bias", self.bias)?;
244         self.debug_field(f, "Creation date", &self.creation_date)?;
245         self.debug_field(f, "Creation time", &self.creation_time)?;
246         self.debug_field(f, "File label", &self.file_label)?;
247         self.debug_field(f, "Endianness", self.endian)
248     }
249 }
250
251 impl HeaderRecord {
252     fn read<R: Read>(r: &mut R) -> Result<HeaderRecord, Error> {
253         let magic: [u8; 4] = read_bytes(r)?;
254         let magic: Magic = magic.try_into().map_err(|_| Error::NotASystemFile)?;
255
256         let eye_catcher = UnencodedStr::<60>(read_bytes(r)?);
257         let layout_code: [u8; 4] = read_bytes(r)?;
258         let endian = Endian::identify_u32(2, layout_code)
259             .or_else(|| Endian::identify_u32(2, layout_code))
260             .ok_or_else(|| Error::NotASystemFile)?;
261         let layout_code = endian.parse(layout_code);
262
263         let nominal_case_size: u32 = endian.parse(read_bytes(r)?);
264         let nominal_case_size =
265             (nominal_case_size <= i32::MAX as u32 / 16).then_some(nominal_case_size);
266
267         let compression_code: u32 = endian.parse(read_bytes(r)?);
268         let compression = match (magic, compression_code) {
269             (Magic::ZSAV, 2) => Some(Compression::ZLib),
270             (Magic::ZSAV, code) => return Err(Error::InvalidZsavCompression(code)),
271             (_, 0) => None,
272             (_, 1) => Some(Compression::Simple),
273             (_, code) => return Err(Error::InvalidSavCompression(code)),
274         };
275
276         let weight_index: u32 = endian.parse(read_bytes(r)?);
277         let weight_index = (weight_index > 0).then_some(weight_index);
278
279         let n_cases: u32 = endian.parse(read_bytes(r)?);
280         let n_cases = (n_cases < i32::MAX as u32 / 2).then_some(n_cases);
281
282         let bias: f64 = endian.parse(read_bytes(r)?);
283
284         let creation_date = UnencodedStr::<9>(read_bytes(r)?);
285         let creation_time = UnencodedStr::<8>(read_bytes(r)?);
286         let file_label = UnencodedStr::<64>(read_bytes(r)?);
287         let _: [u8; 3] = read_bytes(r)?;
288
289         Ok(HeaderRecord {
290             magic,
291             layout_code,
292             nominal_case_size,
293             compression,
294             weight_index,
295             n_cases,
296             bias,
297             creation_date,
298             creation_time,
299             eye_catcher,
300             file_label,
301             endian,
302         })
303     }
304 }
305
306 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
307 pub struct Magic([u8; 4]);
308
309 impl Magic {
310     /// Magic number for a regular system file.
311     pub const SAV: Magic = Magic(*b"$FL2");
312
313     /// Magic number for a system file that contains zlib-compressed data.
314     pub const ZSAV: Magic = Magic(*b"$FL3");
315
316     /// Magic number for an EBDIC-encoded system file.  This is `$FL2` encoded
317     /// in EBCDIC.
318     pub const EBCDIC: Magic = Magic([0x5b, 0xc6, 0xd3, 0xf2]);
319 }
320
321 impl Debug for Magic {
322     fn fmt(&self, f: &mut Formatter) -> FmtResult {
323         let s = match self {
324             &Magic::SAV => "$FL2",
325             &Magic::ZSAV => "$FL3",
326             &Magic::EBCDIC => "($FL2 in EBCDIC)",
327             _ => return write!(f, "{:?}", self.0),
328         };
329         write!(f, "{s}")
330     }
331 }
332
333 impl TryFrom<[u8; 4]> for Magic {
334     type Error = Error;
335
336     fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
337         let magic = Magic(value);
338         match magic {
339             Magic::SAV | Magic::ZSAV | Magic::EBCDIC => Ok(magic),
340             _ => Err(Error::BadMagic(value)),
341         }
342     }
343 }
344
345 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
346 pub enum VarType {
347     Numeric,
348     String,
349 }
350
351 impl VarType {
352     fn from_width(width: i32) -> VarType {
353         match width {
354             0 => VarType::Numeric,
355             _ => VarType::String,
356         }
357     }
358 }
359
360 mod state {
361     use super::{
362         Compression, Error, HeaderRecord, Record, Value, VarType, VariableRecord, ZHeader,
363         ZTrailer, ZlibDecodeMultiple,
364     };
365     use crate::endian::Endian;
366     use std::{
367         collections::VecDeque,
368         io::{Read, Seek},
369     };
370
371     pub trait State {
372         #[allow(clippy::type_complexity)]
373         fn read(self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error>;
374     }
375
376     struct Start<R: Read + Seek> {
377         reader: R,
378     }
379
380     pub fn new<R: Read + Seek + 'static>(reader: R) -> Box<dyn State> {
381         Box::new(Start { reader })
382     }
383
384     struct CommonState<R: Read + Seek> {
385         reader: R,
386         endian: Endian,
387         bias: f64,
388         compression: Option<Compression>,
389         var_types: Vec<VarType>,
390     }
391
392     impl<R: Read + Seek + 'static> State for Start<R> {
393         fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
394             let header = HeaderRecord::read(&mut self.reader)?;
395             let next_state = Headers(CommonState {
396                 reader: self.reader,
397                 endian: header.endian,
398                 bias: header.bias,
399                 compression: header.compression,
400                 var_types: Vec::new(),
401             });
402             Ok(Some((Record::Header(header), Box::new(next_state))))
403         }
404     }
405
406     struct Headers<R: Read + Seek>(CommonState<R>);
407
408     impl<R: Read + Seek + 'static> State for Headers<R> {
409         fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
410             let record = Record::read(&mut self.0.reader, self.0.endian)?;
411             match record {
412                 Record::Variable(VariableRecord { width, .. }) => {
413                     self.0.var_types.push(VarType::from_width(width));
414                 }
415                 Record::EndOfHeaders(_) => {
416                     let next_state: Box<dyn State> = match self.0.compression {
417                         None => Box::new(Data(self.0)),
418                         Some(Compression::Simple) => Box::new(CompressedData::new(self.0)),
419                         Some(Compression::ZLib) => Box::new(ZlibHeader(self.0)),
420                     };
421                     return Ok(Some((record, next_state)));
422                 }
423                 _ => (),
424             };
425             Ok(Some((record, self)))
426         }
427     }
428
429     struct ZlibHeader<R: Read + Seek>(CommonState<R>);
430
431     impl<R: Read + Seek + 'static> State for ZlibHeader<R> {
432         fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
433             let zheader = ZHeader::read(&mut self.0.reader, self.0.endian)?;
434             Ok(Some((Record::ZHeader(zheader), self)))
435         }
436     }
437
438     struct ZlibTrailer<R: Read + Seek>(CommonState<R>, ZHeader);
439
440     impl<R: Read + Seek + 'static> State for ZlibTrailer<R> {
441         fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
442             let retval = ZTrailer::read(
443                 &mut self.0.reader,
444                 self.0.endian,
445                 self.1.ztrailer_offset,
446                 self.1.ztrailer_len,
447             )?;
448             let next_state = Box::new(CompressedData::new(CommonState {
449                 reader: ZlibDecodeMultiple::new(self.0.reader),
450                 endian: self.0.endian,
451                 bias: self.0.bias,
452                 compression: self.0.compression,
453                 var_types: self.0.var_types,
454             }));
455             match retval {
456                 None => next_state.read(),
457                 Some(ztrailer) => Ok(Some((Record::ZTrailer(ztrailer), next_state))),
458             }
459         }
460     }
461
462     struct Data<R: Read + Seek>(CommonState<R>);
463
464     impl<R: Read + Seek + 'static> State for Data<R> {
465         fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
466             match Value::read_case(&mut self.0.reader, &self.0.var_types, self.0.endian)? {
467                 None => Ok(None),
468                 Some(values) => Ok(Some((Record::Case(values), self))),
469             }
470         }
471     }
472
473     struct CompressedData<R: Read + Seek> {
474         common: CommonState<R>,
475         codes: VecDeque<u8>,
476     }
477
478     impl<R: Read + Seek + 'static> CompressedData<R> {
479         fn new(common: CommonState<R>) -> CompressedData<R> {
480             CompressedData {
481                 common,
482                 codes: VecDeque::new(),
483             }
484         }
485     }
486
487     impl<R: Read + Seek + 'static> State for CompressedData<R> {
488         fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
489             match Value::read_compressed_case(
490                 &mut self.common.reader,
491                 &self.common.var_types,
492                 &mut self.codes,
493                 self.common.endian,
494                 self.common.bias,
495             )? {
496                 None => Ok(None),
497                 Some(values) => Ok(Some((Record::Case(values), self))),
498             }
499         }
500     }
501 }
502
503 #[derive(Copy, Clone)]
504 pub enum Value {
505     Number(Option<f64>),
506     String(UnencodedStr<8>),
507 }
508
509 impl Debug for Value {
510     fn fmt(&self, f: &mut Formatter) -> FmtResult {
511         match self {
512             Value::Number(Some(number)) => write!(f, "{number:?}"),
513             Value::Number(None) => write!(f, "SYSMIS"),
514             Value::String(bytes) => write!(f, "{:?}", bytes),
515         }
516     }
517 }
518
519 impl Value {
520     fn read<R: Read>(r: &mut R, var_type: VarType, endian: Endian) -> Result<Value, IoError> {
521         Ok(Self::from_raw(
522             UntypedValue(read_bytes(r)?),
523             var_type,
524             endian,
525         ))
526     }
527
528     pub fn from_raw(raw: UntypedValue, var_type: VarType, endian: Endian) -> Value {
529         match var_type {
530             VarType::String => Value::String(UnencodedStr(raw.0)),
531             VarType::Numeric => {
532                 let number: f64 = endian.parse(raw.0);
533                 Value::Number((number != -f64::MAX).then_some(number))
534             }
535         }
536     }
537
538     fn read_case<R: Read + Seek>(
539         reader: &mut R,
540         var_types: &[VarType],
541         endian: Endian,
542     ) -> Result<Option<Vec<Value>>, Error> {
543         let case_start = reader.stream_position()?;
544         let mut values = Vec::with_capacity(var_types.len());
545         for (i, &var_type) in var_types.iter().enumerate() {
546             let Some(raw) = try_read_bytes(reader)? else {
547                 if i == 0 {
548                     return Ok(None);
549                 } else {
550                     let offset = reader.stream_position()?;
551                     return Err(Error::EofInCase {
552                         offset,
553                         case_ofs: offset - case_start,
554                         case_len: var_types.len() * 8,
555                     });
556                 }
557             };
558             values.push(Value::from_raw(UntypedValue(raw), var_type, endian));
559         }
560         Ok(Some(values))
561     }
562
563     fn read_compressed_case<R: Read + Seek>(
564         reader: &mut R,
565         var_types: &[VarType],
566         codes: &mut VecDeque<u8>,
567         endian: Endian,
568         bias: f64,
569     ) -> Result<Option<Vec<Value>>, Error> {
570         let case_start = reader.stream_position()?;
571         let mut values = Vec::with_capacity(var_types.len());
572         for (i, &var_type) in var_types.iter().enumerate() {
573             let value = loop {
574                 let Some(code) = codes.pop_front() else {
575                     let Some(new_codes): Option<[u8; 8]> = try_read_bytes(reader)? else {
576                         if i == 0 {
577                             return Ok(None);
578                         } else {
579                             let offset = reader.stream_position()?;
580                             return Err(Error::EofInCompressedCase {
581                                 offset,
582                                 case_ofs: offset - case_start,
583                             });
584                         }
585                     };
586                     codes.extend(new_codes.into_iter());
587                     continue;
588                 };
589                 match code {
590                     0 => (),
591                     1..=251 => match var_type {
592                         VarType::Numeric => break Value::Number(Some(code as f64 - bias)),
593                         VarType::String => {
594                             break Value::String(UnencodedStr(endian.to_bytes(code as f64 - bias)))
595                         }
596                     },
597                     252 => {
598                         if i == 0 {
599                             return Ok(None);
600                         } else {
601                             let offset = reader.stream_position()?;
602                             return Err(Error::PartialCompressedCase {
603                                 offset,
604                                 case_ofs: offset - case_start,
605                             });
606                         }
607                     }
608                     253 => {
609                         break Value::from_raw(UntypedValue(read_bytes(reader)?), var_type, endian)
610                     }
611                     254 => match var_type {
612                         VarType::String => break Value::String(UnencodedStr(*b"        ")), // XXX EBCDIC
613                         VarType::Numeric => {
614                             return Err(Error::CompressedStringExpected {
615                                 offset: case_start,
616                                 case_ofs: reader.stream_position()? - case_start,
617                             })
618                         }
619                     },
620                     255 => match var_type {
621                         VarType::Numeric => break Value::Number(None),
622                         VarType::String => {
623                             return Err(Error::CompressedNumberExpected {
624                                 offset: case_start,
625                                 case_ofs: reader.stream_position()? - case_start,
626                             })
627                         }
628                     },
629                 }
630             };
631             values.push(value);
632         }
633         Ok(Some(values))
634     }
635 }
636
637 struct ZlibDecodeMultiple<R>
638 where
639     R: Read + Seek,
640 {
641     reader: Option<ZlibDecoder<R>>,
642 }
643
644 impl<R> ZlibDecodeMultiple<R>
645 where
646     R: Read + Seek,
647 {
648     fn new(reader: R) -> ZlibDecodeMultiple<R> {
649         ZlibDecodeMultiple {
650             reader: Some(ZlibDecoder::new(reader)),
651         }
652     }
653 }
654
655 impl<R> Read for ZlibDecodeMultiple<R>
656 where
657     R: Read + Seek,
658 {
659     fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
660         loop {
661             match self.reader.as_mut().unwrap().read(buf)? {
662                 0 => {
663                     let inner = self.reader.take().unwrap().into_inner();
664                     self.reader = Some(ZlibDecoder::new(inner));
665                 }
666                 n => return Ok(n),
667             };
668         }
669     }
670 }
671
672 impl<R> Seek for ZlibDecodeMultiple<R>
673 where
674     R: Read + Seek,
675 {
676     fn seek(&mut self, pos: SeekFrom) -> Result<u64, IoError> {
677         self.reader.as_mut().unwrap().get_mut().seek(pos)
678     }
679 }
680
681 pub struct Reader {
682     state: Option<Box<dyn State>>,
683 }
684
685 impl Reader {
686     pub fn new<R: Read + Seek + 'static>(reader: R) -> Result<Reader, Error> {
687         Ok(Reader {
688             state: Some(state::new(reader)),
689         })
690     }
691     pub fn collect_headers(&mut self) -> Result<Vec<Record>, Error> {
692         let mut headers = Vec::new();
693         for record in self {
694             match record? {
695                 Record::EndOfHeaders(_) => break,
696                 r => headers.push(r),
697             };
698         }
699         Ok(headers)
700     }
701 }
702
703 impl Iterator for Reader {
704     type Item = Result<Record, Error>;
705
706     fn next(&mut self) -> Option<Self::Item> {
707         match self.state.take()?.read() {
708             Ok(Some((record, next_state))) => {
709                 self.state = Some(next_state);
710                 Some(Ok(record))
711             }
712             Ok(None) => None,
713             Err(error) => Some(Err(error)),
714         }
715     }
716 }
717
718 impl FusedIterator for Reader {}
719
720 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
721 pub struct Spec(pub u32);
722
723 impl Debug for Spec {
724     fn fmt(&self, f: &mut Formatter) -> FmtResult {
725         let type_ = format_name(self.0 >> 16);
726         let w = (self.0 >> 8) & 0xff;
727         let d = self.0 & 0xff;
728         write!(f, "{:06x} ({type_}{w}.{d})", self.0)
729     }
730 }
731
732 fn format_name(type_: u32) -> Cow<'static, str> {
733     match type_ {
734         1 => "A",
735         2 => "AHEX",
736         3 => "COMMA",
737         4 => "DOLLAR",
738         5 => "F",
739         6 => "IB",
740         7 => "PIBHEX",
741         8 => "P",
742         9 => "PIB",
743         10 => "PK",
744         11 => "RB",
745         12 => "RBHEX",
746         15 => "Z",
747         16 => "N",
748         17 => "E",
749         20 => "DATE",
750         21 => "TIME",
751         22 => "DATETIME",
752         23 => "ADATE",
753         24 => "JDATE",
754         25 => "DTIME",
755         26 => "WKDAY",
756         27 => "MONTH",
757         28 => "MOYR",
758         29 => "QYR",
759         30 => "WKYR",
760         31 => "PCT",
761         32 => "DOT",
762         33 => "CCA",
763         34 => "CCB",
764         35 => "CCC",
765         36 => "CCD",
766         37 => "CCE",
767         38 => "EDATE",
768         39 => "SDATE",
769         40 => "MTIME",
770         41 => "YMDHMS",
771         _ => return format!("<unknown format {type_}>").into(),
772     }
773     .into()
774 }
775
776 #[derive(Clone)]
777 pub struct MissingValues {
778     /// Individual missing values, up to 3 of them.
779     pub values: Vec<Value>,
780
781     /// Optional range of missing values.
782     pub range: Option<(Value, Value)>,
783 }
784
785 impl Debug for MissingValues {
786     fn fmt(&self, f: &mut Formatter) -> FmtResult {
787         for (i, value) in self.values.iter().enumerate() {
788             if i > 0 {
789                 write!(f, ", ")?;
790             }
791             write!(f, "{value:?}")?;
792         }
793
794         if let Some((low, high)) = self.range {
795             if !self.values.is_empty() {
796                 write!(f, ", ")?;
797             }
798             write!(f, "{low:?} THRU {high:?}")?;
799         }
800
801         if self.is_empty() {
802             write!(f, "none")?;
803         }
804
805         Ok(())
806     }
807 }
808
809 impl MissingValues {
810     fn is_empty(&self) -> bool {
811         self.values.is_empty() && self.range.is_none()
812     }
813
814     fn read<R: Read + Seek>(
815         r: &mut R,
816         offset: u64,
817         width: i32,
818         code: i32,
819         endian: Endian,
820     ) -> Result<MissingValues, Error> {
821         let (n_values, has_range) = match (width, code) {
822             (_, 0..=3) => (code, false),
823             (0, -2) => (0, true),
824             (0, -3) => (1, true),
825             (0, _) => return Err(Error::BadNumericMissingValueCode { offset, code }),
826             (_, _) => return Err(Error::BadStringMissingValueCode { offset, code }),
827         };
828
829         let var_type = VarType::from_width(width);
830
831         let mut values = Vec::new();
832         for _ in 0..n_values {
833             values.push(Value::read(r, var_type, endian)?);
834         }
835         let range = if has_range {
836             let low = Value::read(r, var_type, endian)?;
837             let high = Value::read(r, var_type, endian)?;
838             Some((low, high))
839         } else {
840             None
841         };
842         Ok(MissingValues { values, range })
843     }
844 }
845
846 #[derive(Clone)]
847 pub struct VariableRecord {
848     /// Offset from the start of the file to the start of the record.
849     pub offset: u64,
850
851     /// Variable width, in the range -1..=255.
852     pub width: i32,
853
854     /// Variable name, padded on the right with spaces.
855     pub name: UnencodedStr<8>,
856
857     /// Print format.
858     pub print_format: Spec,
859
860     /// Write format.
861     pub write_format: Spec,
862
863     /// Missing values.
864     pub missing_values: MissingValues,
865
866     /// Optional variable label.
867     pub label: Option<UnencodedString>,
868 }
869
870 impl Debug for VariableRecord {
871     fn fmt(&self, f: &mut Formatter) -> FmtResult {
872         writeln!(
873             f,
874             "Width: {} ({})",
875             self.width,
876             if self.width > 0 {
877                 "string"
878             } else if self.width == 0 {
879                 "numeric"
880             } else {
881                 "long string continuation record"
882             }
883         )?;
884         writeln!(f, "Print format: {:?}", self.print_format)?;
885         writeln!(f, "Write format: {:?}", self.write_format)?;
886         writeln!(f, "Name: {:?}", &self.name)?;
887         writeln!(f, "Variable label: {:?}", self.label)?;
888         writeln!(f, "Missing values: {:?}", self.missing_values)
889     }
890 }
891
892 impl VariableRecord {
893     fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<VariableRecord, Error> {
894         let offset = r.stream_position()?;
895         let width: i32 = endian.parse(read_bytes(r)?);
896         let has_variable_label: u32 = endian.parse(read_bytes(r)?);
897         let missing_value_code: i32 = endian.parse(read_bytes(r)?);
898         let print_format = Spec(endian.parse(read_bytes(r)?));
899         let write_format = Spec(endian.parse(read_bytes(r)?));
900         let name = UnencodedStr::<8>(read_bytes(r)?);
901
902         let label = match has_variable_label {
903             0 => None,
904             1 => {
905                 let len: u32 = endian.parse(read_bytes(r)?);
906                 let read_len = len.min(65535) as usize;
907                 let label = UnencodedString(read_vec(r, read_len)?);
908
909                 let padding_bytes = Integer::next_multiple_of(&len, &4) - len;
910                 let _ = read_vec(r, padding_bytes as usize)?;
911
912                 Some(label)
913             }
914             _ => {
915                 return Err(Error::BadVariableLabelCode {
916                     offset,
917                     code: has_variable_label,
918                 })
919             }
920         };
921
922         let missing_values = MissingValues::read(r, offset, width, missing_value_code, endian)?;
923
924         Ok(VariableRecord {
925             offset,
926             width,
927             name,
928             print_format,
929             write_format,
930             missing_values,
931             label,
932         })
933     }
934 }
935
936 #[derive(Copy, Clone)]
937 pub struct UntypedValue(pub [u8; 8]);
938
939 impl Debug for UntypedValue {
940     fn fmt(&self, f: &mut Formatter) -> FmtResult {
941         let little: f64 = Endian::Little.parse(self.0);
942         let little = format!("{:?}", little);
943         let big: f64 = Endian::Big.parse(self.0);
944         let big = format!("{:?}", big);
945         let number = if little.len() <= big.len() {
946             little
947         } else {
948             big
949         };
950         write!(f, "{number}")?;
951
952         let string = default_decode(&self.0);
953         let string = string
954             .split(|c: char| c == '\0' || c.is_control())
955             .next()
956             .unwrap();
957         write!(f, "{string:?}")?;
958         Ok(())
959     }
960 }
961
962 #[derive(Clone)]
963 pub struct UnencodedString(pub Vec<u8>);
964
965 impl From<Vec<u8>> for UnencodedString {
966     fn from(source: Vec<u8>) -> Self {
967         Self(source)
968     }
969 }
970
971 impl From<&[u8]> for UnencodedString {
972     fn from(source: &[u8]) -> Self {
973         Self(source.into())
974     }
975 }
976
977 impl Debug for UnencodedString {
978     fn fmt(&self, f: &mut Formatter) -> FmtResult {
979         write!(f, "{:?}", default_decode(self.0.as_slice()))
980     }
981 }
982
983 #[derive(Copy, Clone)]
984 pub struct UnencodedStr<const N: usize>(pub [u8; N]);
985
986 impl<const N: usize> From<[u8; N]> for UnencodedStr<N> {
987     fn from(source: [u8; N]) -> Self {
988         Self(source)
989     }
990 }
991
992 impl<const N: usize> Debug for UnencodedStr<N> {
993     fn fmt(&self, f: &mut Formatter) -> FmtResult {
994         write!(f, "{:?}", default_decode(&self.0))
995     }
996 }
997
998 #[derive(Clone)]
999 pub struct ValueLabelRecord {
1000     /// Offset from the start of the file to the start of the record.
1001     pub offset: u64,
1002
1003     /// The labels.
1004     pub labels: Vec<(UntypedValue, UnencodedString)>,
1005 }
1006
1007 impl Debug for ValueLabelRecord {
1008     fn fmt(&self, f: &mut Formatter) -> FmtResult {
1009         for (value, label) in self.labels.iter() {
1010             writeln!(f, "{value:?}: {label:?}")?;
1011         }
1012         Ok(())
1013     }
1014 }
1015
1016 impl ValueLabelRecord {
1017     /// Maximum number of value labels in a record.
1018     pub const MAX: u32 = u32::MAX / 8;
1019
1020     fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<ValueLabelRecord, Error> {
1021         let offset = r.stream_position()?;
1022         let n: u32 = endian.parse(read_bytes(r)?);
1023         if n > ValueLabelRecord::MAX {
1024             return Err(Error::BadNumberOfValueLabels {
1025                 offset,
1026                 n,
1027                 max: ValueLabelRecord::MAX,
1028             });
1029         }
1030
1031         let mut labels = Vec::new();
1032         for _ in 0..n {
1033             let value = UntypedValue(read_bytes(r)?);
1034             let label_len: u8 = endian.parse(read_bytes(r)?);
1035             let label_len = label_len as usize;
1036             let padded_len = Integer::next_multiple_of(&(label_len + 1), &8);
1037
1038             let mut label = read_vec(r, padded_len - 1)?;
1039             label.truncate(label_len);
1040             labels.push((value, UnencodedString(label)));
1041         }
1042         Ok(ValueLabelRecord { offset, labels })
1043     }
1044 }
1045
1046 #[derive(Clone)]
1047 pub struct VarIndexRecord {
1048     /// Offset from the start of the file to the start of the record.
1049     pub offset: u64,
1050
1051     /// The 1-based indexes of the variable indexes.
1052     pub dict_indexes: Vec<u32>,
1053 }
1054
1055 impl Debug for VarIndexRecord {
1056     fn fmt(&self, f: &mut Formatter) -> FmtResult {
1057         write!(f, "apply to variables")?;
1058         for dict_index in self.dict_indexes.iter() {
1059             write!(f, " #{dict_index}")?;
1060         }
1061         Ok(())
1062     }
1063 }
1064
1065 impl VarIndexRecord {
1066     /// Maximum number of variable indexes in a record.
1067     pub const MAX: u32 = u32::MAX / 8;
1068
1069     fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<VarIndexRecord, Error> {
1070         let offset = r.stream_position()?;
1071         let n: u32 = endian.parse(read_bytes(r)?);
1072         if n > VarIndexRecord::MAX {
1073             return Err(Error::BadNumberOfVarIndexes {
1074                 offset,
1075                 n,
1076                 max: VarIndexRecord::MAX,
1077             });
1078         }
1079         let mut dict_indexes = Vec::with_capacity(n as usize);
1080         for _ in 0..n {
1081             dict_indexes.push(endian.parse(read_bytes(r)?));
1082         }
1083
1084         Ok(VarIndexRecord {
1085             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::LongStrings(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(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 }