use byteorder::{BigEndian, ByteOrder};
use super::utils;
#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
pub enum ByteArrayError {
OutOfRangeError,
InvalidString(utils::StringError),
}
impl From<utils::StringError> for ByteArrayError {
fn from(e: utils::StringError) -> Self {
Self::InvalidString(e)
}
}
pub struct ByteArray<'a> {
offset: usize,
data: &'a [u8],
}
impl<'a> ByteArray<'a> {
#[must_use]
pub const fn new(data: &'a [u8]) -> Self {
ByteArray { offset: 0, data }
}
#[must_use]
pub const fn len(&self) -> usize {
self.data.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[must_use]
pub const fn remaining_bytes(&self) -> usize {
assert!(self.offset <= self.data.len());
self.data.len() - self.offset
}
pub fn read_byte(&mut self) -> Result<u8, ByteArrayError> {
let offset = self.offset + 1;
if self.offset > self.data.len() {
Err(ByteArrayError::OutOfRangeError)
} else {
self.offset = offset;
Ok(self.data[self.offset - 1])
}
}
pub fn read_u16(&mut self) -> Result<u16, ByteArrayError> {
Ok(BigEndian::read_u16(self.read_bytes(2)?))
}
pub fn read_u32(&mut self) -> Result<u32, ByteArrayError> {
Ok(BigEndian::read_u32(self.read_bytes(4)?))
}
pub fn read_string(&mut self, len: usize) -> Result<String, ByteArrayError> {
let bytes = self.read_bytes(len)?;
utils::to_utf8_string(bytes).map_err(ByteArrayError::from)
}
pub fn read_bytes(&mut self, len: usize) -> Result<&[u8], ByteArrayError> {
let offset = self.offset + len;
if offset > self.data.len() {
log::error!(
"read_bytes() reading {} bytes, current offset: {}, data len: {}",
len,
self.offset,
self.data.len()
);
Err(ByteArrayError::OutOfRangeError)
} else {
self.offset = offset;
Ok(&self.data[self.offset - len..self.offset])
}
}
pub fn reset_offset(&mut self) {
self.offset = 0;
}
#[must_use]
pub const fn offset(&self) -> usize {
self.offset
}
}