oxidef_compact1 0.1.0-alpha.1

Oxidef is an experimental interface definition language and serialisation scheme for efficient and strongly-typed payloads.
Documentation
use core::cmp::min;

use bytes::BufMut;

use crate::{
    codec::Compact1CodecOpinion,
    decoder::{DecError, Decoder, MeasureBuf},
    encoder::{EncError, Encoder, ImprovidentBufMut},
};

// TODO(allocators): support unstable allocator api...

/// Opinion on how to represent a `string`.
///
/// This includes a length limit on the string, to prevent requiring unbounded memory
/// from decoders.
#[derive(Default)]
pub struct StringOpinion {
    max_len: u32,
}

impl StringOpinion {
    #[inline]
    pub fn new(max_len: u32) -> Self {
        Self { max_len }
    }
}

impl Compact1CodecOpinion<String> for StringOpinion {
    fn encode<B: ImprovidentBufMut>(
        &self,
        encoder: &mut Encoder<B>,
        what: &String,
    ) -> Result<(), EncError>
    where
        Self: Sized,
    {
        let bytes = what.len();
        if bytes > min(self.max_len, crate::vu29::VU29_MAX_4BYTE) as usize {
            return Err(EncError::ContainerTooLong);
        }
        crate::vu29::encode_vu29(encoder.buf.deref_mut(), bytes as u32)?;

        encoder.buf.put_slice(what.as_bytes());

        Ok(())
    }

    fn decode<B: MeasureBuf>(&self, decoder: &mut Decoder<B>) -> Result<String, DecError>
    where
        String: Sized,
    {
        let num_bytes = crate::vu29::decode_vu29(&mut decoder.buf)?;

        if num_bytes > self.max_len {
            return Err(DecError::ContainerTooLong);
        }

        let mut out = vec![0u8; num_bytes as usize];

        decoder.buf.try_copy_to_slice(&mut out)?;

        String::from_utf8(out).map_err(|_| DecError::Utf8Decode)
    }
}

/// Opinion on how to represent a `bytes`.
///
/// This includes a length limit on the bytestring, to prevent requiring unbounded memory
/// from decoders.
#[derive(Default)]
pub struct BytesOpinion {
    max_len: u32,
}

impl BytesOpinion {
    #[inline]
    pub fn new(max_len: u32) -> Self {
        Self { max_len }
    }
}

// TODO is Vec<u8> what we're happy with?
impl Compact1CodecOpinion<Vec<u8>> for BytesOpinion {
    fn encode<B: ImprovidentBufMut>(
        &self,
        encoder: &mut Encoder<B>,
        what: &Vec<u8>,
    ) -> Result<(), EncError>
    where
        Self: Sized,
    {
        let bytes = what.len();
        if bytes > min(self.max_len, crate::vu29::VU29_MAX_4BYTE) as usize {
            return Err(EncError::ContainerTooLong);
        }
        crate::vu29::encode_vu29(encoder.buf.deref_mut(), bytes as u32)?;

        encoder.buf.put_slice(what);

        Ok(())
    }

    fn decode<B: MeasureBuf>(&self, decoder: &mut Decoder<B>) -> Result<Vec<u8>, DecError>
    where
        String: Sized,
    {
        let num_bytes = crate::vu29::decode_vu29(&mut decoder.buf)?;

        if num_bytes > self.max_len {
            return Err(DecError::ContainerTooLong);
        }

        let mut out = vec![0u8; num_bytes as usize];

        decoder.buf.try_copy_to_slice(&mut out)?;

        Ok(out)
    }
}

/// Opinion on how to represent a `Vec`
///
/// This includes an inner opinion on how to represent the inner items of the `Vec`.
///
/// TODO(encoding_customisation): support length encoding opinions
pub struct VecOpinion<TO> {
    /// Maximum number of items.
    /// Required as a safety feature to not allocate more memory than allowed.
    max_len: u32,
    /// Opinion for representing items
    inner: TO,
}

impl<TO> VecOpinion<TO> {
    pub fn new(max_len: u32, inner: TO) -> Self {
        Self { inner, max_len }
    }
}

impl<T, TO: Compact1CodecOpinion<T>> Compact1CodecOpinion<Vec<T>> for VecOpinion<TO> {
    fn encode<B: ImprovidentBufMut>(
        &self,
        encoder: &mut Encoder<B>,
        what: &Vec<T>,
    ) -> Result<(), EncError>
    where
        Self: Sized,
    {
        let items = what.len();
        if items > min(self.max_len, crate::vu29::VU29_MAX_4BYTE) as usize {
            return Err(EncError::ContainerTooLong);
        }
        crate::vu29::encode_vu29(encoder.buf.deref_mut(), items as u32)?;

        for item in what {
            self.inner.encode(encoder, item)?;
        }

        Ok(())
    }

    fn decode<B: MeasureBuf>(&self, decoder: &mut Decoder<B>) -> Result<Vec<T>, DecError>
    where
        Vec<T>: Sized,
    {
        let num_items = crate::vu29::decode_vu29(&mut decoder.buf)?;

        if num_items > self.max_len {
            return Err(DecError::ContainerTooLong);
        }

        let mut out = Vec::with_capacity(num_items as usize);

        for _ in 0..num_items {
            out.push(self.inner.decode(decoder)?);
        }

        Ok(out)
    }
}