consortium-codec 0.2.0

Codec traits and implementations for Consortium IPC serialization
Documentation
#[cfg(any(feature = "postcard", feature = "prost", feature = "rkyv"))]
use consortium_codec::CodecFor;

#[cfg(any(feature = "postcard", feature = "prost", feature = "rkyv"))]
fn round_trip<'buf, T, C>(msg: &'buf T, buf: &'buf mut [u8]) -> C::Decoded<'buf>
where
    C: CodecFor<T>,
{
    let len = C::encode(msg, buf).expect("encode should succeed");
    C::decode(&buf[..len]).expect("decode should succeed")
}

#[cfg(feature = "postcard")]
mod postcard {
    use super::round_trip;
    use consortium_codec::PostcardCodec;
    use serde::{Deserialize, Serialize};

    #[derive(Debug, Deserialize, PartialEq, Serialize)]
    struct Command {
        opcode: u8,
        argument: u16,
    }

    #[test]
    fn typed_message_round_trips_through_public_trait() {
        let msg = Command {
            opcode: 3,
            argument: 0x1204,
        };
        let mut buf = [0u8; 16];

        let decoded = round_trip::<_, PostcardCodec>(&msg, &mut buf);

        assert_eq!(decoded, msg);
    }
}

#[cfg(feature = "prost")]
mod prost {
    use super::round_trip;
    use consortium_codec::ProstCodec;
    use prost::Message;
    use prost::bytes::{Buf, BufMut};
    use prost::encoding::{self, DecodeContext, WireType};

    #[derive(Clone, Debug, Default, PartialEq)]
    struct Command {
        opcode: u32,
        argument: u32,
    }

    impl Message for Command {
        fn encode_raw(&self, buf: &mut impl BufMut)
        where
            Self: Sized,
        {
            encoding::uint32::encode(1, &self.opcode, buf);
            encoding::uint32::encode(2, &self.argument, buf);
        }

        fn merge_field(
            &mut self,
            tag: u32,
            wire_type: WireType,
            buf: &mut impl Buf,
            ctx: DecodeContext,
        ) -> Result<(), prost::DecodeError>
        where
            Self: Sized,
        {
            match tag {
                1 => encoding::uint32::merge(wire_type, &mut self.opcode, buf, ctx),
                2 => encoding::uint32::merge(wire_type, &mut self.argument, buf, ctx),
                _ => encoding::skip_field(wire_type, tag, buf, ctx),
            }
        }

        fn encoded_len(&self) -> usize {
            encoding::uint32::encoded_len(1, &self.opcode)
                + encoding::uint32::encoded_len(2, &self.argument)
        }

        fn clear(&mut self) {
            *self = Self::default();
        }
    }

    #[test]
    fn typed_message_round_trips_through_public_trait() {
        let msg = Command {
            opcode: 3,
            argument: 0x1204,
        };
        let mut buf = [0u8; 16];

        let decoded = round_trip::<_, ProstCodec>(&msg, &mut buf);

        assert_eq!(decoded, msg);
    }
}

#[cfg(all(feature = "rkyv", feature = "alloc"))]
mod rkyv {
    use super::round_trip;
    use consortium_codec::RkyvCodec;
    use rkyv::{Archive, Serialize};

    #[derive(Archive, Debug, PartialEq, Serialize)]
    struct Command {
        opcode: u8,
        argument: u16,
    }

    #[test]
    fn typed_message_round_trips_through_public_trait() {
        let msg = Command {
            opcode: 3,
            argument: 0x1204,
        };
        let mut buf = [0u8; 16];

        let decoded = round_trip::<_, RkyvCodec<64>>(&msg, &mut buf);

        assert_eq!(decoded.opcode, msg.opcode);
        assert_eq!(decoded.argument, msg.argument);
    }
}

#[cfg(all(feature = "rkyv", not(feature = "alloc")))]
mod rkyv_no_alloc {
    use super::round_trip;
    use consortium_codec::{RkyvCodec, TrustedArchive};
    use rkyv::{Archive, Serialize};

    #[repr(C, align(16))]
    #[derive(Archive, Debug, PartialEq, Serialize)]
    struct Command {
        opcode: u8,
        argument: u16,
    }

    // SAFETY: `Command` archives to fixed-size integer fields with no
    // address-space-local state, and this test decodes from a 16-byte-aligned
    // buffer containing bytes produced by `RkyvCodec::encode` for `Command`.
    unsafe impl TrustedArchive for Command {}

    #[repr(align(16))]
    struct AlignedBuf<const N: usize>([u8; N]);

    #[test]
    fn trusted_message_round_trips_through_public_trait_without_alloc() {
        let msg = Command {
            opcode: 3,
            argument: 0x1204,
        };
        let mut buf = AlignedBuf([0u8; 32]);

        let decoded = round_trip::<_, RkyvCodec<64>>(&msg, &mut buf.0);

        assert_eq!(decoded.opcode, msg.opcode);
        assert_eq!(decoded.argument, msg.argument);
    }
}