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