Files
MetaCrate/crates/libremetaverse/src/rendering_binary_reader.rs
Chili Palmer c9a1170a27
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m34s
Native code generation / deterministic (push) Has been cancelled
Concurrency and resource soak audit / soak (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
performance evidence / audit (push) Has been cancelled
First release candidate / non-fuzz-release-gate (push) Has been cancelled
Release platform and feature matrix / audit (push) Has been cancelled
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Dependency and supply-chain audit / audit (push) Has been cancelled
Native release artifact audit / audit (push) Has been cancelled
Imaging and meshing gate / native (push) Failing after 53s
JPEG 2000 feature / linux (push) Successful in 2m49s
Native Rust workspace compile / compile (push) Failing after 59s
Skia feature / linux (push) Has been cancelled
Complete first release candidate audit (#107)
2026-08-12 14:44:28 +00:00

165 lines
5.2 KiB
Rust

use std::io::Read;
use std::sync::Mutex;
use libremetaverse_types::compat::ReadWrite;
use crate::Error;
use crate::rendering::EndianAwareBinaryReaderSourceFormat as SourceFormat;
/// A synchronized binary reader whose numeric methods honor the source byte order.
pub struct EndianAwareBinaryReader {
stream: Mutex<Box<dyn ReadWrite + Send>>,
format: SourceFormat,
}
impl EndianAwareBinaryReader {
pub fn new_with_stream(stream: Box<dyn ReadWrite + Send>) -> Result<Self, Error> {
Self::new_with_stream_source_format(stream, SourceFormat::LittleEndian)
}
pub fn new_with_stream_source_format(
stream: Box<dyn ReadWrite + Send>,
format: SourceFormat,
) -> Result<Self, Error> {
Ok(Self {
stream: Mutex::new(stream),
format,
})
}
fn read_exact<const N: usize>(&self) -> Result<[u8; N], Error> {
let mut bytes = [0_u8; N];
self.stream
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.read_exact(&mut bytes)
.map_err(|_| Error::IndexOutOfRange)?;
Ok(bytes)
}
pub fn read_double(&self) -> Result<f64, Error> {
let bytes = self.read_exact()?;
Ok(match self.format {
SourceFormat::BigEndian => f64::from_be_bytes(bytes),
SourceFormat::LittleEndian => f64::from_le_bytes(bytes),
})
}
pub fn read_int16(&self) -> Result<i16, Error> {
let bytes = self.read_exact()?;
Ok(match self.format {
SourceFormat::BigEndian => i16::from_be_bytes(bytes),
SourceFormat::LittleEndian => i16::from_le_bytes(bytes),
})
}
pub fn read_int32(&self) -> Result<i32, Error> {
let bytes = self.read_exact()?;
Ok(match self.format {
SourceFormat::BigEndian => i32::from_be_bytes(bytes),
SourceFormat::LittleEndian => i32::from_le_bytes(bytes),
})
}
pub fn read_int64(&self) -> Result<i64, Error> {
let bytes = self.read_exact()?;
Ok(match self.format {
SourceFormat::BigEndian => i64::from_be_bytes(bytes),
SourceFormat::LittleEndian => i64::from_le_bytes(bytes),
})
}
pub fn read_single(&self) -> Result<f32, Error> {
let bytes = self.read_exact()?;
Ok(match self.format {
SourceFormat::BigEndian => f32::from_be_bytes(bytes),
SourceFormat::LittleEndian => f32::from_le_bytes(bytes),
})
}
pub fn read_u_int32(&self) -> Result<u32, Error> {
let bytes = self.read_exact()?;
Ok(match self.format {
SourceFormat::BigEndian => u32::from_be_bytes(bytes),
SourceFormat::LittleEndian => u32::from_le_bytes(bytes),
})
}
pub fn read_string_with_method(&self) -> Result<String, Error> {
let mut bytes = Vec::new();
let mut stream = self
.stream
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
loop {
let mut byte = [0_u8; 1];
stream
.read_exact(&mut byte)
.map_err(|_| Error::IndexOutOfRange)?;
if byte[0] == 0 {
break;
}
bytes.push(byte[0]);
}
Ok(String::from_utf8_lossy(&bytes).into_owned())
}
pub fn read_string_with_int32(&self, size: i32) -> Result<String, Error> {
let size = usize::try_from(size).map_err(|_| Error::Argument)?;
let mut bytes = vec![0_u8; size];
let mut stream = self
.stream
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut read = 0;
while read < size {
let count = stream
.read(&mut bytes[read..])
.map_err(|_| Error::InvalidOperation)?;
if count == 0 {
break;
}
read += count;
}
bytes.truncate(read);
Ok(String::from_utf8_lossy(&bytes).trim().to_owned())
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
#[test]
fn reads_both_byte_orders_and_strings() {
let mut big = Vec::new();
big.extend_from_slice(&(-123_i16).to_be_bytes());
big.extend_from_slice(&42.5_f32.to_be_bytes());
big.extend_from_slice(b"hello\0 padded ");
let reader = EndianAwareBinaryReader::new_with_stream_source_format(
Box::new(Cursor::new(big)),
SourceFormat::BigEndian,
)
.unwrap();
assert_eq!(reader.read_int16().unwrap(), -123);
assert_eq!(reader.read_single().unwrap(), 42.5);
assert_eq!(reader.read_string_with_method().unwrap(), "hello");
assert_eq!(reader.read_string_with_int32(8).unwrap(), "padded");
let reader = EndianAwareBinaryReader::new_with_stream(Box::new(Cursor::new(
0x1234_5678_u32.to_le_bytes().to_vec(),
)))
.unwrap();
assert_eq!(reader.read_u_int32().unwrap(), 0x1234_5678);
}
#[test]
fn reports_short_numeric_reads() {
let reader =
EndianAwareBinaryReader::new_with_stream(Box::new(Cursor::new(vec![1, 2]))).unwrap();
assert_eq!(reader.read_int32(), Err(Error::IndexOutOfRange));
}
}