NULLx76 / ringbuffer

A fixed-size circular buffer written in Rust.
https://crates.io/crates/ringbuffer
MIT License
96 stars 20 forks source link

feat: implement Serialize and Deserialize #133

Open dzmitry-lahoda opened 8 months ago

dzmitry-lahoda commented 8 months ago

All the different ringbuffers should implement Serialize and Deserialize when the serde feature is enabled, and should do so safely and soundly.

dzmitry-lahoda commented 6 months ago

@jdonszelmann would PR with borsh serde considered for review and merge if made? behind borsh feature gate

jdonszelmann commented 6 months ago

I like the idea, but why do we need borsh? I think if you implement the Serialize and Deserialize traits that should be enough. Then you can choose any format (like postcard or even json if you really wanted)

jdonszelmann commented 6 months ago

I talked to @NULLx76 about this and we'd prefer serde to borsh

NULLx76 commented 6 months ago

Additionally I'm against this because Borsh seems related to blockchain/web3 which is not something we'd like to condone

jdonszelmann commented 6 months ago

Feel free to file a PR though

dzmitry-lahoda commented 6 months ago

please reopen when change reasoning.

here is wrapper for Const if somebody needs that

//! This module provides serialization and deserialization for the `ringbuffer` crate.
use borsh::{BorshDeserialize, BorshSerialize};
use ringbuffer::{ConstGenericRingBuffer, RingBuffer};

#[derive(Debug, Default, BorshSerialize, BorshDeserialize, Clone)]
pub struct ConstGenericRingBufferWrapper<T, const CAP: usize> {
    #[borsh(
        serialize_with = "serialize_const_generic_ring_buffer",
        deserialize_with = "deserialize_const_generic_ring_buffer"
    )]
    buffer: ConstGenericRingBuffer<T, CAP>,
}

/// Ring buffer is just fixed size array
#[cfg(test)]
impl<T: borsh::BorshSchema, const CAP: usize> borsh::BorshSchema
    for ConstGenericRingBufferWrapper<T, CAP>
{
    fn add_definitions_recursively(
        definitions: &mut std::collections::BTreeMap<
            borsh::schema::Declaration,
            borsh::schema::Definition,
        >,
    ) {
        <[T; CAP]>::add_definitions_recursively(definitions)
    }

    fn declaration() -> borsh::schema::Declaration {
        <[T; CAP]>::declaration()
    }
}

impl<T, const CAP: usize> ConstGenericRingBufferWrapper<T, CAP> {
    pub fn peek(&self) -> Option<&T> {
        self.buffer.peek()
    }

    pub fn push(&mut self, item: T) {
        self.buffer.push(item);
    }

    pub fn back(&self) -> Option<&T> {
        self.buffer.back()
    }

    pub fn iter(&self) -> impl Iterator<Item = &T> {
        self.buffer.iter()
    }

    pub fn get(&self, index: usize) -> Option<&T> {
        self.buffer.get(index)
    }

    pub fn len(&self) -> usize {
        self.buffer.len()
    }

    pub fn front(&self) -> Option<&T> {
        self.buffer.front()
    }
}

pub fn serialize_const_generic_ring_buffer<T: borsh::BorshSerialize, const CAP: usize>(
    obj: &ConstGenericRingBuffer<T, CAP>,
    writer: &mut impl std::io::prelude::Write,
) -> std::io::Result<()> {
    for item in obj.into_iter() {
        item.serialize(writer)?;
    }
    Ok(())
}

pub fn deserialize_const_generic_ring_buffer<T: borsh::BorshDeserialize, const CAP: usize>(
    reader: &mut impl std::io::prelude::Read,
) -> std::io::Result<ConstGenericRingBuffer<T, CAP>> {
    let mut buffer = ConstGenericRingBuffer::new();
    for _ in 0..CAP {
        buffer.push(T::try_from_reader(reader)?);
    }
    if T::try_from_reader(reader).is_ok() {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            "Buffer is longs than CAP",
        ));
    }
    Ok(buffer)
}
dzmitry-lahoda commented 6 months ago

sorry, some mix of things. closed for borsh. for serde open sure :) sorry for confusion.