bytes icon indicating copy to clipboard operation
bytes copied to clipboard

API to peek at integers of buffer

Open vimpunk opened this issue 4 years ago • 3 comments

This is a feature request for the Buf trait to provide an API to peek at an integer of the buffer without advancing the underlying cursor. E.g. there is get_u32 and it would be really useful to have the equivalent but non cursor advancing peek_u32 that returns the next u32 of buffer.

If this feature already exists, my apologies, but I seem to have missed it. If not, is there any reason it is not included?

vimpunk avatar Mar 28 '20 20:03 vimpunk

Peeking is a bit harder. Buf is basically a byte optimized iterator. In order to provide peaking of multiple bytes, you need to be able to rewind. Rewinding is not trivial to do for some implementations (single linked list, for example).

There are a few options. First, you can take T: Buf + Clone. In which case, this implies that you can clone the buffer to "peek".

Another alternative is something I proposed in https://github.com/tokio-rs/bytes/issues/300, but this would work only for "bufs" that are in continuous memory.

carllerche avatar Mar 28 '20 20:03 carllerche

@carllerche Those are fair points.

Maybe this could be a separate trait implemented only by buffers that abstract over contiguous memory, such as BytesMut (at least as far as I understand it is contiguous, by inspecting its source? Maybe the trait could be called Peek and offer the peek_<integer> methods. What do you think? Have I missed something?


This is a bit unrelated, but I wanted to give you an idea since my use case involves tokio_codec and as far as I know, you are its author.

My use case of this in implementing the Decoder trait is to decode messages in a TCP based binary protocol, where messages are prefixed with a fixed size length header, whether the buffer has the full message, and only proceeding to decode (i.e. consume the bytes in the buffer) if it does. Now, this only works if the message length prefix is kept in the buffer until the whole message can be extracted (thus the need for peeking).

Currently I work around this limitation with the following pattern, exploiting the fact that Buf is implemented for byte slices:

let mut buf: BytesMut = ...;

// ...

let mut tmp_buf = buf.bytes();
let payload_len = tmp_buf.get_u32() as usize;

// check that we got the full payload in the buffer (NOTE: we need
// to add the message length prefix's byte count to payload_len
// since the buffer cursor was not advanced and thus we need to
// consider the prefix too)
if buf.remaining() >= 4 + payload_len {
    // we have the full message in the buffer so advance the buffer
    // cursor past the message length header
    buf.advance(4);
} else {
    return Ok(None);
}

I would think this is a common pattern as I'm sure other protocols also employ length prefixes/headers. Is there a better way to do this?

vimpunk avatar Mar 29 '20 17:03 vimpunk

I'm new to rust. May be this is a solution?

            let mut len_bytes = [0; 4];
            len_bytes.clone_from_slice(&bytesMut[0..4]);
            let len = u32::from_be_bytes(len_bytes);

alexniver avatar Jun 21 '22 04:06 alexniver