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