use crate::config::Config;
use crate::error::{JustcodeError, Result};
pub struct Reader<'a> {
data: &'a [u8],
position: usize,
config: Config,
}
impl<'a> Reader<'a> {
pub fn new(data: &'a [u8], config: Config) -> Self {
Self {
data,
position: 0,
config,
}
}
pub fn read_u8(&mut self) -> Result<u8> {
self.check_size(1)?;
let value = self.data[self.position];
self.position += 1;
Ok(value)
}
pub fn read_u16(&mut self) -> Result<u16> {
self.check_size(2)?;
let bytes = &self.data[self.position..self.position + 2];
self.position += 2;
Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
}
pub fn read_u32(&mut self) -> Result<u32> {
self.check_size(4)?;
let bytes = &self.data[self.position..self.position + 4];
self.position += 4;
Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
pub fn read_u64(&mut self) -> Result<u64> {
self.check_size(8)?;
let bytes = &self.data[self.position..self.position + 8];
self.position += 8;
Ok(u64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]))
}
pub fn read_i8(&mut self) -> Result<i8> {
Ok(self.read_u8()? as i8)
}
pub fn read_i16(&mut self) -> Result<i16> {
Ok(self.read_u16()? as i16)
}
pub fn read_i32(&mut self) -> Result<i32> {
Ok(self.read_u32()? as i32)
}
pub fn read_i64(&mut self) -> Result<i64> {
Ok(self.read_u64()? as i64)
}
pub fn read_f32(&mut self) -> Result<f32> {
let bits = self.read_u32()?;
Ok(f32::from_bits(bits))
}
pub fn read_f64(&mut self) -> Result<f64> {
let bits = self.read_u64()?;
Ok(f64::from_bits(bits))
}
pub fn read_bool(&mut self) -> Result<bool> {
let value = self.read_u8()?;
Ok(value != 0)
}
pub fn read_bytes(&mut self, len: usize) -> Result<&'a [u8]> {
self.check_size(len)?;
let bytes = &self.data[self.position..self.position + len];
self.position += len;
Ok(bytes)
}
pub fn config(&self) -> Config {
self.config
}
pub fn bytes_read(&self) -> usize {
self.position
}
pub fn remaining(&self) -> &'a [u8] {
&self.data[self.position..]
}
fn check_size(&self, needed: usize) -> Result<()> {
let available = self.data.len().saturating_sub(self.position);
if available < needed {
return Err(JustcodeError::UnexpectedEndOfInput {
expected: needed,
got: available,
});
}
if let Some(limit) = self.config.limit {
let new_size = self.position + needed;
if new_size > limit {
return Err(JustcodeError::SizeLimitExceeded {
limit,
requested: new_size,
});
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config;
#[test]
fn test_read_primitives() {
let config = config::standard();
let data = vec![
42u8, 0xE8, 0x03, 0xA0, 0x86, 0x01, 0x00, 0x00, 0xCA, 0x9A, 0x3B, 0x00, 0x00, 0x00, 0x00, 1, 0, ];
let mut reader = Reader::new(&data, config);
assert_eq!(reader.read_u8().unwrap(), 42);
assert_eq!(reader.read_u16().unwrap(), 1000);
assert_eq!(reader.read_u32().unwrap(), 100000);
assert_eq!(reader.read_u64().unwrap(), 1000000000);
assert_eq!(reader.read_bool().unwrap(), true);
assert_eq!(reader.read_bool().unwrap(), false);
}
#[test]
fn test_read_floats() {
let config = config::standard();
let mut writer = crate::writer::Writer::new(config);
writer.write_f32(3.14).unwrap();
writer.write_f64(2.718).unwrap();
let data = writer.into_bytes();
let mut reader = Reader::new(&data, config);
assert!((reader.read_f32().unwrap() - 3.14).abs() < 0.01);
assert!((reader.read_f64().unwrap() - 2.718).abs() < 0.001);
}
#[test]
fn test_unexpected_end_of_input() {
let config = config::standard();
let data = vec![42u8];
let mut reader = Reader::new(&data, config);
assert!(reader.read_u32().is_err());
}
#[test]
fn test_size_limit() {
let config = config::standard().with_limit(10);
let data = vec![0u8; 20];
let mut reader = Reader::new(&data, config);
assert!(reader.read_bytes(15).is_err());
}
#[test]
fn test_read_signed_integers() {
let config = config::standard();
let mut writer = crate::writer::Writer::new(config);
writer.write_i8(-42).unwrap();
writer.write_i16(-1000).unwrap();
writer.write_i32(-100000).unwrap();
writer.write_i64(-1000000000).unwrap();
let data = writer.into_bytes();
let mut reader = Reader::new(&data, config);
assert_eq!(reader.read_i8().unwrap(), -42);
assert_eq!(reader.read_i16().unwrap(), -1000);
assert_eq!(reader.read_i32().unwrap(), -100000);
assert_eq!(reader.read_i64().unwrap(), -1000000000);
}
#[test]
fn test_read_bytes() {
let config = config::standard();
let data = vec![1, 2, 3, 4, 5, 6, 7, 8];
let mut reader = Reader::new(&data, config);
let bytes = reader.read_bytes(4).unwrap();
assert_eq!(bytes, &[1, 2, 3, 4]);
assert_eq!(reader.bytes_read(), 4);
}
#[test]
fn test_remaining() {
let config = config::standard();
let data = vec![1, 2, 3, 4, 5];
let mut reader = Reader::new(&data, config);
reader.read_u8().unwrap();
assert_eq!(reader.remaining(), &[2, 3, 4, 5]);
}
#[test]
fn test_config() {
let config = config::standard().with_limit(100);
let data = vec![1, 2, 3];
let reader = Reader::new(&data, config);
assert_eq!(reader.config().limit, Some(100));
}
#[test]
fn test_bytes_read() {
let config = config::standard();
let data = vec![1, 2, 3, 4, 5];
let mut reader = Reader::new(&data, config);
assert_eq!(reader.bytes_read(), 0);
reader.read_u8().unwrap();
assert_eq!(reader.bytes_read(), 1);
reader.read_u16().unwrap();
assert_eq!(reader.bytes_read(), 3);
}
#[test]
fn test_size_limit_edge_cases() {
let config = config::standard().with_limit(5);
let data = vec![0u8; 10];
let mut reader = Reader::new(&data, config);
reader.read_bytes(5).unwrap();
assert!(reader.read_bytes(1).is_err());
}
#[test]
fn test_read_bool_edge_cases() {
let config = config::standard();
let data = vec![0, 1, 2, 255];
let mut reader = Reader::new(&data, config);
assert_eq!(reader.read_bool().unwrap(), false);
assert_eq!(reader.read_bool().unwrap(), true);
assert_eq!(reader.read_bool().unwrap(), true); assert_eq!(reader.read_bool().unwrap(), true); }
#[test]
fn test_check_size_error_message() {
let config = config::standard();
let data = vec![1u8, 2];
let mut reader = Reader::new(&data, config);
let err = reader.read_u32().unwrap_err();
match err {
JustcodeError::UnexpectedEndOfInput { expected, got } => {
assert_eq!(expected, 4);
assert_eq!(got, 2);
}
_ => panic!("Expected UnexpectedEndOfInput error"),
}
}
#[test]
fn test_size_limit_exceeded_error() {
let config = config::standard().with_limit(5);
let data = vec![0u8; 10];
let mut reader = Reader::new(&data, config);
reader.read_bytes(5).unwrap();
let err = reader.read_bytes(1).unwrap_err();
match err {
JustcodeError::SizeLimitExceeded { limit, requested } => {
assert_eq!(limit, 5);
assert_eq!(requested, 6);
}
_ => panic!("Expected SizeLimitExceeded error"),
}
}
#[test]
fn test_size_limit_at_boundary() {
let config = config::standard().with_limit(5);
let data = vec![0u8; 10];
let mut reader = Reader::new(&data, config);
reader.read_bytes(5).unwrap();
assert!(reader.read_bytes(1).is_err());
}
#[test]
fn test_check_size_with_limit_none() {
let config = config::standard(); let data = vec![1u8, 2, 3, 4, 5];
let mut reader = Reader::new(&data, config);
reader.read_bytes(5).unwrap();
}
#[test]
fn test_check_size_with_limit_success() {
let config = config::standard().with_limit(10);
let data = vec![0u8; 20];
let mut reader = Reader::new(&data, config);
reader.read_bytes(5).unwrap();
assert_eq!(reader.bytes_read(), 5);
}
#[test]
fn test_check_size_exact_error_paths() {
let config = config::standard();
let data = vec![1u8];
let mut reader = Reader::new(&data, config);
let err = reader.read_u16().unwrap_err();
if let JustcodeError::UnexpectedEndOfInput { expected, got } = err {
assert_eq!(expected, 2);
assert_eq!(got, 1);
} else {
panic!("Expected UnexpectedEndOfInput");
}
let config = config::standard().with_limit(3);
let data = vec![0u8; 10];
let mut reader = Reader::new(&data, config);
reader.read_bytes(2).unwrap(); let err = reader.read_bytes(2).unwrap_err(); if let JustcodeError::SizeLimitExceeded { limit, requested } = err {
assert_eq!(limit, 3);
assert_eq!(requested, 4);
} else {
panic!("Expected SizeLimitExceeded");
}
}
}