Follow up to https://github.com/tokio-rs/bytes/pull/689
* If `at == self.len()`, we already know `at <= self.len()`.
* If `at == 0`, we already know `at <= self.len()`.
* use checked_sub
* return when additional == 0
* move safe operation out of unsafe block
* use spare_capacity_mut instead of chunk_mut
We don't need to check capacity because it's already been reserved
above.
* Add safety comments
* refactor to use guard clauses
This would be better written with let-else, but we won't get that until
`MSRV >= 1.65.x`.
* use if-let instead of unwrap
* reduce scope of unsafe blocks
Co-authored-by: Alice Ryhl <[email protected]>
---------
Co-authored-by: Alice Ryhl <[email protected]>
A fundamental invariant of `reserve` is that it can extend capacity
while the stored data remains the same, even if it's moved to a new
allocation. As a result, `len` can never change during a call to
`reserve`.
* set len a little more concisely
* inline set_end
* remove kind assertions
* remove a duplicate assertion
* remove redundant assertion and min
* rename set_start to advance_unchecked
Back in #362, an assertion was added to ensure that the alignment of
bytes::Shared is even so we can use the least significant bit as a flag.
bytes_mut::Shared uses the same technique but has no such assertion so
I've added one here.
tokio::io::ReadBuf uses names `new` and `uninit` for its constructors.
For consistency with that, rename recently introduced UninitSlice
constructors to match those names.
Introduce UninitSlice::from_slice and UninitSlice::from_uninit_slice
methods which safely create Uninit slice from provided slice of maybe
uninitialised or initialised memory.
In addition, add `From<&mut [T]>` implementations (for `T=u8` and
`T=MaybeUninit<u8>`) which do conversion from slice to UninitSlice.
Closes: #552
This function can be hot in applications that do a lot of encoding. Ideally would do the same for `<BytesMut as BufMut>::put_slice` and `<BytesMut as BufMut::put_u8`.
When we freeze a BytesMut, we turn it into a Vec, and then convert that
to a Bytes. Currently, this happen using Vec::into_boxed_slice, which
reallocates to a slice of the same length as the Vev if the length and
the capacity are not equal. This can pose a performance problem if the
Vec is large or if this happens many times in a loop.
Instead, let's compare the length and capacity, and if they're the same,
continue to handle this using into_boxed_slice. Otherwise, since we
have a type of vtable which can handle a separate capacity, the shared
vtable, let's turn our Vec into that kind of Bytes. While this does not
avoid allocation altogether, it performs a fixed size allocation and
avoids any need to memcpy.
Fixes calls to `reserve` when the underlying shared buffer was already
big enough to fit the requested capacity. Previously a new even larger
buffer was created anyways. This could eventually lead to an OOM
condition.
This adds an unsafe method to convert a `&mut UninitSlice` into a
`&mut [MaybeUninit<u8>]`. This method is unsafe because some of the
bytes in the slice may be initialized, and the caller should not
overwrite them with uninitialized bytes.
This came about when auditing [tokio-util's udp frame], where they want
to pass the unitialized portion of a `BytesMut` to [ReadBuf::uninit].
They need to do this unsafe pointer casting in a few places, which
complicates audits. This method lets us document the safety invariants
the caller needs to maintain when doing this conversion.
[tokio-util's udp frame]: https://github.com/tokio-rs/tokio/blob/master/tokio-util/src/udp/frame.rs#L87
[ReadBuf::uninit]: https://docs.rs/tokio/latest/tokio/io/struct.ReadBuf.html#method.uninit