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