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>, format: SourceFormat, } impl EndianAwareBinaryReader { pub fn new_with_stream(stream: Box) -> Result { Self::new_with_stream_source_format(stream, SourceFormat::LittleEndian) } pub fn new_with_stream_source_format( stream: Box, format: SourceFormat, ) -> Result { Ok(Self { stream: Mutex::new(stream), format, }) } fn read_exact(&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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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)); } }