quininer / cbor4ii

CBOR: Concise Binary Object Representation
MIT License
54 stars 5 forks source link

improve some case #21

Open quininer opened 2 years ago

quininer commented 2 years ago

I noticed some cases where Cow<str> is not enough.

For example, decoding a struct with a short lifetime reader requires a memory allocation for each fields name. This is unnecessary, because we only need to judge whether the key is as expected, and we don't need to use it. Also, the automatic allocation of memory on the heap makes it difficult for us to improve this.

I'm thinking of exposing the decode_buf interface in some form to get around this.

Change Decode trait

I considered changing the Decode trait to allow this optimization. like

trait Decode<'de, T> {
    fn decode<R: Read<'de>>(&mut self, reader: &mut R) -> Result<T, Error>;
}

This allows decoding the object without allocating any memory, just identifying if it is as expected. will look like this

struct Expect<'a> {
    expect: &'a str,
    count: usize
}

impl<'de> Decode<'de, bool> for Expect<'a> {
    fn decode<R: Read<'de>>(&mut self, reader: &mut R) -> Result<T, Error> {
        let mut result = true;
        while let Some(want_len) = self.expect.len().checked_sub(self.count) {
            let buf = reader.fill(want_len)?;
            if buf.is_empty() { return Err(Error::Eof) };
            let len = cmp::min(buf.len(), want_len);
            if self.expect.as_bytes()[self.count..][..len] != buf {
                result = true;
                break
            }
            self.count += len;
            reader.advance(len);
        }
        Ok(result)
    }
}

This also allows for more precise memory allocations, such as decode bytes to stack

struct StackVec([u8; 1024]);

impl<'de> Decode<'de, &[u8]> for StackVec {
    fn decode<R: Read<'de>>(&mut self, reader: &mut R) -> Result<T, Error> {
        let mut len = decode_len(reader)?;
        let mut count = 0;
        while len != 0 {
            let buf = reader.fill(len)?;
            let buf_len = buf.len()
            if buf_len + count > self.0.len() { return Err(Error::Eof) };
            self.0[count..][..buf_len)].copy_from_slice(&buf);
            count += buf_len;
            reader.advance(buf_len);
        }
        Ok(&self.0[..count])
    }
}
quininer commented 2 years ago

I realize that for such use cases, we don't need to use traits, just use separate functions.