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 std::collections::HashMap;
use std::hash::Hash;

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

/// Opinion on how to represent a `map` as a `HashMap`
pub struct HashMapOpinion<KO, VO> {
    /// Maximum number of items.
    /// Required as a safety feature to not allocate more memory than allowed.
    max_len: u32,
    /// Opinion for representing keys
    inner_k: KO,
    /// Opinion for representing values
    inner_v: VO,
}

impl<KO, VO> HashMapOpinion<KO, VO> {
    pub fn new(max_len: u32, inner_k: KO, inner_v: VO) -> Self {
        Self {
            inner_k,
            inner_v,
            max_len,
        }
    }
}

impl<K: Eq + Hash, KO: Compact1CodecOpinion<K>, V, VO: Compact1CodecOpinion<V>>
    Compact1CodecOpinion<HashMap<K, V>> for HashMapOpinion<KO, VO>
{
    fn encode<B: ImprovidentBufMut>(
        &self,
        encoder: &mut Encoder<B>,
        what: &HashMap<K, V>,
    ) -> 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 (key, value) in what {
            self.inner_k.encode(encoder, key)?;
            self.inner_v.encode(encoder, value)?;
        }

        Ok(())
    }

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

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

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

        for _ in 0..num_items {
            let k = self.inner_k.decode(decoder)?;
            let v = self.inner_v.decode(decoder)?;
            if out.insert(k, v).is_some() {
                return Err(DecError::DuplicateMapKeys);
            }
        }

        Ok(out)
    }
}