Skip to content

Commit 2ac7233

Browse files
authored
Change BufMut methods that expose maybe-uninitialized bytes (#305)
- The return type of `BufMut::bytes_mut` is now `&mut [MaybeUninit<u8>]`. - The argument type of `BufMut::bytes_vectored_mut` is now `&mut [bytes::buf::IoSliceMut]`. - `bytes::buf::IoSliceMut` is a `repr(transparent)` wrapper around an `std::io::IoSliceMut`, but does not expose the inner bytes with a safe API, since they might be uninitialized. - `BufMut::bytesMut` and `BufMut::bytes_vectored_mut` are no longer `unsafe fn`, since the types encapsulate the unsafety instead.
1 parent fe2183d commit 2ac7233

File tree

7 files changed

+108
-58
lines changed

7 files changed

+108
-58
lines changed

src/buf/buf_mut.rs

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#[cfg(feature = "std")]
22
use super::Writer;
33

4-
use core::{mem, cmp, ptr, usize};
4+
use core::{cmp, mem::{self, MaybeUninit}, ptr, usize};
55

66
#[cfg(feature = "std")]
7-
use std::io::IoSliceMut;
7+
use std::fmt;
88

99
use alloc::{vec::Vec, boxed::Box};
1010

@@ -72,13 +72,15 @@ pub trait BufMut {
7272
/// let mut buf = Vec::with_capacity(16);
7373
///
7474
/// unsafe {
75-
/// buf.bytes_mut()[0] = b'h';
76-
/// buf.bytes_mut()[1] = b'e';
75+
/// // MaybeUninit::as_mut_ptr
76+
/// buf.bytes_mut()[0].as_mut_ptr().write(b'h');
77+
/// buf.bytes_mut()[1].as_mut_ptr().write(b'e');
7778
///
7879
/// buf.advance_mut(2);
7980
///
80-
/// buf.bytes_mut()[0] = b'l';
81-
/// buf.bytes_mut()[1..3].copy_from_slice(b"lo");
81+
/// buf.bytes_mut()[0].as_mut_ptr().write(b'l');
82+
/// buf.bytes_mut()[1].as_mut_ptr().write(b'l');
83+
/// buf.bytes_mut()[2].as_mut_ptr().write(b'o');
8284
///
8385
/// buf.advance_mut(3);
8486
/// }
@@ -139,13 +141,15 @@ pub trait BufMut {
139141
/// let mut buf = Vec::with_capacity(16);
140142
///
141143
/// unsafe {
142-
/// buf.bytes_mut()[0] = b'h';
143-
/// buf.bytes_mut()[1] = b'e';
144+
/// // MaybeUninit::as_mut_ptr
145+
/// buf.bytes_mut()[0].as_mut_ptr().write(b'h');
146+
/// buf.bytes_mut()[1].as_mut_ptr().write(b'e');
144147
///
145148
/// buf.advance_mut(2);
146149
///
147-
/// buf.bytes_mut()[0] = b'l';
148-
/// buf.bytes_mut()[1..3].copy_from_slice(b"lo");
150+
/// buf.bytes_mut()[0].as_mut_ptr().write(b'l');
151+
/// buf.bytes_mut()[1].as_mut_ptr().write(b'l');
152+
/// buf.bytes_mut()[2].as_mut_ptr().write(b'o');
149153
///
150154
/// buf.advance_mut(3);
151155
/// }
@@ -161,7 +165,7 @@ pub trait BufMut {
161165
/// `bytes_mut` returning an empty slice implies that `remaining_mut` will
162166
/// return 0 and `remaining_mut` returning 0 implies that `bytes_mut` will
163167
/// return an empty slice.
164-
unsafe fn bytes_mut(&mut self) -> &mut [u8];
168+
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>];
165169

166170
/// Fills `dst` with potentially multiple mutable slices starting at `self`'s
167171
/// current position.
@@ -192,13 +196,13 @@ pub trait BufMut {
192196
///
193197
/// [`readv`]: http://man7.org/linux/man-pages/man2/readv.2.html
194198
#[cfg(feature = "std")]
195-
unsafe fn bytes_vectored_mut<'a>(&'a mut self, dst: &mut [IoSliceMut<'a>]) -> usize {
199+
fn bytes_vectored_mut<'a>(&'a mut self, dst: &mut [IoSliceMut<'a>]) -> usize {
196200
if dst.is_empty() {
197201
return 0;
198202
}
199203

200204
if self.has_remaining_mut() {
201-
dst[0] = IoSliceMut::new(self.bytes_mut());
205+
dst[0] = IoSliceMut::from(self.bytes_mut());
202206
1
203207
} else {
204208
0
@@ -238,7 +242,7 @@ pub trait BufMut {
238242

239243
ptr::copy_nonoverlapping(
240244
s.as_ptr(),
241-
d.as_mut_ptr(),
245+
d.as_mut_ptr() as *mut u8,
242246
l);
243247
}
244248

@@ -280,7 +284,7 @@ pub trait BufMut {
280284

281285
ptr::copy_nonoverlapping(
282286
src[off..].as_ptr(),
283-
dst.as_mut_ptr(),
287+
dst.as_mut_ptr() as *mut u8,
284288
cnt);
285289

286290
off += cnt;
@@ -931,12 +935,12 @@ impl<T: BufMut + ?Sized> BufMut for &mut T {
931935
(**self).remaining_mut()
932936
}
933937

934-
unsafe fn bytes_mut(&mut self) -> &mut [u8] {
938+
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
935939
(**self).bytes_mut()
936940
}
937941

938942
#[cfg(feature = "std")]
939-
unsafe fn bytes_vectored_mut<'b>(&'b mut self, dst: &mut [IoSliceMut<'b>]) -> usize {
943+
fn bytes_vectored_mut<'b>(&'b mut self, dst: &mut [IoSliceMut<'b>]) -> usize {
940944
(**self).bytes_vectored_mut(dst)
941945
}
942946

@@ -950,12 +954,12 @@ impl<T: BufMut + ?Sized> BufMut for Box<T> {
950954
(**self).remaining_mut()
951955
}
952956

953-
unsafe fn bytes_mut(&mut self) -> &mut [u8] {
957+
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
954958
(**self).bytes_mut()
955959
}
956960

957961
#[cfg(feature = "std")]
958-
unsafe fn bytes_vectored_mut<'b>(&'b mut self, dst: &mut [IoSliceMut<'b>]) -> usize {
962+
fn bytes_vectored_mut<'b>(&'b mut self, dst: &mut [IoSliceMut<'b>]) -> usize {
959963
(**self).bytes_vectored_mut(dst)
960964
}
961965

@@ -971,8 +975,9 @@ impl BufMut for &mut [u8] {
971975
}
972976

973977
#[inline]
974-
unsafe fn bytes_mut(&mut self) -> &mut [u8] {
975-
self
978+
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
979+
// MaybeUninit is repr(transparent), so safe to transmute
980+
unsafe { mem::transmute(&mut **self) }
976981
}
977982

978983
#[inline]
@@ -1003,7 +1008,7 @@ impl BufMut for Vec<u8> {
10031008
}
10041009

10051010
#[inline]
1006-
unsafe fn bytes_mut(&mut self) -> &mut [u8] {
1011+
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
10071012
use core::slice;
10081013

10091014
if self.capacity() == self.len() {
@@ -1013,11 +1018,54 @@ impl BufMut for Vec<u8> {
10131018
let cap = self.capacity();
10141019
let len = self.len();
10151020

1016-
let ptr = self.as_mut_ptr();
1017-
&mut slice::from_raw_parts_mut(ptr, cap)[len..]
1021+
let ptr = self.as_mut_ptr() as *mut MaybeUninit<u8>;
1022+
unsafe {
1023+
&mut slice::from_raw_parts_mut(ptr, cap)[len..]
1024+
}
10181025
}
10191026
}
10201027

10211028
// The existence of this function makes the compiler catch if the BufMut
10221029
// trait is "object-safe" or not.
10231030
fn _assert_trait_object(_b: &dyn BufMut) {}
1031+
1032+
// ===== impl IoSliceMut =====
1033+
1034+
/// A buffer type used for `readv`.
1035+
///
1036+
/// This is a wrapper around an `std::io::IoSliceMut`, but does not expose
1037+
/// the inner bytes in a safe API, as they may point at uninitialized memory.
1038+
///
1039+
/// This is `repr(transparent)` of the `std::io::IoSliceMut`, so it is valid to
1040+
/// transmute them. However, as the memory might be uninitialized, care must be
1041+
/// taken to not *read* the internal bytes, only *write* to them.
1042+
#[repr(transparent)]
1043+
#[cfg(feature = "std")]
1044+
pub struct IoSliceMut<'a>(std::io::IoSliceMut<'a>);
1045+
1046+
#[cfg(feature = "std")]
1047+
impl fmt::Debug for IoSliceMut<'_> {
1048+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1049+
f.debug_struct("IoSliceMut")
1050+
.field("len", &self.0.len())
1051+
.finish()
1052+
}
1053+
}
1054+
1055+
#[cfg(feature = "std")]
1056+
impl<'a> From<&'a mut [u8]> for IoSliceMut<'a> {
1057+
fn from(buf: &'a mut [u8]) -> IoSliceMut<'a> {
1058+
IoSliceMut(std::io::IoSliceMut::new(buf))
1059+
}
1060+
}
1061+
1062+
#[cfg(feature = "std")]
1063+
impl<'a> From<&'a mut [MaybeUninit<u8>]> for IoSliceMut<'a> {
1064+
fn from(buf: &'a mut [MaybeUninit<u8>]) -> IoSliceMut<'a> {
1065+
IoSliceMut(std::io::IoSliceMut::new(unsafe {
1066+
// We don't look at the contents, and `std::io::IoSliceMut`
1067+
// doesn't either.
1068+
mem::transmute::<&'a mut [MaybeUninit<u8>], &'a mut [u8]>(buf)
1069+
}))
1070+
}
1071+
}

src/buf/chain.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use crate::{Buf, BufMut};
22
use crate::buf::IntoIter;
33

4+
use core::mem::MaybeUninit;
5+
6+
#[cfg(feature = "std")]
7+
use std::io::{IoSlice};
48
#[cfg(feature = "std")]
5-
use std::io::{IoSlice, IoSliceMut};
9+
use crate::buf::IoSliceMut;
610

711
/// A `Chain` sequences two buffers.
812
///
@@ -196,7 +200,7 @@ impl<T, U> BufMut for Chain<T, U>
196200
self.a.remaining_mut() + self.b.remaining_mut()
197201
}
198202

199-
unsafe fn bytes_mut(&mut self) -> &mut [u8] {
203+
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
200204
if self.a.has_remaining_mut() {
201205
self.a.bytes_mut()
202206
} else {
@@ -223,7 +227,7 @@ impl<T, U> BufMut for Chain<T, U>
223227
}
224228

225229
#[cfg(feature = "std")]
226-
unsafe fn bytes_vectored_mut<'a>(&'a mut self, dst: &mut [IoSliceMut<'a>]) -> usize {
230+
fn bytes_vectored_mut<'a>(&'a mut self, dst: &mut [IoSliceMut<'a>]) -> usize {
227231
let mut n = self.a.bytes_vectored_mut(dst);
228232
n += self.b.bytes_vectored_mut(&mut dst[n..]);
229233
n

src/buf/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ mod writer;
3131

3232
pub use self::buf_impl::Buf;
3333
pub use self::buf_mut::BufMut;
34+
#[cfg(feature = "std")]
35+
pub use self::buf_mut::IoSliceMut;
3436
pub use self::chain::Chain;
3537
pub use self::iter::IntoIter;
3638
#[cfg(feature = "std")]

src/bytes_mut.rs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -926,19 +926,9 @@ impl BufMut for BytesMut {
926926
}
927927

928928
#[inline]
929-
unsafe fn bytes_mut(&mut self) -> &mut [u8] {
930-
slice::from_raw_parts_mut(self.ptr.as_ptr().offset(self.len as isize), self.cap)
931-
}
932-
933-
#[inline]
934-
fn put_slice(&mut self, src: &[u8]) {
935-
assert!(self.remaining_mut() >= src.len());
936-
937-
let len = src.len();
938-
929+
fn bytes_mut(&mut self) -> &mut [mem::MaybeUninit<u8>] {
939930
unsafe {
940-
self.bytes_mut()[..len].copy_from_slice(src);
941-
self.advance_mut(len);
931+
slice::from_raw_parts_mut(self.ptr.as_ptr().offset(self.len as isize) as *mut mem::MaybeUninit<u8>, self.cap)
942932
}
943933
}
944934
}
@@ -1085,11 +1075,12 @@ impl Extend<u8> for BytesMut {
10851075
let (lower, _) = iter.size_hint();
10861076
self.reserve(lower);
10871077

1078+
// TODO: optimize
1079+
// 1. If self.kind() == KIND_VEC, use Vec::extend
1080+
// 2. Make `reserve` inline-able
10881081
for b in iter {
1089-
unsafe {
1090-
self.bytes_mut()[0] = b;
1091-
self.advance_mut(1);
1092-
}
1082+
self.reserve(1);
1083+
self.put_u8(b);
10931084
}
10941085
}
10951086
}

src/either.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use either::Either;
44
use either::Either::*;
55

66
#[cfg(feature = "std")]
7-
use std::io::{IoSlice, IoSliceMut};
7+
use std::io::IoSlice;
8+
#[cfg(feature = "std")]
9+
use crate::buf::IoSliceMut;
810

911
impl<L, R> Buf for Either<L, R>
1012
where
@@ -60,15 +62,15 @@ where
6062
}
6163
}
6264

63-
unsafe fn bytes_mut(&mut self) -> &mut [u8] {
65+
fn bytes_mut(&mut self) -> &mut [core::mem::MaybeUninit<u8>] {
6466
match *self {
6567
Left(ref mut b) => b.bytes_mut(),
6668
Right(ref mut b) => b.bytes_mut(),
6769
}
6870
}
6971

7072
#[cfg(feature = "std")]
71-
unsafe fn bytes_vectored_mut<'a>(&'a mut self, dst: &mut [IoSliceMut<'a>]) -> usize {
73+
fn bytes_vectored_mut<'a>(&'a mut self, dst: &mut [IoSliceMut<'a>]) -> usize {
7274
match *self {
7375
Left(ref mut b) => b.bytes_vectored_mut(dst),
7476
Right(ref mut b) => b.bytes_vectored_mut(dst),

tests/test_buf_mut.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
#![deny(warnings, rust_2018_idioms)]
22

3-
use bytes::{BufMut, BytesMut};
3+
use bytes::{buf::IoSliceMut, BufMut, BytesMut};
44
use std::usize;
55
use std::fmt::Write;
6-
use std::io::IoSliceMut;
76

87
#[test]
98
fn test_vec_as_mut_buf() {
109
let mut buf = Vec::with_capacity(64);
1110

1211
assert_eq!(buf.remaining_mut(), usize::MAX);
1312

14-
unsafe {
15-
assert!(buf.bytes_mut().len() >= 64);
16-
}
13+
assert!(buf.bytes_mut().len() >= 64);
1714

1815
buf.put(&b"zomg"[..]);
1916

@@ -72,20 +69,16 @@ fn test_clone() {
7269
fn test_bufs_vec_mut() {
7370
let b1: &mut [u8] = &mut [];
7471
let b2: &mut [u8] = &mut [];
75-
let mut dst = [IoSliceMut::new(b1), IoSliceMut::new(b2)];
72+
let mut dst = [IoSliceMut::from(b1), IoSliceMut::from(b2)];
7673

7774
// with no capacity
7875
let mut buf = BytesMut::new();
7976
assert_eq!(buf.capacity(), 0);
80-
unsafe {
81-
assert_eq!(0, buf.bytes_vectored_mut(&mut dst[..]));
82-
}
77+
assert_eq!(0, buf.bytes_vectored_mut(&mut dst[..]));
8378

8479
// with capacity
8580
let mut buf = BytesMut::with_capacity(64);
86-
unsafe {
87-
assert_eq!(1, buf.bytes_vectored_mut(&mut dst[..]));
88-
}
81+
assert_eq!(1, buf.bytes_vectored_mut(&mut dst[..]));
8982
}
9083

9184
#[test]

tests/test_bytes.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,16 @@ fn extend_from_slice_mut() {
464464
}
465465
}
466466

467+
#[test]
468+
fn extend_mut_without_size_hint() {
469+
let mut bytes = BytesMut::with_capacity(0);
470+
let mut long_iter = LONG.iter();
471+
472+
// Use iter::from_fn since it doesn't know a size_hint
473+
bytes.extend(std::iter::from_fn(|| long_iter.next()));
474+
assert_eq!(*bytes, LONG[..]);
475+
}
476+
467477
#[test]
468478
fn from_static() {
469479
let mut a = Bytes::from_static(b"ab");

0 commit comments

Comments
 (0)