use snafu::{Backtrace, Snafu};
use crate::stream::StreamError;
mod decode;
mod encode;
pub use decode::*;
pub use encode::*;
pub type FormatMetadata = u16;
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Default)]
pub enum Format {
Blob(FormatMetadata),
Data(DataFormat),
#[default]
Fluid,
}
impl Format {
pub const fn data(ordinal: FormatMetadata) -> Self {
Self::Data(DataFormat {
ordinal,
blob_size: 0,
data_fields: 0,
})
}
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)) => Self::Blob(f1 + f2),
(Format::Blob(size), Format::Data(_)) | (Format::Blob(size), Format::Fluid) => {
DataFormat {
ordinal: 0,
blob_size: size,
data_fields: 1,
}
.as_format()
}
(Format::Data(format), Format::Blob(size)) => DataFormat {
ordinal: format.ordinal,
blob_size: format.blob_size + size,
data_fields: format.data_fields,
}
.as_format(),
(Format::Data(format), Format::Data(_)) | (Format::Data(format), Format::Fluid) => {
DataFormat {
ordinal: format.ordinal,
blob_size: format.blob_size,
data_fields: format.data_fields + 1,
}
.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 {
ordinal: 0,
blob_size: size,
data_fields: 0,
},
Format::Data(format) => format,
Format::Fluid => DataFormat {
ordinal: 0,
blob_size: 0,
data_fields: 1,
},
}
}
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 {
ordinal: 0,
blob_size: 0,
data_fields: 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.ordinal)?;
writer.write_data(&format.blob_size)?;
writer.write_data(&format.data_fields)
}
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 {
ordinal: 1,
blob_size: 2,
data_fields: 0,
},
},
Format::Data(_) => DataHeader {
count: 1,
format: DataFormat {
ordinal: 2,
blob_size: 2 + 2 + 2,
data_fields: 0,
},
},
Format::Fluid => DataHeader {
count: 1,
format: DataFormat {
ordinal: 3,
blob_size: 0,
data_fields: 0,
},
},
};
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 ordinal = 0;
reader.read_data_into(&mut ordinal)?;
let mut blob_size = 0;
reader.read_data_into(&mut blob_size)?;
let mut data_fields = 0;
reader.read_data_into(&mut data_fields)?;
*self = Format::Data(DataFormat {
ordinal,
blob_size,
data_fields,
});
}
3 => {
*self = Format::Fluid;
}
_ => unreachable!(),
}
Ok(())
}
}
#[derive(Default, Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct DataFormat {
pub ordinal: FormatMetadata,
pub blob_size: FormatMetadata,
pub data_fields: FormatMetadata,
}
impl DataFormat {
pub const fn as_format(self) -> Format {
Format::Data(self)
}
}
#[derive(Default, Copy, Clone, Debug, PartialEq)]
pub struct DataHeader {
pub count: FormatMetadata,
pub format: DataFormat,
}
impl Encodable for DataHeader {
const FORMAT: Format = Format::Blob(0)
.with(Format::Blob((u16::BITS / 8) as FormatMetadata))
.with(Format::Blob((u16::BITS / 8) as FormatMetadata))
.with(Format::Blob((u16::BITS / 8) as FormatMetadata))
.with(Format::Blob((u16::BITS / 8) as FormatMetadata));
#[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.ordinal.to_le_bytes())?;
writer.write_all(&self.format.blob_size.to_le_bytes())?;
writer.write_all(&self.format.data_fields.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 bytes = [0u8; (u16::BITS / 8) as usize];
reader.read_exact(&mut bytes)?;
self.count = u16::from_le_bytes(bytes);
reader.read_exact(&mut bytes)?;
self.format.ordinal = u16::from_le_bytes(bytes);
reader.read_exact(&mut bytes)?;
self.format.blob_size = u16::from_le_bytes(bytes);
reader.read_exact(&mut bytes)?;
self.format.data_fields = u16::from_le_bytes(bytes);
Ok(())
}
}
#[derive(Debug, Snafu)]
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: FormatMetadata,
backtrace: Backtrace,
},
#[snafu(display("expected to decode {length} more bytes of blob field data"))]
MissingBlobLength { length: FormatMetadata },
#[snafu(display("expected to decode {count} more fields of data"))]
MissingDataFields { count: FormatMetadata },
#[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 }
}
}
#[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(&1u16.to_le_bytes()).unwrap(); bytes.write_all(&0u16.to_le_bytes()).unwrap(); bytes.write_all(&12u16.to_le_bytes()).unwrap(); bytes.write_all(&1u16.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 u16).to_le_bytes())
.unwrap(); bytes.write_all(&0u16.to_le_bytes()).unwrap(); bytes.write_all(&1u16.to_le_bytes()).unwrap(); bytes.write_all(&0u16.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 {
ordinal: 1337,
blob_size: 9001,
data_fields: 42,
});
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());
}
}