Skip to content

Commit

Permalink
Implement BufMut for &mut [MaybeUninit<u8>] (#597)
Browse files Browse the repository at this point in the history
  • Loading branch information
mina86 authored Feb 10, 2023
1 parent 99a2754 commit b29112c
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 b29112c

Please sign in to comment.