Skip to content

Commit

Permalink
Implement BufMut for &mut [MaybeUninit<u8>]
Browse files Browse the repository at this point in the history
This will allow using BufMut without having to initialise memory
first.  In particular, this will allow simple integration with tokio:

    #![feature(maybe_uninit_slice)]

    use tokio::io::AsyncReadExt;

    let mut tokio_file = /* ... */;
    let mut buf = [MaybeUninit::uninit(); 4096];
    let len = tokio_file.read_buf(&mut &mut buf[..]);
    // SAFETY: read initialised first len bytes of the buffer.
    let buf = unsafe {
        MaybeUninit::slice_assume_init_mut(&buf[..len])
    };
  • Loading branch information
mina86 committed Feb 9, 2023
1 parent 99a2754 commit f9ca1ef
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 13 deletions.
35 changes: 35 additions & 0 deletions src/buf/buf_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,41 @@ unsafe impl BufMut for &mut [u8] {
}
}

unsafe impl BufMut for &mut [core::mem::MaybeUninit<u8>] {
#[inline]
fn remaining_mut(&self) -> usize {
self.len()
}

#[inline]
fn chunk_mut(&mut self) -> &mut UninitSlice {
UninitSlice::from_uninit_slice(self)
}

#[inline]
unsafe fn advance_mut(&mut self, cnt: usize) {
// Lifetime dance taken from `impl Write for &mut [u8]`.
let (_, b) = core::mem::replace(self, &mut []).split_at_mut(cnt);
*self = b;
}

#[inline]
fn put_slice(&mut self, src: &[u8]) {
self.chunk_mut()[..src.len()].copy_from_slice(src);
unsafe {
self.advance_mut(src.len());
}
}

fn put_bytes(&mut self, val: u8, cnt: usize) {
assert!(self.remaining_mut() >= cnt);
unsafe {
ptr::write_bytes(self.as_mut_ptr() as *mut u8, val, cnt);
self.advance_mut(cnt);
}
}
}

unsafe impl BufMut for Vec<u8> {
#[inline]
fn remaining_mut(&self) -> usize {
Expand Down
123 changes: 110 additions & 13 deletions tests/test_buf_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use bytes::buf::UninitSlice;
use bytes::{BufMut, BytesMut};
use core::fmt::Write;
use core::mem::MaybeUninit;
use core::usize;

#[test]
Expand Down Expand Up @@ -101,24 +102,120 @@ fn test_clone() {
assert!(buf != buf2);
}

fn do_test_slice_small<T: ?Sized>(make: impl Fn(&mut [u8]) -> &mut T)
where
for<'r> &'r mut T: BufMut,
{
let mut buf = [b'X'; 8];

let mut slice = make(&mut buf[..]);
slice.put_bytes(b'A', 2);
slice.put_u8(b'B');
slice.put_slice(b"BCC");
assert_eq!(2, slice.remaining_mut());
assert_eq!(b"AABBCCXX", &buf[..]);

let mut slice = make(&mut buf[..]);
slice.put_u32(0x61626364);
assert_eq!(4, slice.remaining_mut());
assert_eq!(b"abcdCCXX", &buf[..]);

let mut slice = make(&mut buf[..]);
slice.put_u32_le(0x30313233);
assert_eq!(4, slice.remaining_mut());
assert_eq!(b"3210CCXX", &buf[..]);
}

fn do_test_slice_large<T: ?Sized>(make: impl Fn(&mut [u8]) -> &mut T)
where
for<'r> &'r mut T: BufMut,
{
const LEN: usize = 100;
const FILL: [u8; LEN] = [b'Y'; LEN];

let test = |fill: &dyn Fn(&mut &mut T, usize)| {
for buf_len in 0..LEN {
let mut buf = [b'X'; LEN];
for fill_len in 0..=buf_len {
let mut slice = make(&mut buf[..buf_len]);
fill(&mut slice, fill_len);
assert_eq!(buf_len - fill_len, slice.remaining_mut());
let (head, tail) = buf.split_at(fill_len);
assert_eq!(&FILL[..fill_len], head);
assert!(tail.iter().all(|b| *b == b'X'));
}
}
};

test(&|slice, fill_len| slice.put_slice(&FILL[..fill_len]));
test(&|slice, fill_len| slice.put_bytes(FILL[0], fill_len));
}

fn do_test_slice_put_slice_panics<T: ?Sized>(make: impl Fn(&mut [u8]) -> &mut T)
where
for<'r> &'r mut T: BufMut,
{
let mut buf = [b'X'; 4];
let mut slice = make(&mut buf[..]);
slice.put_slice(b"12345");
}

fn do_test_slice_put_bytes_panics<T: ?Sized>(make: impl Fn(&mut [u8]) -> &mut T)
where
for<'r> &'r mut T: BufMut,
{
let mut buf = [b'X'; 4];
let mut slice = make(&mut buf[..]);
slice.put_bytes(b'1', 5);
}

#[test]
fn test_slice_buf_mut_small() {
do_test_slice_small(|x| x);
}

#[test]
fn test_mut_slice() {
let mut v = vec![0, 0, 0, 0];
let mut s = &mut v[..];
s.put_u32(42);
fn test_slice_buf_mut_large() {
do_test_slice_large(|x| x);
}

assert_eq!(s.len(), 0);
assert_eq!(&v, &[0, 0, 0, 42]);
#[test]
#[should_panic]
fn test_slice_buf_mut_put_slice_overflow() {
do_test_slice_put_slice_panics(|x| x);
}

#[test]
fn test_slice_put_bytes() {
let mut v = [0, 0, 0, 0];
let mut s = &mut v[..];
s.put_u8(17);
s.put_bytes(19, 2);
assert_eq!(1, s.remaining_mut());
assert_eq!(&[17, 19, 19, 0], &v[..]);
#[should_panic]
fn test_slice_buf_mut_put_bytes_overflow() {
do_test_slice_put_bytes_panics(|x| x);
}

fn make_maybe_uninit_slice(slice: &mut [u8]) -> &mut [MaybeUninit<u8>] {
// SAFETY: [u8] has the same layout as [MaybeUninit<u8>].
unsafe { core::mem::transmute(slice) }
}

#[test]
fn test_maybe_uninit_buf_mut_small() {
do_test_slice_small(make_maybe_uninit_slice);
}

#[test]
fn test_maybe_uninit_buf_mut_large() {
do_test_slice_large(make_maybe_uninit_slice);
}

#[test]
#[should_panic]
fn test_maybe_uninit_buf_mut_put_slice_overflow() {
do_test_slice_put_slice_panics(make_maybe_uninit_slice);
}

#[test]
#[should_panic]
fn test_maybe_uninit_buf_mut_put_bytes_overflow() {
do_test_slice_put_bytes_panics(make_maybe_uninit_slice);
}

#[test]
Expand Down

0 comments on commit f9ca1ef

Please sign in to comment.