use crate::{Error, Result};
pub const NULL_LENGTH: u32 = 0xFFFF_FFFF;
#[derive(Debug, Clone)]
pub struct QReader<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> QReader<'a> {
#[must_use]
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
#[must_use]
pub fn remaining(&self) -> usize {
self.data.len() - self.pos
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.remaining() == 0
}
fn take(&mut self, len: usize) -> Result<&'a [u8]> {
let end = self
.pos
.checked_add(len)
.filter(|&end| end <= self.data.len())
.ok_or(Error::QDataStreamFailed { context: None })?;
let bytes = &self.data[self.pos..end];
self.pos = end;
Ok(bytes)
}
fn take_array<const N: usize>(&mut self) -> Result<[u8; N]> {
let bytes = self.take(N)?;
bytes
.try_into()
.map_err(|_| Error::QDataStreamFailed { context: None })
}
pub fn read_i32(&mut self) -> Result<i32> {
self.take_array().map(i32::from_be_bytes)
}
pub fn read_u32(&mut self) -> Result<u32> {
self.take_array().map(u32::from_be_bytes)
}
pub fn read_i64(&mut self) -> Result<i64> {
self.take_array().map(i64::from_be_bytes)
}
pub fn read_u64(&mut self) -> Result<u64> {
self.take_array().map(u64::from_be_bytes)
}
fn read_block(&mut self) -> Result<Option<&'a [u8]>> {
let start = self.pos;
let len = self.read_u32()?;
if len == NULL_LENGTH {
return Ok(None);
}
let block = usize::try_from(len)
.map_err(|_| Error::QDataStreamFailed { context: None })
.and_then(|len| self.take(len));
if block.is_err() {
self.pos = start;
}
block.map(Some)
}
pub fn read_bytearray(&mut self) -> Result<Option<&'a [u8]>> {
self.read_block()
}
pub fn read_qstring(&mut self) -> Result<Option<String>> {
let start = self.pos;
let decoded = self
.read_block()
.and_then(|block| block.map(decode_utf16be).transpose());
if decoded.is_err() {
self.pos = start;
}
decoded
}
}
fn decode_utf16be(bytes: &[u8]) -> Result<String> {
let (units, rest) = bytes.as_chunks::<2>();
if !rest.is_empty() {
return Err(Error::QDataStreamFailed { context: None });
}
char::decode_utf16(units.iter().map(|&unit| u16::from_be_bytes(unit)))
.collect::<std::result::Result<String, _>>()
.map_err(|_| Error::QDataStreamFailed { context: None })
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct QWriter {
buf: Vec<u8>,
}
impl QWriter {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.buf
}
#[must_use]
pub fn into_bytes(self) -> Vec<u8> {
self.buf
}
pub fn write_i32(&mut self, value: i32) {
self.buf.extend_from_slice(&value.to_be_bytes());
}
pub fn write_u32(&mut self, value: u32) {
self.buf.extend_from_slice(&value.to_be_bytes());
}
pub fn write_i64(&mut self, value: i64) {
self.buf.extend_from_slice(&value.to_be_bytes());
}
pub fn write_u64(&mut self, value: u64) {
self.buf.extend_from_slice(&value.to_be_bytes());
}
fn write_len(&mut self, len: usize) -> Result<()> {
let len = u32::try_from(len)
.ok()
.filter(|&len| len != NULL_LENGTH)
.ok_or(Error::QDataStreamFailed { context: None })?;
self.write_u32(len);
Ok(())
}
pub fn write_bytearray(&mut self, value: Option<&[u8]>) -> Result<()> {
match value {
None => self.write_u32(NULL_LENGTH),
Some(bytes) => {
self.write_len(bytes.len())?;
self.buf.extend_from_slice(bytes);
}
}
Ok(())
}
pub fn write_qstring(&mut self, value: Option<&str>) -> Result<()> {
let Some(text) = value else {
self.write_u32(NULL_LENGTH);
return Ok(());
};
let byte_len = text
.encode_utf16()
.count()
.checked_mul(2)
.ok_or(Error::QDataStreamFailed { context: None })?;
self.write_len(byte_len)?;
self.buf.reserve(byte_len);
for unit in text.encode_utf16() {
self.buf.extend_from_slice(&unit.to_be_bytes());
}
Ok(())
}
}