#[derive(Debug, thiserror::Error)]
pub enum OffsetError {
#[error("Buffer overrun: offset {offset} is beyond buffer length {buffer_len}")]
BufferOverrun {
offset: usize,
buffer_len: usize,
},
#[error("Invalid offset: {reason}")]
InvalidOffset {
reason: String,
},
#[error("Arithmetic overflow in offset calculation")]
ArithmeticOverflow,
}
pub fn resolve_absolute_offset(offset: i64, buffer: &[u8]) -> Result<usize, OffsetError> {
let buffer_len = buffer.len();
if offset >= 0 {
let abs_offset = usize::try_from(offset).map_err(|_| OffsetError::ArithmeticOverflow)?;
if abs_offset >= buffer_len {
return Err(OffsetError::BufferOverrun {
offset: abs_offset,
buffer_len,
});
}
Ok(abs_offset)
} else {
if offset == i64::MIN {
return Err(OffsetError::ArithmeticOverflow);
}
let offset_from_end =
usize::try_from(-offset).map_err(|_| OffsetError::ArithmeticOverflow)?;
if offset_from_end > buffer_len {
return Err(OffsetError::BufferOverrun {
offset: buffer_len.saturating_sub(offset_from_end),
buffer_len,
});
}
let resolved_offset = buffer_len - offset_from_end;
Ok(resolved_offset)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resolve_absolute_offset_positive() {
let buffer = b"Hello, World!";
assert_eq!(resolve_absolute_offset(0, buffer).unwrap(), 0);
assert_eq!(resolve_absolute_offset(1, buffer).unwrap(), 1);
assert_eq!(resolve_absolute_offset(7, buffer).unwrap(), 7);
assert_eq!(resolve_absolute_offset(12, buffer).unwrap(), 12); }
#[test]
fn test_resolve_absolute_offset_negative() {
let buffer = b"Hello, World!";
assert_eq!(resolve_absolute_offset(-1, buffer).unwrap(), 12); assert_eq!(resolve_absolute_offset(-6, buffer).unwrap(), 7); assert_eq!(resolve_absolute_offset(-13, buffer).unwrap(), 0); }
#[test]
fn test_resolve_absolute_offset_out_of_bounds_positive() {
let buffer = b"Hello";
let result = resolve_absolute_offset(5, buffer);
assert!(result.is_err());
match result.unwrap_err() {
OffsetError::BufferOverrun { offset, buffer_len } => {
assert_eq!(offset, 5);
assert_eq!(buffer_len, 5);
}
_ => panic!("Expected BufferOverrun error"),
}
let result = resolve_absolute_offset(100, buffer);
assert!(result.is_err());
}
#[test]
fn test_resolve_absolute_offset_out_of_bounds_negative() {
let buffer = b"Hi";
let result = resolve_absolute_offset(-3, buffer);
assert!(result.is_err());
match result.unwrap_err() {
OffsetError::BufferOverrun { .. } => {
}
_ => panic!("Expected BufferOverrun error"),
}
let result = resolve_absolute_offset(-100, buffer);
assert!(result.is_err());
}
#[test]
fn test_resolve_absolute_offset_empty_buffer() {
let buffer = b"";
assert!(resolve_absolute_offset(0, buffer).is_err());
assert!(resolve_absolute_offset(1, buffer).is_err());
assert!(resolve_absolute_offset(-1, buffer).is_err());
}
#[test]
fn test_resolve_absolute_offset_edge_cases() {
let buffer = b"X";
assert_eq!(resolve_absolute_offset(0, buffer).unwrap(), 0);
assert_eq!(resolve_absolute_offset(-1, buffer).unwrap(), 0);
assert!(resolve_absolute_offset(1, buffer).is_err());
assert!(resolve_absolute_offset(-2, buffer).is_err());
}
#[test]
fn test_large_buffer_offsets() {
let large_buffer = vec![0u8; 1024];
assert_eq!(resolve_absolute_offset(0, &large_buffer).unwrap(), 0);
assert_eq!(resolve_absolute_offset(512, &large_buffer).unwrap(), 512);
assert_eq!(resolve_absolute_offset(1023, &large_buffer).unwrap(), 1023);
assert_eq!(resolve_absolute_offset(-1, &large_buffer).unwrap(), 1023);
assert_eq!(resolve_absolute_offset(-512, &large_buffer).unwrap(), 512);
assert_eq!(resolve_absolute_offset(-1024, &large_buffer).unwrap(), 0);
assert!(resolve_absolute_offset(1024, &large_buffer).is_err());
assert!(resolve_absolute_offset(-1025, &large_buffer).is_err());
}
#[test]
fn test_offset_security_edge_cases() {
let buffer = b"test";
let overflow_cases = vec![i64::MAX, i64::MIN, i64::MAX - 1, i64::MIN + 1];
for offset in overflow_cases {
let result = resolve_absolute_offset(offset, buffer);
if let Ok(resolved) = result {
assert!(
resolved < buffer.len(),
"Resolved offset {resolved} exceeds buffer length {}",
buffer.len()
);
} else {
}
}
}
#[test]
fn test_offset_error_display() {
let error = OffsetError::BufferOverrun {
offset: 10,
buffer_len: 5,
};
let error_str = error.to_string();
assert!(error_str.contains("Buffer overrun"));
assert!(error_str.contains("10"));
assert!(error_str.contains('5'));
let error = OffsetError::InvalidOffset {
reason: "test reason".to_string(),
};
let error_str = error.to_string();
assert!(error_str.contains("Invalid offset"));
assert!(error_str.contains("test reason"));
let error = OffsetError::ArithmeticOverflow;
let error_str = error.to_string();
assert!(error_str.contains("Arithmetic overflow"));
}
#[test]
fn test_resolve_absolute_offset_arithmetic_overflow() {
let buffer = b"test";
let result = resolve_absolute_offset(i64::MIN, buffer);
assert!(result.is_err());
match result.unwrap_err() {
OffsetError::ArithmeticOverflow => {
}
_ => panic!("Expected ArithmeticOverflow error"),
}
}
}