use crate::Result;
pub trait CilIO: Sized {
type Bytes: Sized + AsRef<[u8]> + for<'a> TryFrom<&'a [u8]>;
fn from_le_bytes(bytes: Self::Bytes) -> Self;
fn from_be_bytes(bytes: Self::Bytes) -> Self;
fn to_le_bytes(self) -> Self::Bytes;
fn to_be_bytes(self) -> Self::Bytes;
}
impl CilIO for u64 {
type Bytes = [u8; 8];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
u64::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
u64::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
u64::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
u64::to_be_bytes(self)
}
}
impl CilIO for i64 {
type Bytes = [u8; 8];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
i64::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
i64::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
i64::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
i64::to_be_bytes(self)
}
}
impl CilIO for u32 {
type Bytes = [u8; 4];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
u32::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
u32::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
u32::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
u32::to_be_bytes(self)
}
}
impl CilIO for i32 {
type Bytes = [u8; 4];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
i32::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
i32::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
i32::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
i32::to_be_bytes(self)
}
}
impl CilIO for u16 {
type Bytes = [u8; 2];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
u16::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
u16::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
u16::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
u16::to_be_bytes(self)
}
}
impl CilIO for i16 {
type Bytes = [u8; 2];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
i16::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
i16::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
i16::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
i16::to_be_bytes(self)
}
}
impl CilIO for u8 {
type Bytes = [u8; 1];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
u8::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
u8::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
u8::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
u8::to_be_bytes(self)
}
}
impl CilIO for i8 {
type Bytes = [u8; 1];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
i8::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
i8::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
i8::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
i8::to_be_bytes(self)
}
}
impl CilIO for f32 {
type Bytes = [u8; 4];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
f32::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
f32::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
f32::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
f32::to_be_bytes(self)
}
}
impl CilIO for f64 {
type Bytes = [u8; 8];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
f64::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
f64::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
f64::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
f64::to_be_bytes(self)
}
}
impl CilIO for usize {
type Bytes = [u8; std::mem::size_of::<usize>()];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
usize::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
usize::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
usize::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
usize::to_be_bytes(self)
}
}
impl CilIO for isize {
type Bytes = [u8; std::mem::size_of::<isize>()];
fn from_le_bytes(bytes: Self::Bytes) -> Self {
isize::from_le_bytes(bytes)
}
fn from_be_bytes(bytes: Self::Bytes) -> Self {
isize::from_be_bytes(bytes)
}
fn to_le_bytes(self) -> Self::Bytes {
isize::to_le_bytes(self)
}
fn to_be_bytes(self) -> Self::Bytes {
isize::to_be_bytes(self)
}
}
pub fn read_le<T: CilIO>(data: &[u8]) -> Result<T> {
let mut offset = 0_usize;
read_le_at(data, &mut offset)
}
pub fn read_le_at<T: CilIO>(data: &[u8], offset: &mut usize) -> Result<T> {
let type_len = std::mem::size_of::<T>();
if (type_len + *offset) > data.len() {
return Err(out_of_bounds_error!());
}
let Ok(read) = data[*offset..*offset + type_len].try_into() else {
return Err(out_of_bounds_error!());
};
*offset += type_len;
Ok(T::from_le_bytes(read))
}
pub fn read_le_at_dyn(data: &[u8], offset: &mut usize, is_large: bool) -> Result<u32> {
let res = if is_large {
read_le_at::<u32>(data, offset)?
} else {
u32::from(read_le_at::<u16>(data, offset)?)
};
Ok(res)
}
pub fn read_be<T: CilIO>(data: &[u8]) -> Result<T> {
let mut offset = 0_usize;
read_be_at(data, &mut offset)
}
pub fn read_be_at<T: CilIO>(data: &[u8], offset: &mut usize) -> Result<T> {
let type_len = std::mem::size_of::<T>();
if (type_len + *offset) > data.len() {
return Err(out_of_bounds_error!());
}
let Ok(read) = data[*offset..*offset + type_len].try_into() else {
return Err(out_of_bounds_error!());
};
*offset += type_len;
Ok(T::from_be_bytes(read))
}
pub fn read_be_at_dyn(data: &[u8], offset: &mut usize, is_large: bool) -> Result<u32> {
let res = if is_large {
read_be_at::<u32>(data, offset)?
} else {
u32::from(read_be_at::<u16>(data, offset)?)
};
Ok(res)
}
pub fn write_le<T: CilIO>(data: &mut [u8], value: T) -> Result<()> {
let mut offset = 0_usize;
write_le_at(data, &mut offset, value)
}
pub fn write_le_at<T: CilIO>(data: &mut [u8], offset: &mut usize, value: T) -> Result<()> {
let type_len = std::mem::size_of::<T>();
if (type_len + *offset) > data.len() {
return Err(out_of_bounds_error!());
}
let bytes = value.to_le_bytes();
data[*offset..*offset + type_len].copy_from_slice(bytes.as_ref());
*offset += type_len;
Ok(())
}
pub fn write_le_at_dyn(
data: &mut [u8],
offset: &mut usize,
value: u32,
is_large: bool,
) -> Result<()> {
if is_large {
write_le_at::<u32>(data, offset, value)?;
} else {
#[allow(clippy::cast_possible_truncation)]
write_le_at::<u16>(data, offset, value as u16)?;
}
Ok(())
}
pub fn write_be<T: CilIO>(data: &mut [u8], value: T) -> Result<()> {
let mut offset = 0_usize;
write_be_at(data, &mut offset, value)
}
pub fn write_be_at<T: CilIO>(data: &mut [u8], offset: &mut usize, value: T) -> Result<()> {
let type_len = std::mem::size_of::<T>();
if (type_len + *offset) > data.len() {
return Err(out_of_bounds_error!());
}
let bytes = value.to_be_bytes();
data[*offset..*offset + type_len].copy_from_slice(bytes.as_ref());
*offset += type_len;
Ok(())
}
pub fn write_be_at_dyn(
data: &mut [u8],
offset: &mut usize,
value: u32,
is_large: bool,
) -> Result<()> {
if is_large {
write_be_at::<u32>(data, offset, value)?;
} else {
#[allow(clippy::cast_possible_truncation)]
write_be_at::<u16>(data, offset, value as u16)?;
}
Ok(())
}
#[allow(clippy::cast_possible_truncation)]
pub fn write_compressed_uint(value: u32, buffer: &mut Vec<u8>) {
if value < 0x80 {
buffer.push(value as u8);
} else if value < 0x4000 {
buffer.push(0x80 | ((value >> 8) as u8));
buffer.push(value as u8);
} else {
buffer.push(0xC0 | ((value >> 24) as u8));
buffer.push((value >> 16) as u8);
buffer.push((value >> 8) as u8);
buffer.push(value as u8);
}
}
#[allow(clippy::cast_sign_loss)]
pub fn write_compressed_int(value: i32, buffer: &mut Vec<u8>) {
let unsigned_value = if value >= 0 {
(value as u32) << 1
} else {
(((-value - 1) as u32) << 1) | 1
};
write_compressed_uint(unsigned_value, buffer);
}
#[allow(clippy::cast_possible_truncation)]
pub fn write_7bit_encoded_int(mut value: u32, buffer: &mut Vec<u8>) {
while value >= 0x80 {
buffer.push((value as u8) | 0x80);
value >>= 7;
}
buffer.push(value as u8);
}
pub fn write_string_utf8(value: &str, buffer: &mut Vec<u8>) {
buffer.extend_from_slice(value.as_bytes());
buffer.push(0);
}
#[allow(clippy::cast_possible_truncation)]
pub fn write_prefixed_string_utf8(value: &str, buffer: &mut Vec<u8>) {
let bytes = value.as_bytes();
write_7bit_encoded_int(bytes.len() as u32, buffer);
buffer.extend_from_slice(bytes);
}
#[allow(clippy::cast_possible_truncation)]
pub fn write_prefixed_string_utf16(value: &str, buffer: &mut Vec<u8>) {
let utf16_chars: Vec<u16> = value.encode_utf16().collect();
let byte_length = utf16_chars.len() * 2;
write_7bit_encoded_int(byte_length as u32, buffer);
for char in utf16_chars {
buffer.push(char as u8); buffer.push((char >> 8) as u8); }
}
pub fn write_string_at(data: &mut [u8], offset: &mut usize, value: &str) -> Result<()> {
let string_bytes = value.as_bytes();
let total_length = string_bytes.len() + 1;
if *offset + total_length > data.len() {
return Err(out_of_bounds_error!());
}
data[*offset..*offset + string_bytes.len()].copy_from_slice(string_bytes);
*offset += string_bytes.len();
data[*offset] = 0;
*offset += 1;
Ok(())
}
pub fn read_compressed_int(data: &[u8], offset: &mut usize) -> Result<(usize, usize)> {
if *offset >= data.len() {
return Err(out_of_bounds_error!());
}
let first_byte = data[*offset];
if first_byte & 0x80 == 0 {
*offset += 1;
Ok((first_byte as usize, 1))
} else if first_byte & 0xC0 == 0x80 {
if *offset + 1 >= data.len() {
return Err(out_of_bounds_error!());
}
let second_byte = data[*offset + 1];
let value = (((first_byte & 0x3F) as usize) << 8) | (second_byte as usize);
*offset += 2;
Ok((value, 2))
} else {
if *offset + 3 >= data.len() {
return Err(out_of_bounds_error!());
}
let mut value = ((first_byte & 0x1F) as usize) << 24;
value |= (data[*offset + 1] as usize) << 16;
value |= (data[*offset + 2] as usize) << 8;
value |= data[*offset + 3] as usize;
*offset += 4;
Ok((value, 4))
}
}
pub fn read_compressed_int_at(data: &[u8], offset: usize) -> Result<(usize, usize)> {
let mut mutable_offset = offset;
read_compressed_int(data, &mut mutable_offset)
}
pub fn read_compressed_uint(data: &[u8], offset: &mut usize) -> Result<u32> {
let (value, _consumed) = read_compressed_int(data, offset)?;
u32::try_from(value).map_err(|_| out_of_bounds_error!())
}
pub fn read_compressed_uint_at(data: &[u8], offset: usize) -> Result<u32> {
let mut mutable_offset = offset;
read_compressed_uint(data, &mut mutable_offset)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Error;
const TEST_BUFFER: [u8; 8] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
#[test]
fn read_le_u8() {
let result = read_le::<u8>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x01);
}
#[test]
fn read_le_i8() {
let result = read_le::<i8>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x01);
}
#[test]
fn read_le_u16() {
let result = read_le::<u16>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0201);
}
#[test]
fn read_le_i16() {
let result = read_le::<i16>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0201);
}
#[test]
fn read_le_u32() {
let result = read_le::<u32>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0403_0201);
}
#[test]
fn read_le_i32() {
let result = read_le::<i32>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0403_0201);
}
#[test]
fn read_le_u64() {
let result = read_le::<u64>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0807_0605_0403_0201);
}
#[test]
fn read_le_i64() {
let result = read_le::<i64>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0807_0605_0403_0201);
}
#[test]
fn read_be_u8() {
let result = read_be::<u8>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x1);
}
#[test]
fn read_be_i8() {
let result = read_be::<i8>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x1);
}
#[test]
fn read_be_u16() {
let result = read_be::<u16>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x102);
}
#[test]
fn read_be_i16() {
let result = read_be::<i16>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x102);
}
#[test]
fn read_be_u32() {
let result = read_be::<u32>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0102_0304);
}
#[test]
fn read_be_i32() {
let result = read_be::<i32>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0102_0304);
}
#[test]
fn read_be_u64() {
let result = read_be::<u64>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0102_0304_0506_0708);
}
#[test]
fn read_be_i64() {
let result = read_be::<i64>(&TEST_BUFFER).unwrap();
assert_eq!(result, 0x0102_0304_0506_0708);
}
#[test]
fn read_be_f32() {
let result = read_be::<f32>(&TEST_BUFFER).unwrap();
assert_eq!(result, 2.3879393e-38);
}
#[test]
fn read_be_f64() {
let result = read_be::<f64>(&TEST_BUFFER).unwrap();
assert_eq!(result, 8.20788039913184e-304);
}
#[test]
fn read_le_f32() {
let result = read_le::<f32>(&TEST_BUFFER).unwrap();
assert_eq!(result, 1.5399896e-36);
}
#[test]
fn read_le_f64() {
let result = read_le::<f64>(&TEST_BUFFER).unwrap();
assert_eq!(result, 5.447603722011605e-270);
}
#[test]
fn read_be_from() {
let mut offset = 2_usize;
let result = read_be_at::<u16>(&TEST_BUFFER, &mut offset).unwrap();
assert_eq!(result, 0x304);
}
#[test]
fn read_le_from() {
let mut offset = 2_usize;
let result = read_le_at::<u16>(&TEST_BUFFER, &mut offset).unwrap();
assert_eq!(result, 0x403);
}
#[test]
fn read_le_dyn() {
let mut offset = 0;
let res_1 = read_le_at_dyn(&TEST_BUFFER, &mut offset, true).unwrap();
assert_eq!(res_1, 0x4030201);
offset = 0;
let res_2 = read_le_at_dyn(&TEST_BUFFER, &mut offset, false).unwrap();
assert_eq!(res_2, 0x201);
}
#[test]
fn read_be_dyn() {
let mut offset = 0;
let res_1 = read_be_at_dyn(&TEST_BUFFER, &mut offset, true).unwrap();
assert_eq!(res_1, 0x1020304);
offset = 0;
let res_2 = read_be_at_dyn(&TEST_BUFFER, &mut offset, false).unwrap();
assert_eq!(res_2, 0x102);
}
#[test]
fn errors() {
let buffer = [0xFF, 0xFF, 0xFF, 0xFF];
let result = read_le::<u64>(&buffer);
assert!(matches!(result, Err(Error::OutOfBounds { .. })));
let result = read_le::<f64>(&buffer);
assert!(matches!(result, Err(Error::OutOfBounds { .. })));
}
#[test]
fn read_le_usize() {
let size_bytes = std::mem::size_of::<usize>();
let mut buffer = vec![0u8; size_bytes];
buffer[0] = 0x78;
buffer[1] = 0x56;
if size_bytes >= 4 {
buffer[2] = 0x34;
buffer[3] = 0x12;
}
let result = read_le::<usize>(&buffer).unwrap();
if size_bytes == 8 {
assert_eq!(result, 0x12345678);
} else {
assert_eq!(result, 0x5678);
}
}
#[test]
fn read_be_usize() {
let size_bytes = std::mem::size_of::<usize>();
let mut buffer = vec![0u8; size_bytes];
if size_bytes >= 4 {
buffer[size_bytes - 4] = 0x12;
buffer[size_bytes - 3] = 0x34;
}
buffer[size_bytes - 2] = 0x56;
buffer[size_bytes - 1] = 0x78;
let result = read_be::<usize>(&buffer).unwrap();
if size_bytes == 8 {
assert_eq!(result, 0x12345678);
} else {
assert_eq!(result, 0x5678);
}
}
#[test]
fn read_le_isize() {
let size_bytes = std::mem::size_of::<isize>();
let mut buffer = vec![0u8; size_bytes];
for item in buffer.iter_mut().take(size_bytes) {
*item = 0xFF;
}
let result = read_le::<isize>(&buffer).unwrap();
assert_eq!(result, -1);
}
#[test]
fn read_be_isize() {
let size_bytes = std::mem::size_of::<isize>();
let mut buffer = vec![0u8; size_bytes];
for item in buffer.iter_mut().take(size_bytes) {
*item = 0xFF;
}
let result = read_be::<isize>(&buffer).unwrap();
assert_eq!(result, -1);
}
#[test]
fn write_le_u8() {
let mut buffer = [0u8; 1];
write_le(&mut buffer, 0x42u8).unwrap();
assert_eq!(buffer, [0x42]);
}
#[test]
fn write_le_i8() {
let mut buffer = [0u8; 1];
write_le(&mut buffer, -1i8).unwrap();
assert_eq!(buffer, [0xFF]);
}
#[test]
fn write_le_u16() {
let mut buffer = [0u8; 2];
write_le(&mut buffer, 0x1234u16).unwrap();
assert_eq!(buffer, [0x34, 0x12]); }
#[test]
fn write_le_i16() {
let mut buffer = [0u8; 2];
write_le(&mut buffer, -1i16).unwrap();
assert_eq!(buffer, [0xFF, 0xFF]);
}
#[test]
fn write_le_u32() {
let mut buffer = [0u8; 4];
write_le(&mut buffer, 0x12345678u32).unwrap();
assert_eq!(buffer, [0x78, 0x56, 0x34, 0x12]); }
#[test]
fn write_le_i32() {
let mut buffer = [0u8; 4];
write_le(&mut buffer, -1i32).unwrap();
assert_eq!(buffer, [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn write_le_u64() {
let mut buffer = [0u8; 8];
write_le(&mut buffer, 0x123456789ABCDEFu64).unwrap();
assert_eq!(buffer, [0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01]); }
#[test]
fn write_le_i64() {
let mut buffer = [0u8; 8];
write_le(&mut buffer, -1i64).unwrap();
assert_eq!(buffer, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn write_be_u8() {
let mut buffer = [0u8; 1];
write_be(&mut buffer, 0x42u8).unwrap();
assert_eq!(buffer, [0x42]);
}
#[test]
fn write_be_i8() {
let mut buffer = [0u8; 1];
write_be(&mut buffer, -1i8).unwrap();
assert_eq!(buffer, [0xFF]);
}
#[test]
fn write_be_u16() {
let mut buffer = [0u8; 2];
write_be(&mut buffer, 0x1234u16).unwrap();
assert_eq!(buffer, [0x12, 0x34]); }
#[test]
fn write_be_i16() {
let mut buffer = [0u8; 2];
write_be(&mut buffer, -1i16).unwrap();
assert_eq!(buffer, [0xFF, 0xFF]);
}
#[test]
fn write_be_u32() {
let mut buffer = [0u8; 4];
write_be(&mut buffer, 0x12345678u32).unwrap();
assert_eq!(buffer, [0x12, 0x34, 0x56, 0x78]); }
#[test]
fn write_be_i32() {
let mut buffer = [0u8; 4];
write_be(&mut buffer, -1i32).unwrap();
assert_eq!(buffer, [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn write_be_u64() {
let mut buffer = [0u8; 8];
write_be(&mut buffer, 0x123456789ABCDEFu64).unwrap();
assert_eq!(buffer, [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]); }
#[test]
fn write_be_i64() {
let mut buffer = [0u8; 8];
write_be(&mut buffer, -1i64).unwrap();
assert_eq!(buffer, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn write_le_f32() {
let mut buffer = [0u8; 4];
write_le(&mut buffer, 1.0f32).unwrap();
assert_eq!(buffer, [0x00, 0x00, 0x80, 0x3F]);
}
#[test]
fn write_le_f64() {
let mut buffer = [0u8; 8];
write_le(&mut buffer, 1.0f64).unwrap();
assert_eq!(buffer, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F]);
}
#[test]
fn write_be_f32() {
let mut buffer = [0u8; 4];
write_be(&mut buffer, 1.0f32).unwrap();
assert_eq!(buffer, [0x3F, 0x80, 0x00, 0x00]);
}
#[test]
fn write_be_f64() {
let mut buffer = [0u8; 8];
write_be(&mut buffer, 1.0f64).unwrap();
assert_eq!(buffer, [0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
}
#[test]
fn write_le_at_sequential() {
let mut buffer = [0u8; 8];
let mut offset = 0;
write_le_at(&mut buffer, &mut offset, 0x1234u16).unwrap();
assert_eq!(offset, 2);
write_le_at(&mut buffer, &mut offset, 0x5678u16).unwrap();
assert_eq!(offset, 4);
write_le_at(&mut buffer, &mut offset, 0xABCDu32).unwrap();
assert_eq!(offset, 8);
assert_eq!(buffer, [0x34, 0x12, 0x78, 0x56, 0xCD, 0xAB, 0x00, 0x00]);
}
#[test]
fn write_be_at_sequential() {
let mut buffer = [0u8; 8];
let mut offset = 0;
write_be_at(&mut buffer, &mut offset, 0x1234u16).unwrap();
assert_eq!(offset, 2);
write_be_at(&mut buffer, &mut offset, 0x5678u16).unwrap();
assert_eq!(offset, 4);
write_be_at(&mut buffer, &mut offset, 0xABCDu32).unwrap();
assert_eq!(offset, 8);
assert_eq!(buffer, [0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0xAB, 0xCD]);
}
#[test]
fn write_le_dyn() {
let mut buffer = [0u8; 6];
let mut offset = 0;
write_le_at_dyn(&mut buffer, &mut offset, 0x1234, false).unwrap();
assert_eq!(offset, 2);
write_le_at_dyn(&mut buffer, &mut offset, 0x56789ABC, true).unwrap();
assert_eq!(offset, 6);
assert_eq!(buffer, [0x34, 0x12, 0xBC, 0x9A, 0x78, 0x56]);
}
#[test]
fn write_be_dyn() {
let mut buffer = [0u8; 6];
let mut offset = 0;
write_be_at_dyn(&mut buffer, &mut offset, 0x1234, false).unwrap();
assert_eq!(offset, 2);
write_be_at_dyn(&mut buffer, &mut offset, 0x56789ABC, true).unwrap();
assert_eq!(offset, 6);
assert_eq!(buffer, [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC]);
}
#[test]
fn write_errors() {
let mut buffer = [0u8; 2];
let result = write_le(&mut buffer, 0x12345678u32);
assert!(matches!(result, Err(Error::OutOfBounds { .. })));
let result = write_be(&mut buffer, 0x12345678u32);
assert!(matches!(result, Err(Error::OutOfBounds { .. })));
}
#[test]
fn round_trip_consistency() {
const VALUE_U32: u32 = 0x12345678;
const VALUE_I32: i32 = -12345;
const VALUE_F32: f32 = 3.0419;
let mut buffer = [0u8; 4];
write_le(&mut buffer, VALUE_U32).unwrap();
let read_value: u32 = read_le(&buffer).unwrap();
assert_eq!(read_value, VALUE_U32);
write_le(&mut buffer, VALUE_I32).unwrap();
let read_value: i32 = read_le(&buffer).unwrap();
assert_eq!(read_value, VALUE_I32);
write_le(&mut buffer, VALUE_F32).unwrap();
let read_value: f32 = read_le(&buffer).unwrap();
assert_eq!(read_value, VALUE_F32);
write_be(&mut buffer, VALUE_U32).unwrap();
let read_value: u32 = read_be(&buffer).unwrap();
assert_eq!(read_value, VALUE_U32);
write_be(&mut buffer, VALUE_I32).unwrap();
let read_value: i32 = read_be(&buffer).unwrap();
assert_eq!(read_value, VALUE_I32);
write_be(&mut buffer, VALUE_F32).unwrap();
let read_value: f32 = read_be(&buffer).unwrap();
assert_eq!(read_value, VALUE_F32);
}
#[test]
fn test_write_compressed_uint_single_byte() {
let test_cases = vec![
(0, vec![0]),
(1, vec![1]),
(127, vec![127]), ];
for (value, expected) in test_cases {
let mut buffer = Vec::new();
write_compressed_uint(value, &mut buffer);
assert_eq!(buffer, expected, "Failed for value {value}");
}
}
#[test]
fn test_write_compressed_uint_two_bytes() {
let test_cases = vec![
(128, vec![0x80, 0x80]), (255, vec![0x80, 0xFF]), (16383, vec![0xBF, 0xFF]), ];
for (value, expected) in test_cases {
let mut buffer = Vec::new();
write_compressed_uint(value, &mut buffer);
assert_eq!(buffer, expected, "Failed for value {value}");
}
}
#[test]
fn test_write_compressed_uint_four_bytes() {
let test_cases = vec![
(16384, vec![0xC0, 0x00, 0x40, 0x00]), (0x1FFFFFFF, vec![0xDF, 0xFF, 0xFF, 0xFF]), ];
for (value, expected) in test_cases {
let mut buffer = Vec::new();
write_compressed_uint(value, &mut buffer);
assert_eq!(buffer, expected, "Failed for value {value}");
}
}
#[test]
fn test_write_compressed_int_positive() {
let test_cases = vec![
(0, vec![0]), (1, vec![2]), (10, vec![20]), (63, vec![126]), ];
for (value, expected) in test_cases {
let mut buffer = Vec::new();
write_compressed_int(value, &mut buffer);
assert_eq!(buffer, expected, "Failed for value {value}");
}
}
#[test]
fn test_write_compressed_int_negative() {
let test_cases = vec![
(-1, vec![1]), (-5, vec![9]), (-10, vec![19]), ];
for (value, expected) in test_cases {
let mut buffer = Vec::new();
write_compressed_int(value, &mut buffer);
assert_eq!(buffer, expected, "Failed for value {value}");
}
}
#[test]
fn test_write_7bit_encoded_int() {
let test_cases = vec![
(0, vec![0]),
(127, vec![0x7F]), (128, vec![0x80, 0x01]), (16383, vec![0xFF, 0x7F]), (16384, vec![0x80, 0x80, 0x01]), (2097151, vec![0xFF, 0xFF, 0x7F]), (2097152, vec![0x80, 0x80, 0x80, 0x01]), ];
for (value, expected) in test_cases {
let mut buffer = Vec::new();
write_7bit_encoded_int(value, &mut buffer);
assert_eq!(buffer, expected, "Failed for value {value}");
}
}
#[test]
fn test_write_string_utf8() {
let test_cases = vec![
("", vec![0]), ("Hello", b"Hello\0".to_vec()), ("中文", vec![0xE4, 0xB8, 0xAD, 0xE6, 0x96, 0x87, 0x00]), ];
for (input, expected) in test_cases {
let mut buffer = Vec::new();
write_string_utf8(input, &mut buffer);
assert_eq!(buffer, expected, "Failed for input '{input}'");
}
}
#[test]
fn test_write_prefixed_string_utf8() {
let test_cases = vec![
("", vec![0]), ("Hello", vec![5, b'H', b'e', b'l', b'l', b'o']), ("Hi", vec![2, b'H', b'i']), ];
for (input, expected) in test_cases {
let mut buffer = Vec::new();
write_prefixed_string_utf8(input, &mut buffer);
assert_eq!(buffer, expected, "Failed for input '{input}'");
}
}
#[test]
fn test_write_prefixed_string_utf16() {
let test_cases = vec![
("", vec![0]), ("A", vec![2, 0x41, 0x00]), (
"Hello",
vec![
10, 0x48, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00,
],
), ];
for (input, expected) in test_cases {
let mut buffer = Vec::new();
write_prefixed_string_utf16(input, &mut buffer);
assert_eq!(buffer, expected, "Failed for input '{input}'");
}
}
#[test]
fn test_string_encoding_edge_cases() {
let long_string = "a".repeat(200);
let mut buffer = Vec::new();
write_prefixed_string_utf8(&long_string, &mut buffer);
assert_eq!(buffer[0], 0xC8);
assert_eq!(buffer[1], 0x01);
assert_eq!(buffer.len(), 202);
let mut buffer = Vec::new();
write_prefixed_string_utf16("中", &mut buffer);
assert_eq!(buffer, vec![2, 0x2D, 0x4E]);
}
#[test]
fn test_write_string_at() {
let mut buffer = [0u8; 20];
let mut offset = 0;
write_string_at(&mut buffer, &mut offset, "Hello").unwrap();
assert_eq!(offset, 6); assert_eq!(&buffer[0..6], b"Hello\0");
write_string_at(&mut buffer, &mut offset, "World").unwrap();
assert_eq!(offset, 12); assert_eq!(&buffer[6..12], b"World\0");
assert_eq!(&buffer[0..12], b"Hello\0World\0");
}
#[test]
fn test_write_string_at_empty_string() {
let mut buffer = [0u8; 5];
let mut offset = 0;
write_string_at(&mut buffer, &mut offset, "").unwrap();
assert_eq!(offset, 1); assert_eq!(&buffer[0..1], b"\0");
}
#[test]
fn test_write_string_at_exact_fit() {
let mut buffer = [0u8; 6];
let mut offset = 0;
write_string_at(&mut buffer, &mut offset, "Hello").unwrap();
assert_eq!(offset, 6);
assert_eq!(&buffer, b"Hello\0");
}
#[test]
fn test_write_string_at_bounds_error() {
let mut buffer = [0u8; 5];
let mut offset = 0;
let result = write_string_at(&mut buffer, &mut offset, "Hello");
assert!(result.is_err());
assert_eq!(offset, 0); }
#[test]
fn test_write_string_at_with_offset() {
let mut buffer = [0u8; 10];
let mut offset = 3;
write_string_at(&mut buffer, &mut offset, "Hi").unwrap();
assert_eq!(offset, 6); assert_eq!(&buffer[3..6], b"Hi\0");
assert_eq!(&buffer[0..3], &[0, 0, 0]); }
#[test]
fn test_write_string_at_utf8() {
let mut buffer = [0u8; 20];
let mut offset = 0;
write_string_at(&mut buffer, &mut offset, "café").unwrap();
assert_eq!(offset, 6); assert_eq!(&buffer[0..6], "café\0".as_bytes());
}
}