sas7bdat 0.2.0

Rust library + CLI for decoding SAS7BDAT datasets and streaming them to modern formats.
Documentation
use super::constants::{
    SAS_SUBHEADER_SIGNATURE_COLUMN_ATTRS, SAS_SUBHEADER_SIGNATURE_COLUMN_FORMAT,
    SAS_SUBHEADER_SIGNATURE_COLUMN_LIST, SAS_SUBHEADER_SIGNATURE_COLUMN_NAME,
    SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE, SAS_SUBHEADER_SIGNATURE_COLUMN_TEXT,
    SAS_SUBHEADER_SIGNATURE_COUNTS, SAS_SUBHEADER_SIGNATURE_ROW_SIZE,
};
use crate::{
    dataset::Endianness,
    error::{Error, Result, Section},
    parser::core::byteorder::{read_u32, read_u64},
};
use std::borrow::Cow;

pub struct PointerInfo {
    pub offset: usize,
    pub length: usize,
    pub compression: u8,
    pub is_compressed_data: bool,
}

pub fn parse_pointer(pointer: &[u8], uses_u64: bool, endian: Endianness) -> Result<PointerInfo> {
    if uses_u64 {
        if pointer.len() < 18 {
            return Err(Error::Corrupted {
                section: Section::Header,
                details: Cow::from("64-bit pointer too short"),
            });
        }
        let offset =
            usize::try_from(read_u64(endian, &pointer[0..8])).map_err(|_| Error::Unsupported {
                feature: Cow::from("64-bit pointer offset exceeds platform pointer width"),
            })?;
        let length =
            usize::try_from(read_u64(endian, &pointer[8..16])).map_err(|_| Error::Unsupported {
                feature: Cow::from("64-bit pointer length exceeds platform pointer width"),
            })?;
        Ok(PointerInfo {
            offset,
            length,
            compression: pointer[16],
            is_compressed_data: pointer[17] != 0,
        })
    } else {
        if pointer.len() < 10 {
            return Err(Error::Corrupted {
                section: Section::Header,
                details: Cow::from("32-bit pointer too short"),
            });
        }
        let offset =
            usize::try_from(read_u32(endian, &pointer[0..4])).map_err(|_| Error::Unsupported {
                feature: Cow::from("32-bit pointer offset exceeds platform pointer width"),
            })?;
        let length =
            usize::try_from(read_u32(endian, &pointer[4..8])).map_err(|_| Error::Unsupported {
                feature: Cow::from("32-bit pointer length exceeds platform pointer width"),
            })?;
        Ok(PointerInfo {
            offset,
            length,
            compression: pointer[8],
            is_compressed_data: pointer[9] != 0,
        })
    }
}

pub fn read_signature(data: &[u8], endian: Endianness, uses_u64: bool) -> u32 {
    if data.len() < 4 {
        return 0;
    }
    let mut signature = read_u32(endian, &data[0..4]);
    if matches!(endian, Endianness::Big) && signature == u32::MAX && uses_u64 && data.len() >= 8 {
        signature = read_u32(endian, &data[4..8]);
    }
    signature
}

pub const fn signature_is_recognized(signature: u32) -> bool {
    matches!(
        signature,
        SAS_SUBHEADER_SIGNATURE_COLUMN_TEXT
            | SAS_SUBHEADER_SIGNATURE_COLUMN_ATTRS
            | SAS_SUBHEADER_SIGNATURE_COLUMN_FORMAT
            | SAS_SUBHEADER_SIGNATURE_COLUMN_NAME
            | SAS_SUBHEADER_SIGNATURE_COLUMN_SIZE
            | SAS_SUBHEADER_SIGNATURE_ROW_SIZE
            | SAS_SUBHEADER_SIGNATURE_COUNTS
            | SAS_SUBHEADER_SIGNATURE_COLUMN_LIST
    )
}