mod absolute;
mod indirect;
mod relative;
pub use absolute::{OffsetError, resolve_absolute_offset};
use crate::LibmagicError;
use crate::parser::ast::OffsetSpec;
fn map_offset_error(e: &OffsetError, original_offset: i64) -> LibmagicError {
match e {
OffsetError::BufferOverrun {
offset,
buffer_len: _,
} => LibmagicError::EvaluationError(crate::error::EvaluationError::BufferOverrun {
offset: *offset,
}),
OffsetError::InvalidOffset { reason: _ } | OffsetError::ArithmeticOverflow => {
LibmagicError::EvaluationError(crate::error::EvaluationError::InvalidOffset {
offset: original_offset,
})
}
}
}
pub fn resolve_offset(spec: &OffsetSpec, buffer: &[u8]) -> Result<usize, LibmagicError> {
match spec {
OffsetSpec::Absolute(offset) => {
resolve_absolute_offset(*offset, buffer).map_err(|e| map_offset_error(&e, *offset))
}
OffsetSpec::Indirect { .. } => indirect::resolve_indirect_offset(spec, buffer),
OffsetSpec::Relative(_) => relative::resolve_relative_offset(spec, buffer),
OffsetSpec::FromEnd(offset) => {
resolve_absolute_offset(*offset, buffer).map_err(|e| map_offset_error(&e, *offset))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resolve_offset_absolute() {
let buffer = b"Test data for offset resolution";
let spec = OffsetSpec::Absolute(5);
let result = resolve_offset(&spec, buffer).unwrap();
assert_eq!(result, 5);
}
#[test]
fn test_resolve_offset_absolute_negative() {
let buffer = b"Test data";
let spec = OffsetSpec::Absolute(-4);
let result = resolve_offset(&spec, buffer).unwrap();
assert_eq!(result, 5); }
#[test]
fn test_resolve_offset_from_end() {
let buffer = b"Test data";
let spec = OffsetSpec::FromEnd(-3);
let result = resolve_offset(&spec, buffer).unwrap();
assert_eq!(result, 6); }
#[test]
fn test_resolve_offset_absolute_out_of_bounds() {
let buffer = b"Short";
let spec = OffsetSpec::Absolute(10);
let result = resolve_offset(&spec, buffer);
assert!(result.is_err());
match result.unwrap_err() {
LibmagicError::EvaluationError(crate::error::EvaluationError::BufferOverrun {
..
}) => {
}
_ => panic!("Expected EvaluationError with BufferOverrun"),
}
}
#[test]
fn test_resolve_offset_indirect_not_implemented() {
let buffer = b"Test data";
let spec = OffsetSpec::Indirect {
base_offset: 0,
pointer_type: crate::parser::ast::TypeKind::Byte { signed: true },
adjustment: 0,
endian: crate::parser::ast::Endianness::Little,
};
let result = resolve_offset(&spec, buffer);
assert!(result.is_err());
match result.unwrap_err() {
LibmagicError::EvaluationError(crate::error::EvaluationError::UnsupportedType {
type_name,
}) => {
assert!(type_name.contains("Indirect offsets not yet implemented"));
}
_ => panic!("Expected EvaluationError with UnsupportedType"),
}
}
#[test]
fn test_resolve_offset_relative_not_implemented() {
let buffer = b"Test data";
let spec = OffsetSpec::Relative(4);
let result = resolve_offset(&spec, buffer);
assert!(result.is_err());
match result.unwrap_err() {
LibmagicError::EvaluationError(crate::error::EvaluationError::UnsupportedType {
type_name,
}) => {
assert!(type_name.contains("Relative offsets not yet implemented"));
}
_ => panic!("Expected EvaluationError with UnsupportedType"),
}
}
#[test]
fn test_resolve_offset_comprehensive() {
let buffer = b"0123456789ABCDEF";
let test_cases = vec![
(OffsetSpec::Absolute(0), 0),
(OffsetSpec::Absolute(8), 8),
(OffsetSpec::Absolute(15), 15),
(OffsetSpec::Absolute(-1), 15),
(OffsetSpec::Absolute(-8), 8),
(OffsetSpec::Absolute(-16), 0),
(OffsetSpec::FromEnd(-1), 15),
(OffsetSpec::FromEnd(-8), 8),
(OffsetSpec::FromEnd(-16), 0),
];
for (spec, expected) in test_cases {
let result = resolve_offset(&spec, buffer).unwrap();
assert_eq!(result, expected, "Failed for spec: {spec:?}");
}
}
}