Support for non-contiguous buffers in tokio_util::codec::Encoder
#4122
Labels
A-tokio-util
Area: The tokio-util crate
C-feature-request
Category: A feature request.
M-codec
Module: tokio-util/codec
Is your feature request related to a problem? Please describe.
tokio_util::codec::Encoder
does not support zero-copy writes. It also makes it very awkward to support a variable-width length-prefixed frame when the size of the encoded data is not known prior to writing the data.Today, the
Encoder::encode()
method takes a&mut BytesMut
, which is a contiguous buffer. This means there's no way to add data to it without copying the data. In the variable-width length-prefixed scenario, this requires writing the data first in order to calculate the length (and therefore the size of the encoded length), so this also involves a copy (either writing to a side buffer, or writing to theBytesMut
prior to shifting the data to fit the encoded length).This is especially frustrating as the lower-level method that
Framed
uses to actually write out the data works with non-contiguous buffers, butFramed
does not take advantage of that.Describe the solution you'd like
Change the type from
BytesMut
to something that supports non-contiguous buffers. I don't know offhand what existing type would work for this, but it's something that should support the same basic functionality ofBytesMut
while also allowing for non-contiguous buffers.From the perspective of
Framed
, once the data has been written by theEncoder
impl,Framed
really only cares that it implementsBuf
. So this new type could perhaps support appendingT where T: Buf + 'static
, thus allowing it to take ownership of arbitrary chunks of data.As for the variable-width length-prefixed scenario, where it makes sense to write into the existing allocated space if there is enough, what we'd like is to be able to write into the existing allocated space, then insert the length prefix before the written data. There are multiple possible APIs here, and I'm not sure what the best one is, but ultimately I want to write into the allocated space and then write the length without copying data unnecessarily. The buffer type could support various operations that split its data into separate chunks (without copying data) in order to insert or reorder them.
Incidentally, this would also help
tokio_util::codec::length_delimited::LengthDelimitedCodec
, which has to copy the data from theBytes
item into the buffer, but with this new approach it would just append it as a chunk.Describe alternatives you've considered
Instead of a new type, we could try changing the
encode()
API such that instead of being given a&mut BytesMut
, it could be given aBytesMut
and required to return aResult<Self::Buffer, Self::Error>
whereSelf::Buffer
is something liketype Buffer: Buf
and the documentation can suggest setting it toBytesMut
in the simple case (existing encoders can just return the inputBytesMut
).This approach also allows for preventing
Encoder
s from altering previously-written data. The trait can instead guarantee that the inputBytesMut
has alen() == 0
(it being provided mostly as a container for existing allocated space). And it can be created by callingbuffer.split_off(buffer.len())
.An upside here is when the buffer has to be extended, it doesn't have to copy all of the previously-written data, only the new frame.
A downside here is if the
Encoder
impl doesn't return the inputBytesMut
, then any additional reserved capacity in it will be thrown away.Additional context
AsyncWrite
gained vectored writes in #3149, it's very annoying thattokio_util::codec
doesn't support this.The text was updated successfully, but these errors were encountered: