1 use crate::endian::{Endian, Parse, ToBytes};
3 use encoding_rs::mem::decode_latin1;
4 use flate2::read::ZlibDecoder;
7 use std::cmp::Ordering;
8 use std::fmt::{Debug, Formatter, Result as FmtResult};
10 use std::str::from_utf8;
12 collections::VecDeque,
13 io::{Error as IoError, Read, Seek, SeekFrom},
16 use thiserror::Error as ThisError;
18 use self::state::State;
20 #[derive(ThisError, Debug)]
22 #[error("Not an SPSS system file")]
25 #[error("Invalid magic number {0:?}")]
28 #[error("I/O error ({0})")]
31 #[error("Invalid SAV compression code {0}")]
32 InvalidSavCompression(u32),
34 #[error("Invalid ZSAV compression code {0}")]
35 InvalidZsavCompression(u32),
37 #[error("Variable record at offset {offset:#x} specifies width {width} not in valid range [-1,255).")]
38 BadVariableWidth { offset: u64, width: i32 },
40 #[error("Document record at offset {offset:#x} has document line count ({n}) greater than the maximum number {max}.")]
41 BadDocumentLength { offset: u64, n: usize, max: usize },
43 #[error("At offset {offset:#x}, unrecognized record type {rec_type}.")]
44 BadRecordType { offset: u64, rec_type: u32 },
46 #[error("In variable record starting at offset {start_offset:#x}, variable label code {code} at offset {code_offset:#x} is not 0 or 1.")]
47 BadVariableLabelCode {
54 "At offset {offset:#x}, numeric missing value code ({code}) is not -3, -2, 0, 1, 2, or 3."
56 BadNumericMissingValueCode { offset: u64, code: i32 },
58 #[error("At offset {offset:#x}, string missing value code ({code}) is not 0, 1, 2, or 3.")]
59 BadStringMissingValueCode { offset: u64, code: i32 },
61 #[error("At offset {offset:#x}, number of value labels ({n}) is greater than the maximum number {max}.")]
62 BadNumberOfValueLabels { offset: u64, n: u32, max: u32 },
64 #[error("At offset {offset:#x}, following value label record, found record type {rec_type} instead of expected type 4 for variable index record")]
65 ExpectedVarIndexRecord { offset: u64, rec_type: u32 },
67 #[error("At offset {offset:#x}, number of variables indexes ({n}) is greater than the maximum number ({max}).")]
68 BadNumberOfVarIndexes { offset: u64, n: u32, max: u32 },
70 #[error("At offset {offset:#x}, record type 7 subtype {subtype} is too large with element size {size} and {count} elements.")]
71 ExtensionRecordTooLarge {
78 #[error("Unexpected end of file at offset {offset:#x}, {case_ofs} bytes into a {case_len}-byte case.")]
86 "Unexpected end of file at offset {offset:#x}, {case_ofs} bytes into a compressed case."
88 EofInCompressedCase { offset: u64, case_ofs: u64 },
90 #[error("Data ends at offset {offset:#x}, {case_ofs} bytes into a compressed case.")]
91 PartialCompressedCase { offset: u64, case_ofs: u64 },
93 #[error("At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a string was found where a number was expected.")]
94 CompressedNumberExpected { offset: u64, case_ofs: u64 },
96 #[error("At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a number was found where a string was expected.")]
97 CompressedStringExpected { offset: u64, case_ofs: u64 },
99 #[error("Block count {n_blocks} in ZLIB trailer at offset {offset:#x} differs from expected block count {expected_n_blocks} calculated from trailer length {ztrailer_len}.")]
100 BadZlibTrailerNBlocks {
103 expected_n_blocks: u64,
107 #[error("At offset {offset:#x}, {record} has bad size {size} bytes instead of the expected {expected_size}.")]
115 #[error("At offset {offset:#x}, {record} has bad count {count} instead of the expected {expected_count}.")]
123 #[error("In long string missing values record starting at offset {record_offset:#x}, value length at offset {offset:#x} is {value_len} instead of the expected 8.")]
124 BadLongMissingValueLength {
130 #[error("The encoding record at offset {offset:#x} contains an encoding name that is not valid UTF-8.")]
131 BadEncodingName { offset: u64 },
133 #[error("Details TBD")]
137 #[derive(Clone, Debug)]
139 Header(HeaderRecord),
140 Variable(VariableRecord),
141 ValueLabel(ValueLabelRecord),
142 Document(DocumentRecord),
143 IntegerInfo(IntegerInfoRecord),
144 FloatInfo(FloatInfoRecord),
145 VariableSets(TextRecord),
146 VarDisplay(VarDisplayRecord),
147 MultipleResponse(MultipleResponseRecord),
148 LongStringValueLabels(LongStringValueLabelRecord),
149 LongStringMissingValues(LongStringMissingValueSet),
150 Encoding(EncodingRecord),
151 NumberOfCases(NumberOfCasesRecord),
152 ProductInfo(TextRecord),
153 LongNames(TextRecord),
154 VeryLongStrings(TextRecord),
155 FileAttributes(TextRecord),
156 VariableAttributes(TextRecord),
157 OtherExtension(Extension),
165 fn read<R: Read + Seek>(reader: &mut R, endian: Endian) -> Result<Record, Error> {
166 let rec_type: u32 = endian.parse(read_bytes(reader)?);
168 2 => Ok(Record::Variable(VariableRecord::read(reader, endian)?)),
169 3 => Ok(Record::ValueLabel(ValueLabelRecord::read(reader, endian)?)),
170 6 => Ok(Record::Document(DocumentRecord::read(reader, endian)?)),
171 7 => Ok(Extension::read(reader, endian)?),
172 999 => Ok(Record::EndOfHeaders(endian.parse(read_bytes(reader)?))),
173 _ => Err(Error::BadRecordType {
174 offset: reader.stream_position()?,
181 // If `s` is valid UTF-8, returns it decoded as UTF-8, otherwise returns it
182 // decoded as Latin-1 (actually bytes interpreted as Unicode code points).
183 fn default_decode(s: &[u8]) -> Cow<str> {
184 from_utf8(s).map_or_else(|_| decode_latin1(s), Cow::from)
187 #[derive(Copy, Clone, Debug)]
188 pub enum Compression {
194 fn offsets(&self) -> Range<u64>;
198 pub struct HeaderRecord {
200 pub offsets: Range<u64>,
205 /// Eye-catcher string, product name, in the file's encoding. Padded
206 /// on the right with spaces.
207 pub eye_catcher: UnencodedStr<60>,
209 /// Layout code, normally either 2 or 3.
210 pub layout_code: u32,
212 /// Number of variable positions, or `None` if the value in the file is
213 /// questionably trustworthy.
214 pub nominal_case_size: Option<u32>,
216 /// Compression type, if any,
217 pub compression: Option<Compression>,
219 /// 1-based variable index of the weight variable, or `None` if the file is
221 pub weight_index: Option<u32>,
223 /// Claimed number of cases, if known.
224 pub n_cases: Option<u32>,
226 /// Compression bias, usually 100.0.
229 /// `dd mmm yy` in the file's encoding.
230 pub creation_date: UnencodedStr<9>,
232 /// `HH:MM:SS` in the file's encoding.
233 pub creation_time: UnencodedStr<8>,
235 /// File label, in the file's encoding. Padded on the right with spaces.
236 pub file_label: UnencodedStr<64>,
238 /// Endianness of the data in the file header.
243 fn debug_field<T: Debug>(&self, f: &mut Formatter, name: &str, value: T) -> FmtResult {
244 writeln!(f, "{name:>17}: {:?}", value)
248 impl Debug for HeaderRecord {
249 fn fmt(&self, f: &mut Formatter) -> FmtResult {
250 writeln!(f, "File header record:")?;
251 self.debug_field(f, "Magic", self.magic)?;
252 self.debug_field(f, "Product name", self.eye_catcher)?;
253 self.debug_field(f, "Layout code", self.layout_code)?;
254 self.debug_field(f, "Nominal case size", self.nominal_case_size)?;
255 self.debug_field(f, "Compression", self.compression)?;
256 self.debug_field(f, "Weight index", self.weight_index)?;
257 self.debug_field(f, "Number of cases", self.n_cases)?;
258 self.debug_field(f, "Compression bias", self.bias)?;
259 self.debug_field(f, "Creation date", self.creation_date)?;
260 self.debug_field(f, "Creation time", self.creation_time)?;
261 self.debug_field(f, "File label", self.file_label)?;
262 self.debug_field(f, "Endianness", self.endian)
267 fn read<R: Read + Seek>(r: &mut R) -> Result<HeaderRecord, Error> {
268 let start = r.stream_position()?;
270 let magic: [u8; 4] = read_bytes(r)?;
271 let magic: Magic = magic.try_into().map_err(|_| Error::NotASystemFile)?;
273 let eye_catcher = UnencodedStr::<60>(read_bytes(r)?);
274 let layout_code: [u8; 4] = read_bytes(r)?;
275 let endian = Endian::identify_u32(2, layout_code)
276 .or_else(|| Endian::identify_u32(2, layout_code))
277 .ok_or_else(|| Error::NotASystemFile)?;
278 let layout_code = endian.parse(layout_code);
280 let nominal_case_size: u32 = endian.parse(read_bytes(r)?);
281 let nominal_case_size =
282 (nominal_case_size <= i32::MAX as u32 / 16).then_some(nominal_case_size);
284 let compression_code: u32 = endian.parse(read_bytes(r)?);
285 let compression = match (magic, compression_code) {
286 (Magic::Zsav, 2) => Some(Compression::ZLib),
287 (Magic::Zsav, code) => return Err(Error::InvalidZsavCompression(code)),
289 (_, 1) => Some(Compression::Simple),
290 (_, code) => return Err(Error::InvalidSavCompression(code)),
293 let weight_index: u32 = endian.parse(read_bytes(r)?);
294 let weight_index = (weight_index > 0).then_some(weight_index);
296 let n_cases: u32 = endian.parse(read_bytes(r)?);
297 let n_cases = (n_cases < i32::MAX as u32 / 2).then_some(n_cases);
299 let bias: f64 = endian.parse(read_bytes(r)?);
301 let creation_date = UnencodedStr::<9>(read_bytes(r)?);
302 let creation_time = UnencodedStr::<8>(read_bytes(r)?);
303 let file_label = UnencodedStr::<64>(read_bytes(r)?);
304 let _: [u8; 3] = read_bytes(r)?;
307 offsets: start..r.stream_position()?,
324 impl Header for HeaderRecord {
325 fn offsets(&self) -> Range<u64> {
330 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
332 /// Regular system file.
335 /// System file with Zlib-compressed data.
338 /// EBCDIC-encoded system file.
343 /// Magic number for a regular system file.
344 pub const SAV: [u8; 4] = *b"$FL2";
346 /// Magic number for a system file that contains zlib-compressed data.
347 pub const ZSAV: [u8; 4] = *b"$FL3";
349 /// Magic number for an EBCDIC-encoded system file. This is `$FL2` encoded
351 pub const EBCDIC: [u8; 4] = [0x5b, 0xc6, 0xd3, 0xf2];
354 impl Debug for Magic {
355 fn fmt(&self, f: &mut Formatter) -> FmtResult {
356 let s = match *self {
357 Magic::Sav => "$FL2",
358 Magic::Zsav => "$FL3",
359 Magic::Ebcdic => "($FL2 in EBCDIC)",
365 impl TryFrom<[u8; 4]> for Magic {
368 fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
370 Magic::SAV => Ok(Magic::Sav),
371 Magic::ZSAV => Ok(Magic::Zsav),
372 Magic::EBCDIC => Ok(Magic::Ebcdic),
373 _ => Err(Error::BadMagic(value)),
378 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
385 fn from_width(width: i32) -> VarType {
387 0 => VarType::Numeric,
388 _ => VarType::String,
395 Compression, Error, HeaderRecord, Record, Value, VarType, VariableRecord, ZHeader,
396 ZTrailer, ZlibDecodeMultiple,
398 use crate::endian::Endian;
400 collections::VecDeque,
405 #[allow(clippy::type_complexity)]
406 fn read(self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error>;
409 struct Start<R: Read + Seek> {
413 pub fn new<R: Read + Seek + 'static>(reader: R) -> Box<dyn State> {
414 Box::new(Start { reader })
417 struct CommonState<R: Read + Seek> {
421 compression: Option<Compression>,
422 var_types: Vec<VarType>,
425 impl<R: Read + Seek + 'static> State for Start<R> {
426 fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
427 let header = HeaderRecord::read(&mut self.reader)?;
428 let next_state = Headers(CommonState {
430 endian: header.endian,
432 compression: header.compression,
433 var_types: Vec::new(),
435 Ok(Some((Record::Header(header), Box::new(next_state))))
439 struct Headers<R: Read + Seek>(CommonState<R>);
441 impl<R: Read + Seek + 'static> State for Headers<R> {
442 fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
443 let record = Record::read(&mut self.0.reader, self.0.endian)?;
445 Record::Variable(VariableRecord { width, .. }) => {
446 self.0.var_types.push(VarType::from_width(width));
448 Record::EndOfHeaders(_) => {
449 let next_state: Box<dyn State> = match self.0.compression {
450 None => Box::new(Data(self.0)),
451 Some(Compression::Simple) => Box::new(CompressedData::new(self.0)),
452 Some(Compression::ZLib) => Box::new(ZlibHeader(self.0)),
454 return Ok(Some((record, next_state)));
458 Ok(Some((record, self)))
462 struct ZlibHeader<R: Read + Seek>(CommonState<R>);
464 impl<R: Read + Seek + 'static> State for ZlibHeader<R> {
465 fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
466 let zheader = ZHeader::read(&mut self.0.reader, self.0.endian)?;
467 let next_state = Box::new(ZlibTrailer(self.0, zheader.clone()));
468 Ok(Some((Record::ZHeader(zheader), next_state)))
472 struct ZlibTrailer<R: Read + Seek>(CommonState<R>, ZHeader);
474 impl<R: Read + Seek + 'static> State for ZlibTrailer<R> {
475 fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
476 let retval = ZTrailer::read(
479 self.1.ztrailer_offset,
482 let next_state = Box::new(CompressedData::new(CommonState {
483 reader: ZlibDecodeMultiple::new(self.0.reader),
484 endian: self.0.endian,
486 compression: self.0.compression,
487 var_types: self.0.var_types,
490 None => next_state.read(),
491 Some(ztrailer) => Ok(Some((Record::ZTrailer(ztrailer), next_state))),
496 struct Data<R: Read + Seek>(CommonState<R>);
498 impl<R: Read + Seek + 'static> State for Data<R> {
499 fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
500 match Value::read_case(&mut self.0.reader, &self.0.var_types, self.0.endian)? {
502 Some(values) => Ok(Some((Record::Case(values), self))),
507 struct CompressedData<R: Read + Seek> {
508 common: CommonState<R>,
512 impl<R: Read + Seek + 'static> CompressedData<R> {
513 fn new(common: CommonState<R>) -> CompressedData<R> {
516 codes: VecDeque::new(),
521 impl<R: Read + Seek + 'static> State for CompressedData<R> {
522 fn read(mut self: Box<Self>) -> Result<Option<(Record, Box<dyn State>)>, Error> {
523 match Value::read_compressed_case(
524 &mut self.common.reader,
525 &self.common.var_types,
531 Some(values) => Ok(Some((Record::Case(values), self))),
537 #[derive(Copy, Clone)]
540 String(UnencodedStr<8>),
543 impl Debug for Value {
544 fn fmt(&self, f: &mut Formatter) -> FmtResult {
546 Value::Number(Some(number)) => write!(f, "{number:?}"),
547 Value::Number(None) => write!(f, "SYSMIS"),
548 Value::String(bytes) => write!(f, "{:?}", bytes),
554 fn read<R: Read>(r: &mut R, var_type: VarType, endian: Endian) -> Result<Value, IoError> {
556 &UntypedValue(read_bytes(r)?),
562 pub fn from_raw(raw: &UntypedValue, var_type: VarType, endian: Endian) -> Value {
564 VarType::String => Value::String(UnencodedStr(raw.0)),
565 VarType::Numeric => {
566 let number: f64 = endian.parse(raw.0);
567 Value::Number((number != -f64::MAX).then_some(number))
572 fn read_case<R: Read + Seek>(
574 var_types: &[VarType],
576 ) -> Result<Option<Vec<Value>>, Error> {
577 let case_start = reader.stream_position()?;
578 let mut values = Vec::with_capacity(var_types.len());
579 for (i, &var_type) in var_types.iter().enumerate() {
580 let Some(raw) = try_read_bytes(reader)? else {
584 let offset = reader.stream_position()?;
585 return Err(Error::EofInCase {
587 case_ofs: offset - case_start,
588 case_len: var_types.len() * 8,
592 values.push(Value::from_raw(&UntypedValue(raw), var_type, endian));
597 fn read_compressed_case<R: Read + Seek>(
599 var_types: &[VarType],
600 codes: &mut VecDeque<u8>,
603 ) -> Result<Option<Vec<Value>>, Error> {
604 let case_start = reader.stream_position()?;
605 let mut values = Vec::with_capacity(var_types.len());
606 for (i, &var_type) in var_types.iter().enumerate() {
608 let Some(code) = codes.pop_front() else {
609 let Some(new_codes): Option<[u8; 8]> = try_read_bytes(reader)? else {
613 let offset = reader.stream_position()?;
614 return Err(Error::EofInCompressedCase {
616 case_ofs: offset - case_start,
620 codes.extend(new_codes.into_iter());
625 1..=251 => match var_type {
626 VarType::Numeric => break Value::Number(Some(code as f64 - bias)),
628 break Value::String(UnencodedStr(endian.to_bytes(code as f64 - bias)))
635 let offset = reader.stream_position()?;
636 return Err(Error::PartialCompressedCase {
638 case_ofs: offset - case_start,
643 break Value::from_raw(&UntypedValue(read_bytes(reader)?), var_type, endian)
645 254 => match var_type {
646 VarType::String => break Value::String(UnencodedStr(*b" ")), // XXX EBCDIC
647 VarType::Numeric => {
648 return Err(Error::CompressedStringExpected {
650 case_ofs: reader.stream_position()? - case_start,
654 255 => match var_type {
655 VarType::Numeric => break Value::Number(None),
657 return Err(Error::CompressedNumberExpected {
659 case_ofs: reader.stream_position()? - case_start,
671 struct ZlibDecodeMultiple<R>
675 reader: Option<ZlibDecoder<R>>,
678 impl<R> ZlibDecodeMultiple<R>
682 fn new(reader: R) -> ZlibDecodeMultiple<R> {
684 reader: Some(ZlibDecoder::new(reader)),
689 impl<R> Read for ZlibDecodeMultiple<R>
693 fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
695 match self.reader.as_mut().unwrap().read(buf)? {
697 let inner = self.reader.take().unwrap().into_inner();
698 self.reader = Some(ZlibDecoder::new(inner));
706 impl<R> Seek for ZlibDecodeMultiple<R>
710 fn seek(&mut self, pos: SeekFrom) -> Result<u64, IoError> {
711 self.reader.as_mut().unwrap().get_mut().seek(pos)
716 state: Option<Box<dyn State>>,
720 pub fn new<R: Read + Seek + 'static>(reader: R) -> Result<Reader, Error> {
722 state: Some(state::new(reader)),
725 pub fn collect_headers(&mut self) -> Result<Vec<Record>, Error> {
726 let mut headers = Vec::new();
729 Record::EndOfHeaders(_) => break,
730 r => headers.push(r),
737 impl Iterator for Reader {
738 type Item = Result<Record, Error>;
740 fn next(&mut self) -> Option<Self::Item> {
741 match self.state.take()?.read() {
742 Ok(Some((record, next_state))) => {
743 self.state = Some(next_state);
747 Err(error) => Some(Err(error)),
752 impl FusedIterator for Reader {}
754 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
755 pub struct Spec(pub u32);
757 impl Debug for Spec {
758 fn fmt(&self, f: &mut Formatter) -> FmtResult {
759 let type_ = format_name(self.0 >> 16);
760 let w = (self.0 >> 8) & 0xff;
761 let d = self.0 & 0xff;
762 write!(f, "{:06x} ({type_}{w}.{d})", self.0)
766 fn format_name(type_: u32) -> Cow<'static, str> {
805 _ => return format!("<unknown format {type_}>").into(),
811 pub struct MissingValues {
812 /// Individual missing values, up to 3 of them.
813 pub values: Vec<Value>,
815 /// Optional range of missing values.
816 pub range: Option<(Value, Value)>,
819 impl Debug for MissingValues {
820 fn fmt(&self, f: &mut Formatter) -> FmtResult {
821 for (i, value) in self.values.iter().enumerate() {
825 write!(f, "{value:?}")?;
828 if let Some((low, high)) = self.range {
829 if !self.values.is_empty() {
832 write!(f, "{low:?} THRU {high:?}")?;
844 fn is_empty(&self) -> bool {
845 self.values.is_empty() && self.range.is_none()
848 fn read<R: Read + Seek>(
854 ) -> Result<MissingValues, Error> {
855 let (n_values, has_range) = match (width, code) {
856 (_, 0..=3) => (code, false),
857 (0, -2) => (0, true),
858 (0, -3) => (1, true),
859 (0, _) => return Err(Error::BadNumericMissingValueCode { offset, code }),
860 (_, _) => return Err(Error::BadStringMissingValueCode { offset, code }),
863 let var_type = VarType::from_width(width);
865 let mut values = Vec::new();
866 for _ in 0..n_values {
867 values.push(Value::read(r, var_type, endian)?);
869 let range = if has_range {
870 let low = Value::read(r, var_type, endian)?;
871 let high = Value::read(r, var_type, endian)?;
876 Ok(MissingValues { values, range })
881 pub struct VariableRecord {
882 /// Range of offsets in file.
883 pub offsets: Range<u64>,
885 /// Variable width, in the range -1..=255.
888 /// Variable name, padded on the right with spaces.
889 pub name: UnencodedStr<8>,
892 pub print_format: Spec,
895 pub write_format: Spec,
898 pub missing_values: MissingValues,
900 /// Optional variable label.
901 pub label: Option<UnencodedString>,
904 impl Debug for VariableRecord {
905 fn fmt(&self, f: &mut Formatter) -> FmtResult {
910 match self.width.cmp(&0) {
911 Ordering::Greater => "string",
912 Ordering::Equal => "numeric",
913 Ordering::Less => "long string continuation record",
916 writeln!(f, "Print format: {:?}", self.print_format)?;
917 writeln!(f, "Write format: {:?}", self.write_format)?;
918 writeln!(f, "Name: {:?}", &self.name)?;
919 writeln!(f, "Variable label: {:?}", self.label)?;
920 writeln!(f, "Missing values: {:?}", self.missing_values)
924 impl VariableRecord {
925 fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<VariableRecord, Error> {
926 let start_offset = r.stream_position()?;
927 let width: i32 = endian.parse(read_bytes(r)?);
928 let code_offset = r.stream_position()?;
929 let has_variable_label: u32 = endian.parse(read_bytes(r)?);
930 let missing_value_code: i32 = endian.parse(read_bytes(r)?);
931 let print_format = Spec(endian.parse(read_bytes(r)?));
932 let write_format = Spec(endian.parse(read_bytes(r)?));
933 let name = UnencodedStr::<8>(read_bytes(r)?);
935 let label = match has_variable_label {
938 let len: u32 = endian.parse(read_bytes(r)?);
939 let read_len = len.min(65535) as usize;
940 let label = UnencodedString(read_vec(r, read_len)?);
942 let padding_bytes = Integer::next_multiple_of(&len, &4) - len;
943 let _ = read_vec(r, padding_bytes as usize)?;
948 return Err(Error::BadVariableLabelCode {
951 code: has_variable_label,
957 MissingValues::read(r, start_offset, width, missing_value_code, endian)?;
959 let end_offset = r.stream_position()?;
962 offsets: start_offset..end_offset,
973 #[derive(Copy, Clone)]
974 pub struct UntypedValue(pub [u8; 8]);
976 impl Debug for UntypedValue {
977 fn fmt(&self, f: &mut Formatter) -> FmtResult {
978 let little: f64 = Endian::Little.parse(self.0);
979 let little = format!("{:?}", little);
980 let big: f64 = Endian::Big.parse(self.0);
981 let big = format!("{:?}", big);
982 let number = if little.len() <= big.len() {
987 write!(f, "{number}")?;
989 let string = default_decode(&self.0);
991 .split(|c: char| c == '\0' || c.is_control())
994 write!(f, "{string:?}")?;
1000 pub struct UnencodedString(pub Vec<u8>);
1002 impl From<Vec<u8>> for UnencodedString {
1003 fn from(source: Vec<u8>) -> Self {
1008 impl From<&[u8]> for UnencodedString {
1009 fn from(source: &[u8]) -> Self {
1014 impl Debug for UnencodedString {
1015 fn fmt(&self, f: &mut Formatter) -> FmtResult {
1016 write!(f, "{:?}", default_decode(self.0.as_slice()))
1020 #[derive(Copy, Clone)]
1021 pub struct UnencodedStr<const N: usize>(pub [u8; N]);
1023 impl<const N: usize> From<[u8; N]> for UnencodedStr<N> {
1024 fn from(source: [u8; N]) -> Self {
1029 impl<const N: usize> Debug for UnencodedStr<N> {
1030 fn fmt(&self, f: &mut Formatter) -> FmtResult {
1031 write!(f, "{:?}", default_decode(&self.0))
1036 pub struct ValueLabelRecord {
1037 /// Range of offsets in file.
1038 pub offsets: Range<u64>,
1041 pub labels: Vec<(UntypedValue, UnencodedString)>,
1043 /// The 1-based indexes of the variable indexes.
1044 pub dict_indexes: Vec<u32>,
1047 impl Debug for ValueLabelRecord {
1048 fn fmt(&self, f: &mut Formatter) -> FmtResult {
1049 writeln!(f, "labels: ")?;
1050 for (value, label) in self.labels.iter() {
1051 writeln!(f, "{value:?}: {label:?}")?;
1053 write!(f, "apply to variables")?;
1054 for dict_index in self.dict_indexes.iter() {
1055 write!(f, " #{dict_index}")?;
1061 impl Header for ValueLabelRecord {
1062 fn offsets(&self) -> Range<u64> {
1063 self.offsets.clone()
1067 impl ValueLabelRecord {
1068 /// Maximum number of value labels in a record.
1069 pub const MAX_LABELS: u32 = u32::MAX / 8;
1071 /// Maximum number of variable indexes in a record.
1072 pub const MAX_INDEXES: u32 = u32::MAX / 8;
1074 fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<ValueLabelRecord, Error> {
1075 let label_offset = r.stream_position()?;
1076 let n: u32 = endian.parse(read_bytes(r)?);
1077 if n > Self::MAX_LABELS {
1078 return Err(Error::BadNumberOfValueLabels {
1079 offset: label_offset,
1081 max: Self::MAX_LABELS,
1085 let mut labels = Vec::new();
1087 let value = UntypedValue(read_bytes(r)?);
1088 let label_len: u8 = endian.parse(read_bytes(r)?);
1089 let label_len = label_len as usize;
1090 let padded_len = Integer::next_multiple_of(&(label_len + 1), &8);
1092 let mut label = read_vec(r, padded_len - 1)?;
1093 label.truncate(label_len);
1094 labels.push((value, UnencodedString(label)));
1097 let index_offset = r.stream_position()?;
1098 let rec_type: u32 = endian.parse(read_bytes(r)?);
1100 return Err(Error::ExpectedVarIndexRecord {
1101 offset: index_offset,
1106 let n: u32 = endian.parse(read_bytes(r)?);
1107 if n > Self::MAX_INDEXES {
1108 return Err(Error::BadNumberOfVarIndexes {
1109 offset: index_offset,
1111 max: Self::MAX_INDEXES,
1114 let mut dict_indexes = Vec::with_capacity(n as usize);
1116 dict_indexes.push(endian.parse(read_bytes(r)?));
1119 let end_offset = r.stream_position()?;
1120 Ok(ValueLabelRecord {
1121 offsets: label_offset..end_offset,
1128 #[derive(Clone, Debug)]
1129 pub struct DocumentRecord {
1130 pub offsets: Range<u64>,
1132 /// The document, as an array of 80-byte lines.
1133 pub lines: Vec<DocumentLine>,
1136 pub type DocumentLine = UnencodedStr<{ DocumentRecord::LINE_LEN }>;
1138 impl DocumentRecord {
1139 /// Length of a line in a document. Document lines are fixed-length and
1140 /// padded on the right with spaces.
1141 pub const LINE_LEN: usize = 80;
1143 /// Maximum number of lines we will accept in a document. This is simply
1144 /// the maximum number that will fit in a 32-bit space.
1145 pub const MAX_LINES: usize = i32::MAX as usize / Self::LINE_LEN;
1147 fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<DocumentRecord, Error> {
1148 let start_offset = r.stream_position()?;
1149 let n: u32 = endian.parse(read_bytes(r)?);
1151 if n > Self::MAX_LINES {
1152 Err(Error::BadDocumentLength {
1153 offset: start_offset,
1155 max: Self::MAX_LINES,
1158 let mut lines = Vec::with_capacity(n);
1160 lines.push(UnencodedStr::<{ DocumentRecord::LINE_LEN }>(read_bytes(r)?));
1162 let end_offset = r.stream_position()?;
1164 offsets: start_offset..end_offset,
1171 impl Header for DocumentRecord {
1172 fn offsets(&self) -> Range<u64> {
1173 self.offsets.clone()
1177 trait ExtensionRecord {
1179 const SIZE: Option<u32>;
1180 const COUNT: Option<u32>;
1181 const NAME: &'static str;
1182 fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error>;
1185 #[derive(Clone, Debug)]
1186 pub struct IntegerInfoRecord {
1187 pub offsets: Range<u64>,
1188 pub version: (i32, i32, i32),
1189 pub machine_code: i32,
1190 pub floating_point_rep: i32,
1191 pub compression_code: i32,
1192 pub endianness: i32,
1193 pub character_code: i32,
1196 impl ExtensionRecord for IntegerInfoRecord {
1197 const SUBTYPE: u32 = 3;
1198 const SIZE: Option<u32> = Some(4);
1199 const COUNT: Option<u32> = Some(8);
1200 const NAME: &'static str = "integer record";
1202 fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1203 ext.check_size::<Self>()?;
1205 let mut input = &ext.data[..];
1206 let data: Vec<i32> = (0..8)
1207 .map(|_| endian.parse(read_bytes(&mut input).unwrap()))
1209 Ok(Record::IntegerInfo(IntegerInfoRecord {
1210 offsets: ext.offsets.clone(),
1211 version: (data[0], data[1], data[2]),
1212 machine_code: data[3],
1213 floating_point_rep: data[4],
1214 compression_code: data[5],
1215 endianness: data[6],
1216 character_code: data[7],
1221 #[derive(Clone, Debug)]
1222 pub struct FloatInfoRecord {
1228 impl ExtensionRecord for FloatInfoRecord {
1229 const SUBTYPE: u32 = 4;
1230 const SIZE: Option<u32> = Some(8);
1231 const COUNT: Option<u32> = Some(3);
1232 const NAME: &'static str = "floating point record";
1234 fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1235 ext.check_size::<Self>()?;
1237 let mut input = &ext.data[..];
1238 let data: Vec<f64> = (0..3)
1239 .map(|_| endian.parse(read_bytes(&mut input).unwrap()))
1241 Ok(Record::FloatInfo(FloatInfoRecord {
1249 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1250 pub enum CategoryLabels {
1255 #[derive(Clone, Debug)]
1256 pub enum MultipleResponseType {
1258 value: UnencodedString,
1259 labels: CategoryLabels,
1264 impl MultipleResponseType {
1265 fn parse(input: &[u8]) -> Result<(MultipleResponseType, &[u8]), Error> {
1266 let (mr_type, input) = match input.split_first() {
1267 Some((b'C', input)) => (MultipleResponseType::MultipleCategory, input),
1268 Some((b'D', input)) => {
1269 let (value, input) = parse_counted_string(input)?;
1271 MultipleResponseType::MultipleDichotomy {
1273 labels: CategoryLabels::VarLabels,
1278 Some((b'E', input)) => {
1279 let (labels, input) = if let Some(rest) = input.strip_prefix(b" 1 ") {
1280 (CategoryLabels::CountedValues, rest)
1281 } else if let Some(rest) = input.strip_prefix(b" 11 ") {
1282 (CategoryLabels::VarLabels, rest)
1284 return Err(Error::TBD);
1286 let (value, input) = parse_counted_string(input)?;
1288 MultipleResponseType::MultipleDichotomy { value, labels },
1292 _ => return Err(Error::TBD),
1294 Ok((mr_type, input))
1298 #[derive(Clone, Debug)]
1299 pub struct MultipleResponseSet {
1300 pub name: UnencodedString,
1301 pub label: UnencodedString,
1302 pub mr_type: MultipleResponseType,
1303 pub short_names: Vec<UnencodedString>,
1306 impl MultipleResponseSet {
1307 fn parse(input: &[u8]) -> Result<(MultipleResponseSet, &[u8]), Error> {
1308 let Some(equals) = input.iter().position(|&b| b == b'=') else {
1309 return Err(Error::TBD);
1311 let (name, input) = input.split_at(equals);
1312 let (mr_type, input) = MultipleResponseType::parse(input)?;
1313 let Some(input) = input.strip_prefix(b" ") else {
1314 return Err(Error::TBD);
1316 let (label, mut input) = parse_counted_string(input)?;
1317 let mut vars = Vec::new();
1318 while input.first() != Some(&b'\n') {
1319 match input.split_first() {
1320 Some((b' ', rest)) => {
1321 let Some(length) = rest.iter().position(|b| b" \n".contains(b)) else {
1322 return Err(Error::TBD);
1324 let (var, rest) = rest.split_at(length);
1325 if !var.is_empty() {
1326 vars.push(var.into());
1330 _ => return Err(Error::TBD),
1333 while input.first() == Some(&b'\n') {
1334 input = &input[1..];
1337 MultipleResponseSet {
1348 #[derive(Clone, Debug)]
1349 pub struct MultipleResponseRecord(pub Vec<MultipleResponseSet>);
1351 impl ExtensionRecord for MultipleResponseRecord {
1352 const SUBTYPE: u32 = 7;
1353 const SIZE: Option<u32> = Some(1);
1354 const COUNT: Option<u32> = None;
1355 const NAME: &'static str = "multiple response set record";
1357 fn parse(ext: &Extension, _endian: Endian) -> Result<Record, Error> {
1358 ext.check_size::<Self>()?;
1360 let mut input = &ext.data[..];
1361 let mut sets = Vec::new();
1362 while !input.is_empty() {
1363 let (set, rest) = MultipleResponseSet::parse(input)?;
1367 Ok(Record::MultipleResponse(MultipleResponseRecord(sets)))
1371 fn parse_counted_string(input: &[u8]) -> Result<(UnencodedString, &[u8]), Error> {
1372 let Some(space) = input.iter().position(|&b| b == b' ') else {
1373 return Err(Error::TBD);
1375 let Ok(length) = from_utf8(&input[..space]) else {
1376 return Err(Error::TBD);
1378 let Ok(length): Result<usize, _> = length.parse() else {
1379 return Err(Error::TBD);
1382 let input = &input[space + 1..];
1383 if input.len() < length {
1384 return Err(Error::TBD);
1387 let (string, rest) = input.split_at(length);
1388 Ok((string.into(), rest))
1391 #[derive(Clone, Debug)]
1392 pub struct VarDisplayRecord(pub Vec<u32>);
1394 impl ExtensionRecord for VarDisplayRecord {
1395 const SUBTYPE: u32 = 11;
1396 const SIZE: Option<u32> = Some(4);
1397 const COUNT: Option<u32> = None;
1398 const NAME: &'static str = "variable display record";
1400 fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1401 ext.check_size::<Self>()?;
1403 let mut input = &ext.data[..];
1404 let display = (0..ext.count)
1405 .map(|_| endian.parse(read_bytes(&mut input).unwrap()))
1407 Ok(Record::VarDisplay(VarDisplayRecord(display)))
1411 #[derive(Clone, Debug)]
1412 pub struct LongStringMissingValues {
1414 pub var_name: UnencodedString,
1417 pub missing_values: MissingValues,
1420 #[derive(Clone, Debug)]
1421 pub struct LongStringMissingValueSet(pub Vec<LongStringMissingValues>);
1423 impl ExtensionRecord for LongStringMissingValueSet {
1424 const SUBTYPE: u32 = 22;
1425 const SIZE: Option<u32> = Some(1);
1426 const COUNT: Option<u32> = None;
1427 const NAME: &'static str = "long string missing values record";
1429 fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1430 ext.check_size::<Self>()?;
1432 let mut input = &ext.data[..];
1433 let mut missing_value_set = Vec::new();
1434 while !input.is_empty() {
1435 let var_name = read_string(&mut input, endian)?;
1436 let n_missing_values: u8 = endian.parse(read_bytes(&mut input)?);
1437 let value_len: u32 = endian.parse(read_bytes(&mut input)?);
1439 let offset = (ext.data.len() - input.len() - 8) as u64 + ext.offsets.start;
1440 return Err(Error::BadLongMissingValueLength {
1441 record_offset: ext.offsets.start,
1446 let mut values = Vec::new();
1447 for i in 0..n_missing_values {
1448 let value: [u8; 8] = read_bytes(&mut input)?;
1449 let numeric_value: u64 = endian.parse(value);
1450 let value = if i > 0 && numeric_value == 8 {
1451 // Tolerate files written by old, buggy versions of PSPP
1452 // where we believed that the value_length was repeated
1453 // before each missing value.
1454 read_bytes(&mut input)?
1458 values.push(Value::String(UnencodedStr(value)));
1460 let missing_values = MissingValues {
1464 missing_value_set.push(LongStringMissingValues {
1469 Ok(Record::LongStringMissingValues(LongStringMissingValueSet(
1475 #[derive(Clone, Debug)]
1476 pub struct EncodingRecord(pub String);
1478 impl ExtensionRecord for EncodingRecord {
1479 const SUBTYPE: u32 = 20;
1480 const SIZE: Option<u32> = Some(1);
1481 const COUNT: Option<u32> = None;
1482 const NAME: &'static str = "encoding record";
1484 fn parse(ext: &Extension, _endian: Endian) -> Result<Record, Error> {
1485 ext.check_size::<Self>()?;
1487 Ok(Record::Encoding(EncodingRecord(
1488 String::from_utf8(ext.data.clone()).map_err(|_| Error::BadEncodingName {
1489 offset: ext.offsets.start,
1495 #[derive(Clone, Debug)]
1496 pub struct NumberOfCasesRecord {
1497 /// Always observed as 1.
1500 /// Number of cases.
1504 impl ExtensionRecord for NumberOfCasesRecord {
1505 const SUBTYPE: u32 = 16;
1506 const SIZE: Option<u32> = Some(8);
1507 const COUNT: Option<u32> = Some(2);
1508 const NAME: &'static str = "extended number of cases record";
1510 fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1511 ext.check_size::<Self>()?;
1513 let mut input = &ext.data[..];
1514 let one = endian.parse(read_bytes(&mut input)?);
1515 let n_cases = endian.parse(read_bytes(&mut input)?);
1517 Ok(Record::NumberOfCases(NumberOfCasesRecord { one, n_cases }))
1521 #[derive(Clone, Debug)]
1522 pub struct TextRecord {
1523 pub offsets: Range<u64>,
1525 /// The text content of the record.
1526 pub text: UnencodedString,
1529 impl From<Extension> for TextRecord {
1530 fn from(source: Extension) -> Self {
1532 offsets: source.offsets,
1533 text: source.data.into(),
1538 #[derive(Clone, Debug)]
1539 pub struct Extension {
1540 pub offsets: Range<u64>,
1545 /// Size of each data element.
1548 /// Number of data elements.
1551 /// `size * count` bytes of data.
1556 fn check_size<E: ExtensionRecord>(&self) -> Result<(), Error> {
1557 if let Some(expected_size) = E::SIZE {
1558 if self.size != expected_size {
1559 return Err(Error::BadRecordSize {
1560 offset: self.offsets.start,
1561 record: E::NAME.into(),
1567 if let Some(expected_count) = E::COUNT {
1568 if self.count != expected_count {
1569 return Err(Error::BadRecordCount {
1570 offset: self.offsets.start,
1571 record: E::NAME.into(),
1580 fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<Record, Error> {
1581 let subtype = endian.parse(read_bytes(r)?);
1582 let header_offset = r.stream_position()?;
1583 let size: u32 = endian.parse(read_bytes(r)?);
1584 let count = endian.parse(read_bytes(r)?);
1585 let Some(product) = size.checked_mul(count) else {
1586 return Err(Error::ExtensionRecordTooLarge {
1587 offset: header_offset,
1593 let start_offset = r.stream_position()?;
1594 let data = read_vec(r, product as usize)?;
1595 let end_offset = start_offset + product as u64;
1596 let extension = Extension {
1597 offsets: start_offset..end_offset,
1604 IntegerInfoRecord::SUBTYPE => Ok(IntegerInfoRecord::parse(&extension, endian)?),
1605 FloatInfoRecord::SUBTYPE => Ok(FloatInfoRecord::parse(&extension, endian)?),
1606 VarDisplayRecord::SUBTYPE => Ok(VarDisplayRecord::parse(&extension, endian)?),
1607 MultipleResponseRecord::SUBTYPE | 19 => {
1608 Ok(MultipleResponseRecord::parse(&extension, endian)?)
1610 LongStringValueLabelRecord::SUBTYPE => {
1611 Ok(LongStringValueLabelRecord::parse(&extension, endian)?)
1613 EncodingRecord::SUBTYPE => Ok(EncodingRecord::parse(&extension, endian)?),
1614 NumberOfCasesRecord::SUBTYPE => Ok(NumberOfCasesRecord::parse(&extension, endian)?),
1615 5 => Ok(Record::VariableSets(extension.into())),
1616 10 => Ok(Record::ProductInfo(extension.into())),
1617 13 => Ok(Record::LongNames(extension.into())),
1618 14 => Ok(Record::VeryLongStrings(extension.into())),
1619 17 => Ok(Record::FileAttributes(extension.into())),
1620 18 => Ok(Record::VariableAttributes(extension.into())),
1621 _ => Ok(Record::OtherExtension(extension)),
1626 #[derive(Clone, Debug)]
1627 pub struct ZHeader {
1628 /// File offset to the start of the record.
1631 /// File offset to the ZLIB data header.
1632 pub zheader_offset: u64,
1634 /// File offset to the ZLIB trailer.
1635 pub ztrailer_offset: u64,
1637 /// Length of the ZLIB trailer in bytes.
1638 pub ztrailer_len: u64,
1642 fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<ZHeader, Error> {
1643 let offset = r.stream_position()?;
1644 let zheader_offset: u64 = endian.parse(read_bytes(r)?);
1645 let ztrailer_offset: u64 = endian.parse(read_bytes(r)?);
1646 let ztrailer_len: u64 = endian.parse(read_bytes(r)?);
1657 #[derive(Clone, Debug)]
1658 pub struct ZTrailer {
1659 /// File offset to the start of the record.
1662 /// Compression bias as a negative integer, e.g. -100.
1665 /// Always observed as zero.
1668 /// Uncompressed size of each block, except possibly the last. Only
1669 /// `0x3ff000` has been observed so far.
1670 pub block_size: u32,
1672 /// Block descriptors, always `(ztrailer_len - 24) / 24)` of them.
1673 pub blocks: Vec<ZBlock>,
1676 #[derive(Clone, Debug)]
1678 /// Offset of block of data if simple compression were used.
1679 pub uncompressed_ofs: u64,
1681 /// Actual offset within the file of the compressed data block.
1682 pub compressed_ofs: u64,
1684 /// The number of bytes in this data block after decompression. This is
1685 /// `block_size` in every data block but the last, which may be smaller.
1686 pub uncompressed_size: u32,
1688 /// The number of bytes in this data block, as stored compressed in this
1690 pub compressed_size: u32,
1694 fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<ZBlock, Error> {
1696 uncompressed_ofs: endian.parse(read_bytes(r)?),
1697 compressed_ofs: endian.parse(read_bytes(r)?),
1698 uncompressed_size: endian.parse(read_bytes(r)?),
1699 compressed_size: endian.parse(read_bytes(r)?),
1705 fn read<R: Read + Seek>(
1710 ) -> Result<Option<ZTrailer>, Error> {
1711 let start_offset = reader.stream_position()?;
1712 if reader.seek(SeekFrom::Start(ztrailer_ofs)).is_err() {
1715 let int_bias = endian.parse(read_bytes(reader)?);
1716 let zero = endian.parse(read_bytes(reader)?);
1717 let block_size = endian.parse(read_bytes(reader)?);
1718 let n_blocks: u32 = endian.parse(read_bytes(reader)?);
1719 let expected_n_blocks = (ztrailer_len - 24) / 24;
1720 if n_blocks as u64 != expected_n_blocks {
1721 return Err(Error::BadZlibTrailerNBlocks {
1722 offset: ztrailer_ofs,
1728 let blocks = (0..n_blocks)
1729 .map(|_| ZBlock::read(reader, endian))
1730 .collect::<Result<Vec<_>, _>>()?;
1731 reader.seek(SeekFrom::Start(start_offset))?;
1733 offset: ztrailer_ofs,
1742 fn try_read_bytes<const N: usize, R: Read>(r: &mut R) -> Result<Option<[u8; N]>, IoError> {
1743 let mut buf = [0; N];
1744 let n = r.read(&mut buf)?;
1747 r.read_exact(&mut buf[n..])?;
1755 fn read_bytes<const N: usize, R: Read>(r: &mut R) -> Result<[u8; N], IoError> {
1756 let mut buf = [0; N];
1757 r.read_exact(&mut buf)?;
1761 fn read_vec<R: Read>(r: &mut R, n: usize) -> Result<Vec<u8>, IoError> {
1762 let mut vec = vec![0; n];
1763 r.read_exact(&mut vec)?;
1767 fn read_string<R: Read>(r: &mut R, endian: Endian) -> Result<UnencodedString, IoError> {
1768 let length: u32 = endian.parse(read_bytes(r)?);
1769 Ok(read_vec(r, length as usize)?.into())
1772 #[derive(Clone, Debug)]
1773 pub struct LongStringValueLabels {
1774 pub var_name: UnencodedString,
1777 /// `(value, label)` pairs, where each value is `width` bytes.
1778 pub labels: Vec<(UnencodedString, UnencodedString)>,
1781 #[derive(Clone, Debug)]
1782 pub struct LongStringValueLabelRecord(pub Vec<LongStringValueLabels>);
1784 impl ExtensionRecord for LongStringValueLabelRecord {
1785 const SUBTYPE: u32 = 21;
1786 const SIZE: Option<u32> = Some(1);
1787 const COUNT: Option<u32> = None;
1788 const NAME: &'static str = "long string value labels record";
1790 fn parse(ext: &Extension, endian: Endian) -> Result<Record, Error> {
1791 ext.check_size::<Self>()?;
1793 let mut input = &ext.data[..];
1794 let mut label_set = Vec::new();
1795 while !input.is_empty() {
1796 let var_name = read_string(&mut input, endian)?;
1797 let width: u32 = endian.parse(read_bytes(&mut input)?);
1798 let n_labels: u32 = endian.parse(read_bytes(&mut input)?);
1799 let mut labels = Vec::new();
1800 for _ in 0..n_labels {
1801 let value = read_string(&mut input, endian)?;
1802 let label = read_string(&mut input, endian)?;
1803 labels.push((value, label));
1805 label_set.push(LongStringValueLabels {
1811 Ok(Record::LongStringValueLabels(LongStringValueLabelRecord(