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