use bytemuck::{Pod, Zeroable};
use crate::{DrivenError, Result};
pub const INFINITY_MAGIC: &[u8; 4] = b"DRV\x00";
#[repr(C)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct InfinityHeader {
pub magic: [u8; 4],
pub version: u16,
pub flags: u16,
pub checksum: [u8; 16],
pub _reserved: [u8; 8],
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
pub struct SectionOffsets {
pub string_table: u32,
pub persona: u32,
pub standards: u32,
pub workflow: u32,
pub context: u32,
pub signature: u32,
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
pub struct RuleFlags(pub u16);
impl RuleFlags {
pub const HAS_PERSONA: u16 = 1 << 0;
pub const HAS_STANDARDS: u16 = 1 << 1;
pub const HAS_WORKFLOW: u16 = 1 << 2;
pub const HAS_CONTEXT: u16 = 1 << 3;
pub const IS_SIGNED: u16 = 1 << 4;
pub const USES_FUSION: u16 = 1 << 5;
pub const COMPRESSED: u16 = 1 << 6;
pub const SIMD_OPTIMIZED: u16 = 1 << 7;
pub fn new() -> Self {
Self(0)
}
pub fn has_persona(self) -> bool {
self.0 & Self::HAS_PERSONA != 0
}
pub fn has_standards(self) -> bool {
self.0 & Self::HAS_STANDARDS != 0
}
pub fn has_workflow(self) -> bool {
self.0 & Self::HAS_WORKFLOW != 0
}
pub fn has_context(self) -> bool {
self.0 & Self::HAS_CONTEXT != 0
}
pub fn is_signed(self) -> bool {
self.0 & Self::IS_SIGNED != 0
}
pub fn uses_fusion(self) -> bool {
self.0 & Self::USES_FUSION != 0
}
pub fn set(&mut self, flag: u16) {
self.0 |= flag;
}
pub fn clear(&mut self, flag: u16) {
self.0 &= !flag;
}
}
impl InfinityHeader {
pub fn new(version: u16, flags: RuleFlags) -> Self {
Self {
magic: *INFINITY_MAGIC,
version,
flags: flags.0,
checksum: [0; 16],
_reserved: [0; 8],
}
}
pub fn from_bytes(data: &[u8]) -> Result<&Self> {
if data.len() < std::mem::size_of::<Self>() {
return Err(DrivenError::InvalidBinary(
"Data too small for infinity header".into(),
));
}
let header: &Self = bytemuck::from_bytes(&data[..std::mem::size_of::<Self>()]);
if &header.magic != INFINITY_MAGIC {
return Err(DrivenError::InvalidBinary("Invalid magic bytes".into()));
}
Ok(header)
}
pub fn flags(&self) -> RuleFlags {
RuleFlags(self.flags)
}
pub fn to_bytes(&self) -> &[u8] {
bytemuck::bytes_of(self)
}
pub const fn size() -> usize {
std::mem::size_of::<Self>()
}
}
#[derive(Debug)]
pub struct InfinityRule<'a> {
pub header: &'a InfinityHeader,
pub section_offsets: &'a SectionOffsets,
data: &'a [u8],
}
impl<'a> InfinityRule<'a> {
pub fn from_bytes(data: &'a [u8]) -> Result<Self> {
let header_size = std::mem::size_of::<InfinityHeader>();
let offsets_size = std::mem::size_of::<SectionOffsets>();
let min_size = header_size + offsets_size;
if data.len() < min_size {
return Err(DrivenError::InvalidBinary(
"Data too small for infinity rule".into(),
));
}
let header = InfinityHeader::from_bytes(data)?;
let section_offsets: &SectionOffsets =
bytemuck::from_bytes(&data[header_size..header_size + offsets_size]);
Ok(Self {
header,
section_offsets,
data,
})
}
pub fn string_table_data(&self) -> Option<&'a [u8]> {
let offset = self.section_offsets.string_table as usize;
if offset == 0 || offset >= self.data.len() {
return None;
}
Some(&self.data[offset..])
}
pub fn persona_data(&self) -> Option<&'a [u8]> {
if !self.header.flags().has_persona() {
return None;
}
let offset = self.section_offsets.persona as usize;
if offset == 0 || offset >= self.data.len() {
return None;
}
Some(&self.data[offset..])
}
pub fn standards_data(&self) -> Option<&'a [u8]> {
if !self.header.flags().has_standards() {
return None;
}
let offset = self.section_offsets.standards as usize;
if offset == 0 || offset >= self.data.len() {
return None;
}
Some(&self.data[offset..])
}
pub fn workflow_data(&self) -> Option<&'a [u8]> {
if !self.header.flags().has_workflow() {
return None;
}
let offset = self.section_offsets.workflow as usize;
if offset == 0 || offset >= self.data.len() {
return None;
}
Some(&self.data[offset..])
}
pub fn context_data(&self) -> Option<&'a [u8]> {
if !self.header.flags().has_context() {
return None;
}
let offset = self.section_offsets.context as usize;
if offset == 0 || offset >= self.data.len() {
return None;
}
Some(&self.data[offset..])
}
pub fn is_signed(&self) -> bool {
self.header.flags().is_signed()
}
pub fn raw_data(&self) -> &'a [u8] {
self.data
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_header_size() {
assert_eq!(InfinityHeader::size(), 32);
}
#[test]
fn test_flags() {
let mut flags = RuleFlags::new();
assert!(!flags.has_persona());
flags.set(RuleFlags::HAS_PERSONA);
assert!(flags.has_persona());
flags.set(RuleFlags::IS_SIGNED);
assert!(flags.is_signed());
}
#[test]
fn test_header_roundtrip() {
let header = InfinityHeader::new(1, RuleFlags::new());
let bytes = header.to_bytes();
assert_eq!(bytes.len(), 32);
let parsed = InfinityHeader::from_bytes(bytes).unwrap();
assert_eq!(parsed.version, 1);
}
}