From dd10854237ced9550ff24827296f47736852d301 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 24 Jul 2023 17:34:56 -0700 Subject: [PATCH] work --- rust/src/lib.rs | 78 ++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 2bde62b758..64e9801447 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -12,6 +12,9 @@ pub enum Error { #[error("Not an SPSS system file")] NotASystemFile, + #[error("Invalid magic number {0:?}")] + BadMagic([u8; 4]), + #[error("I/O error ({source})")] Io { #[from] @@ -95,26 +98,9 @@ pub struct Reader { zheader: Option, } -/// Magic number for a regular system file. -pub const ASCII_MAGIC: &[u8; 4] = b"$FL2"; - -/// Magic number for a system file that contains zlib-compressed data. -pub const ASCII_ZMAGIC: &[u8; 4] = b"$FL3"; - -/// Magic number for an EBDIC-encoded system file. This is `$FL2` encoded in -/// EBCDIC. -pub const EBCDIC_MAGIC: &[u8; 4] = &[0x5b, 0xc6, 0xd3, 0xf2]; - pub struct FileHeader { - /// First 4 bytes of the file, one of `ASCII_MAGIC`, `ASCII_ZMAGIC`, and - /// `EBCDIC_MAGIC`. - pub magic: [u8; 4], - - /// True if `magic` indicates that this file contained zlib-compressed data. - pub is_zsav: bool, - - /// True if `magic` indicates that this file contained EBCDIC data. - pub is_ebcdic: bool, + /// Magic number. + pub magic: Magic, /// Endianness of the data in the file header. pub endianness: Endian, @@ -141,6 +127,33 @@ pub struct FileHeader { pub file_label: [u8; 64], } +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Magic([u8; 4]); + +impl Magic { + /// Magic number for a regular system file. + pub const SAV: Magic = Magic(*b"$FL2"); + + /// Magic number for a system file that contains zlib-compressed data. + pub const ZSAV: Magic = Magic(*b"$FL3"); + + /// Magic number for an EBDIC-encoded system file. This is `$FL2` encoded + /// in EBCDIC. + pub const EBCDIC: Magic = Magic([0x5b, 0xc6, 0xd3, 0xf2]); +} + +impl TryFrom<[u8; 4]> for Magic { + type Error = Error; + + fn try_from(value: [u8; 4]) -> Result { + let magic = Magic(value); + match magic { + Magic::SAV | Magic::ZSAV | Magic::EBCDIC => Ok(magic), + _ => Err(Error::BadMagic(value)), + } + } +} + impl Reader { pub fn new(r: R, warn: impl Fn(Warning)) -> Result, Error> { let mut r = BufReader::new(r); @@ -165,9 +178,9 @@ impl Reader { } } let _: [u8; 4] = read_bytes(&mut r)?; - let zheader = match header.is_zsav { - true => Some(read_zheader(&mut r, e)?), - false => None, + let zheader = match header.magic { + Magic::ZSAV => Some(read_zheader(&mut r, e)?), + _ => None, }; Ok(Reader { @@ -183,12 +196,7 @@ impl Reader { fn read_header(r: &mut R, warn: impl Fn(Warning)) -> Result { let magic: [u8; 4] = read_bytes(r)?; - let (is_zsav, is_ebcdic) = match &magic { - ASCII_MAGIC => (false, false), - ASCII_ZMAGIC => (true, false), - EBCDIC_MAGIC => (false, true), - _ => return Err(Error::NotASystemFile), - }; + let magic: Magic = magic.try_into().map_err(|_| Error::NotASystemFile)?; let eye_catcher: [u8; 60] = read_bytes(r)?; let layout_code: [u8; 4] = read_bytes(r)?; @@ -201,12 +209,12 @@ fn read_header(r: &mut R, warn: impl Fn(Warning)) -> Result None, - (false, 1) => Some(Compression::Simple), - (true, 2) => Some(Compression::ZLib), - (false, code) => return Err(Error::InvalidSavCompression(code)), - (true, code) => return Err(Error::InvalidZsavCompression(code)), + let compression = match (magic, compression_code) { + (Magic::ZSAV, 2) => Some(Compression::ZLib), + (Magic::ZSAV, code) => return Err(Error::InvalidZsavCompression(code)), + (_, 0) => None, + (_, 1) => Some(Compression::Simple), + (_, code) => return Err(Error::InvalidSavCompression(code)), }; let weight_index: u32 = endianness.parse(read_bytes(r)?); @@ -227,8 +235,6 @@ fn read_header(r: &mut R, warn: impl Fn(Warning)) -> Result