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