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