use snafu::{Backtrace, Snafu};
use crate::stream::StreamError;
mod decode;
mod encode;
pub(crate) use decode::DecodingScope;
pub use decode::*;
pub use encode::*;
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Default)]
pub enum Format {
Blob(u16),
Data(DataFormat),
#[default]
Fluid,
}
impl Format {
pub const fn data(ordinal: u8) -> Self {
Self::Data(DataFormat {
blob_size: 0,
data_fields: 0,
ordinal,
})
}
pub const fn is_structured(self) -> bool {
matches!(self, Self::Data(..) | Self::Fluid)
}
pub const fn with(self, other: Self) -> Self {
match (self, other) {
(Format::Blob(f1), Format::Blob(f2)) => {
assert!(
f1 as u32 + f2 as u32 <= u16::MAX as u32,
"blob size overflow in Format::with"
);
Self::Blob(f1 + f2)
}
(Format::Blob(size), Format::Data(_)) | (Format::Blob(size), Format::Fluid) => {
DataFormat {
blob_size: size,
data_fields: 1,
ordinal: 0,
}
.as_format()
}
(Format::Data(format), Format::Blob(size)) => {
assert!(
format.blob_size as u32 + size as u32 <= u16::MAX as u32,
"blob size overflow in Format::with"
);
DataFormat {
blob_size: format.blob_size + size,
data_fields: format.data_fields,
ordinal: format.ordinal,
}
.as_format()
}
(Format::Data(format), Format::Data(_)) | (Format::Data(format), Format::Fluid) => {
assert!(
(format.data_fields as u16) < u8::MAX as u16,
"data fields overflow in Format::with"
);
DataFormat {
blob_size: format.blob_size,
data_fields: format.data_fields + 1,
ordinal: format.ordinal,
}
.as_format()
}
(Format::Fluid, Format::Blob(_))
| (Format::Fluid, Format::Data(_))
| (Format::Fluid, Format::Fluid) => Format::Fluid,
}
}
pub const fn as_data_format(self) -> DataFormat {
match self {
Format::Blob(size) => DataFormat {
blob_size: size,
data_fields: 0,
ordinal: 0,
},
Format::Data(format) => format,
Format::Fluid => DataFormat {
blob_size: 0,
data_fields: 1,
ordinal: 0,
},
}
}
pub fn encode_default_value(
&self,
writer: &mut (impl encode::WritesEncodable + ?Sized),
) -> Result<(), CodecError> {
match self {
Format::Blob(size) => {
for _ in 0..*size {
0u8.encode(writer)?;
}
Ok(())
}
Format::Data(..) | Format::Fluid => Ok(()),
}
}
pub fn encode_default_header(
&self,
writer: &mut (impl encode::WritesEncodable + ?Sized),
) -> Result<(), CodecError> {
match self {
Format::Blob(..) => Ok(()),
Format::Data(format) => DataHeader {
count: 0,
format: *format,
}
.encode(writer),
Format::Fluid => DataHeader {
count: 0,
format: DataFormat {
blob_size: 0,
data_fields: 0,
ordinal: 0,
},
}
.encode(writer),
}
}
}
impl Encodable for Format {
const FORMAT: Format = Format::Fluid;
fn encode(&self, writer: &mut (impl WritesEncodable + ?Sized)) -> Result<(), CodecError> {
match self {
Format::Blob(size) => writer.write_data(size),
Format::Data(format) => {
writer.write_data(&format.blob_size)?;
writer.write_data(&format.data_fields)?;
writer.write_data(&format.ordinal)
}
Format::Fluid => Ok(()),
}
}
fn encode_header(
&self,
writer: &mut (impl WritesEncodable + ?Sized),
) -> Result<(), CodecError> {
let header = match self {
Format::Blob(_) => DataHeader {
count: 1,
format: DataFormat {
blob_size: 2,
data_fields: 0,
ordinal: 1,
},
},
Format::Data(_) => DataHeader {
count: 1,
format: DataFormat {
blob_size: 2 + 1 + 1,
data_fields: 0,
ordinal: 2,
},
},
Format::Fluid => DataHeader {
count: 1,
format: DataFormat {
blob_size: 0,
data_fields: 0,
ordinal: 3,
},
},
};
header.encode(writer)
}
}
impl Decodable for Format {
fn decode(
&mut self,
reader: &mut (impl ReadsDecodable + ?Sized),
header: Option<DataHeader>,
) -> Result<(), CodecError> {
let header = Self::ensure_header(header, &[1, 2, 3])?;
match header.format.ordinal {
1 => {
let mut size = 0;
reader.read_data_into(&mut size)?;
*self = Format::Blob(size);
}
2 => {
let mut blob_size = 0;
reader.read_data_into(&mut blob_size)?;
let mut data_fields = 0;
reader.read_data_into(&mut data_fields)?;
let mut ordinal = 0;
reader.read_data_into(&mut ordinal)?;
*self = Format::Data(DataFormat {
blob_size,
data_fields,
ordinal,
});
}
3 => {
*self = Format::Fluid;
}
_ => unreachable!(),
}
Ok(())
}
}
#[derive(Default, Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct DataFormat {
pub blob_size: u16,
pub data_fields: u8,
pub ordinal: u8,
}
impl DataFormat {
pub const fn as_format(self) -> Format {
Format::Data(self)
}
}
#[derive(Default, Copy, Clone, Debug, PartialEq)]
pub struct DataHeader {
pub count: u32,
pub format: DataFormat,
}
impl Encodable for DataHeader {
#[allow(clippy::eq_op)]
const FORMAT: Format = Format::Blob(0)
.with(Format::Blob((u32::BITS / 8) as u16)) .with(Format::Blob((u16::BITS / 8) as u16)) .with(Format::Blob((u8::BITS / 8) as u16)) .with(Format::Blob((u8::BITS / 8) as u16));
#[inline(always)]
fn encode(
&self,
writer: &mut (impl encode::WritesEncodable + ?Sized),
) -> Result<(), CodecError> {
writer.write_all(&self.count.to_le_bytes())?;
writer.write_all(&self.format.blob_size.to_le_bytes())?;
writer.write_all(&self.format.data_fields.to_le_bytes())?;
writer.write_all(&self.format.ordinal.to_le_bytes())?;
Ok(())
}
#[inline(always)]
fn encode_header(
&self,
_writer: &mut (impl WritesEncodable + ?Sized),
) -> Result<(), CodecError> {
Ok(())
}
}
impl Decodable for DataHeader {
fn decode(
&mut self,
reader: &mut (impl decode::ReadsDecodable + ?Sized),
header: Option<DataHeader>,
) -> Result<(), CodecError> {
Self::ensure_no_header(header)?;
let mut buf = [0u8; 8];
reader.read_exact(&mut buf)?;
self.count = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
self.format.blob_size = u16::from_le_bytes([buf[4], buf[5]]);
self.format.data_fields = buf[6];
self.format.ordinal = buf[7];
Ok(())
}
}
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum CodecError {
#[snafu(display("can't encode data with the format {format:?} as structured data"))]
UnstructuredFormat {
format: Format,
backtrace: Backtrace,
},
#[snafu(display("expected to decode {expected:?}, but found {actual:?}"))]
UnexpectedDataFormat {
expected: Format,
actual: Option<DataHeader>,
backtrace: Backtrace,
},
#[snafu(display("unsupported data format (ordinal {ordinal:?})"))]
UnsupportedDataFormat { ordinal: u8, backtrace: Backtrace },
#[snafu(display("expected to decode {length} more bytes of blob field data"))]
MissingBlobLength { length: u16 },
#[snafu(display("expected to decode {count} more fields of data"))]
MissingDataFields { count: u8 },
#[snafu(display("sequence length {length} exceeds maximum count ({})", u32::MAX))]
CountOverflow { length: usize },
#[snafu(display("an unspecified map's keys must be Text, but found ordinal {ordinal}"))]
UnsupportedUnspecifiedMapKey { ordinal: u8 },
#[snafu(display("an unspecified map has {keys} keys but {values} values"))]
UnspecifiedMapLengthMismatch { keys: usize, values: usize },
#[snafu(display("unexpected end of stream"))]
UnexpectedEof,
#[snafu(display("byte limit exceeded during decoding"))]
ByteLimitExceeded,
#[snafu(display("nesting depth limit exceeded during decoding"))]
DepthLimitExceeded,
#[snafu(display("error when reading or writing from a data stream: {source}"))]
Stream { source: StreamError },
}
impl From<StreamError> for CodecError {
fn from(value: StreamError) -> Self {
Self::Stream { source: value }
}
}
#[inline]
pub fn try_count(length: usize) -> Result<u32, CodecError> {
u32::try_from(length).map_err(|_| CodecError::CountOverflow { length })
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{stream::Writes, types::Text};
pub(super) struct TestData {
pub num_a: i32,
pub num_b: u64,
pub text: Text,
}
impl Encodable for TestData {
const FORMAT: Format = Format::data(0)
.with(i32::FORMAT)
.with(u64::FORMAT)
.with(Text::FORMAT);
fn encode(
&self,
writer: &mut (impl encode::WritesEncodable + ?Sized),
) -> Result<(), CodecError> {
writer.write_data(&self.num_a)?;
writer.write_data(&self.num_b)?;
writer.write_data(&self.text)?;
Ok(())
}
}
impl Default for TestData {
fn default() -> Self {
Self {
num_a: -3i32,
num_b: 333u64,
text: "var-length field!".into(),
}
}
}
pub(super) fn encode_test_data(bytes: &mut Vec<u8>) {
bytes.write_all(&1u32.to_le_bytes()).unwrap(); bytes.write_all(&12u16.to_le_bytes()).unwrap(); bytes.write_all(&1u8.to_le_bytes()).unwrap(); bytes.write_all(&0u8.to_le_bytes()).unwrap();
bytes
.write_all(&TestData::default().num_a.to_le_bytes())
.unwrap();
bytes
.write_all(&TestData::default().num_b.to_le_bytes())
.unwrap();
bytes
.write_all(&(TestData::default().text.len() as u32).to_le_bytes())
.unwrap(); bytes.write_all(&1u16.to_le_bytes()).unwrap(); bytes.write_all(&0u8.to_le_bytes()).unwrap(); bytes.write_all(&0u8.to_le_bytes()).unwrap(); bytes
.write_all(TestData::default().text.as_bytes())
.unwrap();
}
#[test]
fn format_codec() {
let blob_format = Format::Blob(69);
let mut bytes = vec![];
bytes.write_data(&blob_format).unwrap();
assert_eq!(blob_format, bytes.as_slice().read_data().unwrap());
let data_format = Format::Data(DataFormat {
blob_size: 9001,
data_fields: 42,
ordinal: 137,
});
let mut bytes = vec![];
bytes.write_data(&data_format).unwrap();
assert_eq!(data_format, bytes.as_slice().read_data().unwrap());
let fluid_format = Format::Fluid;
let mut bytes = vec![];
bytes.write_data(&fluid_format).unwrap();
assert_eq!(fluid_format, bytes.as_slice().read_data().unwrap());
}
}