use crate::error::TraciError;
#[derive(Debug, Default)]
pub struct Storage {
buf: Vec<u8>,
pos: usize,
}
impl Storage {
#[inline]
pub fn new() -> Self {
Self::default()
}
pub fn from_bytes(data: Vec<u8>) -> Self {
Self { buf: data, pos: 0 }
}
#[inline]
pub fn len(&self) -> usize {
self.buf.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
#[inline]
pub fn position(&self) -> usize {
self.pos
}
#[inline]
pub fn valid_pos(&self) -> bool {
self.pos < self.buf.len()
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.buf
}
#[inline]
pub fn reset(&mut self) {
self.buf.clear();
self.pos = 0;
}
#[inline]
pub fn reset_pos(&mut self) {
self.pos = 0;
}
pub fn append_storage(&mut self, other: &Storage) {
self.buf.extend_from_slice(&other.buf);
}
fn check_read(&self, n: usize) -> Result<(), TraciError> {
if self.pos + n > self.buf.len() {
Err(TraciError::Protocol(format!(
"Storage: attempt to read {} bytes at position {} but buffer length is {}",
n,
self.pos,
self.buf.len()
)))
} else {
Ok(())
}
}
#[inline]
fn read_raw_bytes<const N: usize>(&mut self) -> Result<[u8; N], TraciError> {
self.check_read(N)?;
let mut arr = [0u8; N];
arr.copy_from_slice(&self.buf[self.pos..self.pos + N]);
self.pos += N;
Ok(arr)
}
pub fn read_u8(&mut self) -> Result<u8, TraciError> {
self.check_read(1)?;
let v = self.buf[self.pos];
self.pos += 1;
Ok(v)
}
#[inline]
pub fn write_u8(&mut self, value: u8) {
self.buf.push(value);
}
pub fn read_byte(&mut self) -> Result<i32, TraciError> {
let raw = self.read_u8()? as i32;
Ok(if raw < 128 { raw } else { raw - 256 })
}
pub fn write_byte(&mut self, value: i32) -> Result<(), TraciError> {
if !(-128..=127).contains(&value) {
return Err(TraciError::Protocol(format!(
"Storage::write_byte: value {value} out of range [-128, 127]"
)));
}
self.write_u8(((value + 256) % 256) as u8);
Ok(())
}
pub fn read_i16(&mut self) -> Result<i16, TraciError> {
Ok(i16::from_be_bytes(self.read_raw_bytes::<2>()?))
}
pub fn write_i16(&mut self, value: i16) {
self.buf.extend_from_slice(&value.to_be_bytes());
}
pub fn read_i32(&mut self) -> Result<i32, TraciError> {
Ok(i32::from_be_bytes(self.read_raw_bytes::<4>()?))
}
pub fn write_i32(&mut self, value: i32) {
self.buf.extend_from_slice(&value.to_be_bytes());
}
pub fn read_f32(&mut self) -> Result<f32, TraciError> {
Ok(f32::from_be_bytes(self.read_raw_bytes::<4>()?))
}
pub fn write_f32(&mut self, value: f32) {
self.buf.extend_from_slice(&value.to_be_bytes());
}
pub fn read_f64(&mut self) -> Result<f64, TraciError> {
Ok(f64::from_be_bytes(self.read_raw_bytes::<8>()?))
}
pub fn write_f64(&mut self, value: f64) {
self.buf.extend_from_slice(&value.to_be_bytes());
}
pub fn read_string(&mut self) -> Result<String, TraciError> {
let len = self.read_i32()? as usize;
self.check_read(len)?;
let bytes = self.buf[self.pos..self.pos + len].to_vec();
self.pos += len;
String::from_utf8(bytes).map_err(|e| TraciError::Protocol(format!("Invalid UTF-8 in string: {e}")))
}
pub fn write_string(&mut self, s: &str) {
self.write_i32(s.len() as i32);
self.buf.extend_from_slice(s.as_bytes());
}
pub fn read_string_list(&mut self) -> Result<Vec<String>, TraciError> {
let count = self.read_i32()?;
let mut v = Vec::with_capacity(count as usize);
for _ in 0..count {
v.push(self.read_string()?);
}
Ok(v)
}
pub fn write_string_list(&mut self, list: &[String]) {
self.write_i32(list.len() as i32);
for s in list {
self.write_string(s);
}
}
pub fn read_f64_list(&mut self) -> Result<Vec<f64>, TraciError> {
let count = self.read_i32()?;
let mut v = Vec::with_capacity(count as usize);
for _ in 0..count {
v.push(self.read_f64()?);
}
Ok(v)
}
pub fn write_f64_list(&mut self, list: &[f64]) {
self.write_i32(list.len() as i32);
for &d in list {
self.write_f64(d);
}
}
pub fn write_packet(&mut self, bytes: &[u8]) {
self.buf.extend_from_slice(bytes);
}
}
impl std::fmt::Display for Storage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for b in &self.buf {
write!(f, "{b:02X} ")?;
}
Ok(())
}
}