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
165 lines
5.2 KiB
Rust
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));
|
|
}
|
|
}
|