mirror of
https://github.com/tokio-rs/bytes.git
synced 2026-08-27 00:00:17 +02:00
Support static refs and inline short byte slices
This commit is contained in:
+265
-67
@@ -1,7 +1,7 @@
|
|||||||
use {IntoBuf, ByteBuf, SliceBuf};
|
use {IntoBuf, ByteBuf, SliceBuf};
|
||||||
|
|
||||||
use std::{cmp, fmt, mem, ops, slice};
|
use std::{cmp, fmt, mem, ops, slice, ptr};
|
||||||
use std::cell::UnsafeCell;
|
use std::cell::{Cell, UnsafeCell};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// A reference counted slice of bytes.
|
/// A reference counted slice of bytes.
|
||||||
@@ -22,7 +22,16 @@ pub struct BytesMut {
|
|||||||
inner: Inner
|
inner: Inner
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Inner {
|
struct Inner {
|
||||||
|
data: Data,
|
||||||
|
|
||||||
|
// If this pointer is set, then the the BytesMut is backed by an Arc
|
||||||
|
arc: Cell<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Eq, PartialEq, Clone, Copy)]
|
||||||
|
struct Data {
|
||||||
// Pointer to the start of the memory owned by this BytesMut
|
// Pointer to the start of the memory owned by this BytesMut
|
||||||
ptr: *mut u8,
|
ptr: *mut u8,
|
||||||
|
|
||||||
@@ -31,11 +40,29 @@ pub struct Inner {
|
|||||||
|
|
||||||
// Total number of bytes owned by this BytesMut
|
// Total number of bytes owned by this BytesMut
|
||||||
cap: usize,
|
cap: usize,
|
||||||
|
|
||||||
// If this pointer is set, then the the BytesMut is backed by an Arc
|
|
||||||
arc: UnsafeCell<Option<Arc<Vec<u8>>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
enum Kind {
|
||||||
|
Vec,
|
||||||
|
Arc,
|
||||||
|
Inline,
|
||||||
|
Static,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Shared = Arc<UnsafeCell<Vec<u8>>>;
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
const INLINE_CAP: usize = 8 * 3;
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
const INNER_CAP: usize = 4 * 3;
|
||||||
|
|
||||||
|
const KIND_MASK: usize = 3;
|
||||||
|
const KIND_INLINE: usize = 1;
|
||||||
|
const KIND_STATIC: usize = 2;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* ===== Bytes =====
|
* ===== Bytes =====
|
||||||
@@ -46,13 +73,14 @@ impl Bytes {
|
|||||||
/// Creates a new empty `Bytes`
|
/// Creates a new empty `Bytes`
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Bytes {
|
pub fn new() -> Bytes {
|
||||||
use std::ptr;
|
|
||||||
Bytes {
|
Bytes {
|
||||||
inner: Inner {
|
inner: Inner {
|
||||||
ptr: ptr::null_mut(),
|
data: Data {
|
||||||
len: 0,
|
ptr: ptr::null_mut(),
|
||||||
cap: 0,
|
len: 0,
|
||||||
arc: UnsafeCell::new(None),
|
cap: 0,
|
||||||
|
},
|
||||||
|
arc: Cell::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,6 +91,23 @@ impl Bytes {
|
|||||||
BytesMut::from_slice(bytes).freeze()
|
BytesMut::from_slice(bytes).freeze()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new `Bytes` from a static slice.
|
||||||
|
///
|
||||||
|
/// This is a zero copy function
|
||||||
|
#[inline]
|
||||||
|
pub fn from_static(bytes: &'static [u8]) -> Bytes {
|
||||||
|
Bytes {
|
||||||
|
inner: Inner {
|
||||||
|
data: Data {
|
||||||
|
ptr: bytes.as_ptr() as *mut u8,
|
||||||
|
len: bytes.len(),
|
||||||
|
cap: bytes.len(),
|
||||||
|
},
|
||||||
|
arc: Cell::new(KIND_STATIC),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the number of bytes contained in this `Bytes`.
|
/// Returns the number of bytes contained in this `Bytes`.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.inner.len()
|
self.inner.len()
|
||||||
@@ -233,14 +278,44 @@ impl BytesMut {
|
|||||||
/// Create a new `BytesMut` with the specified capacity.
|
/// Create a new `BytesMut` with the specified capacity.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_capacity(cap: usize) -> BytesMut {
|
pub fn with_capacity(cap: usize) -> BytesMut {
|
||||||
BytesMut::from(Vec::with_capacity(cap))
|
if cap <= INLINE_CAP {
|
||||||
|
BytesMut {
|
||||||
|
inner: Inner {
|
||||||
|
data: Data {
|
||||||
|
ptr: ptr::null_mut(),
|
||||||
|
len: 0,
|
||||||
|
cap: 0,
|
||||||
|
},
|
||||||
|
arc: Cell::new(KIND_INLINE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BytesMut::from(Vec::with_capacity(cap))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `BytesMut` and copy the given slice into it.
|
/// Creates a new `BytesMut` and copy the given slice into it.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_slice<T: AsRef<[u8]>>(bytes: T) -> BytesMut {
|
pub fn from_slice<T: AsRef<[u8]>>(bytes: T) -> BytesMut {
|
||||||
let buf = ByteBuf::from_slice(bytes);
|
let b = bytes.as_ref();
|
||||||
buf.into_inner()
|
|
||||||
|
if b.len() <= INLINE_CAP {
|
||||||
|
unsafe {
|
||||||
|
let len = b.len();
|
||||||
|
let mut data: [u8; INLINE_CAP] = mem::uninitialized();
|
||||||
|
data[0..len].copy_from_slice(b);
|
||||||
|
|
||||||
|
BytesMut {
|
||||||
|
inner: Inner {
|
||||||
|
data: mem::transmute(data),
|
||||||
|
arc: Cell::new(KIND_INLINE | (len << 2)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let buf = ByteBuf::from_slice(b);
|
||||||
|
buf.into_inner()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of bytes contained in this `BytesMut`.
|
/// Returns the number of bytes contained in this `BytesMut`.
|
||||||
@@ -341,28 +416,62 @@ impl BytesMut {
|
|||||||
impl Inner {
|
impl Inner {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn as_ref(&self) -> &[u8] {
|
fn as_ref(&self) -> &[u8] {
|
||||||
unsafe { slice::from_raw_parts(self.ptr, self.len) }
|
if self.is_inline() {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(&self.data as *const _ as *const u8, self.inline_len())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe { slice::from_raw_parts(self.data.ptr, self.data.len) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn as_mut(&mut self) -> &mut [u8] {
|
fn as_mut(&mut self) -> &mut [u8] {
|
||||||
unsafe { slice::from_raw_parts_mut(self.ptr, self.len) }
|
debug_assert!(self.kind() != Kind::Static);
|
||||||
|
|
||||||
|
if self.is_inline() {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(&mut self.data as *mut _ as *mut u8, self.inline_len())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self.data.ptr, self.data.len) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn as_raw(&mut self) -> &mut [u8] {
|
unsafe fn as_raw(&mut self) -> &mut [u8] {
|
||||||
slice::from_raw_parts_mut(self.ptr, self.cap)
|
debug_assert!(self.kind() != Kind::Static);
|
||||||
|
|
||||||
|
if self.is_inline() {
|
||||||
|
slice::from_raw_parts_mut(&mut self.data as *mut _ as *mut u8, INLINE_CAP)
|
||||||
|
} else {
|
||||||
|
slice::from_raw_parts_mut(self.data.ptr, self.data.cap)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.len
|
if self.is_inline() {
|
||||||
|
self.inline_len()
|
||||||
|
} else {
|
||||||
|
self.data.len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn inline_len(&self) -> usize {
|
||||||
|
self.arc.get() >> 2
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn set_len(&mut self, len: usize) {
|
unsafe fn set_len(&mut self, len: usize) {
|
||||||
assert!(len <= self.cap);
|
if self.is_inline() {
|
||||||
self.len = len;
|
assert!(len <= INLINE_CAP);
|
||||||
|
self.arc.set(len << 2 | KIND_INLINE);
|
||||||
|
} else {
|
||||||
|
assert!(len <= self.data.cap);
|
||||||
|
self.data.len = len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -372,7 +481,11 @@ impl Inner {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn capacity(&self) -> usize {
|
pub fn capacity(&self) -> usize {
|
||||||
self.cap
|
if self.is_inline() {
|
||||||
|
INLINE_CAP
|
||||||
|
} else {
|
||||||
|
self.data.cap
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_off(&mut self, at: usize) -> Inner {
|
fn split_off(&mut self, at: usize) -> Inner {
|
||||||
@@ -404,20 +517,46 @@ impl Inner {
|
|||||||
/// This method will panic if `start` is out of bounds for the underlying
|
/// This method will panic if `start` is out of bounds for the underlying
|
||||||
/// slice.
|
/// slice.
|
||||||
unsafe fn set_start(&mut self, start: usize) {
|
unsafe fn set_start(&mut self, start: usize) {
|
||||||
assert!(start <= self.cap);
|
|
||||||
debug_assert!(self.is_shared());
|
debug_assert!(self.is_shared());
|
||||||
debug_assert!(self.len <= self.cap);
|
|
||||||
|
|
||||||
self.ptr = self.ptr.offset(start as isize);
|
if self.is_inline() {
|
||||||
|
if start == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: This could probably be optimized with some bit fiddling
|
let len = self.inline_len();
|
||||||
if self.len >= start {
|
|
||||||
self.len -= start;
|
if len <= start {
|
||||||
|
assert!(start <= INLINE_CAP);
|
||||||
|
|
||||||
|
// Set the length to zero
|
||||||
|
self.arc.set(KIND_INLINE);
|
||||||
|
} else {
|
||||||
|
debug_assert!(start <= INLINE_CAP);
|
||||||
|
|
||||||
|
let new_len = len - start;
|
||||||
|
|
||||||
|
let dst = &self.data as *const Data as *mut Data as *mut u8;
|
||||||
|
let src = (dst as *const u8).offset(start as isize);
|
||||||
|
|
||||||
|
ptr::copy(src, dst, new_len);
|
||||||
|
|
||||||
|
self.arc.set((new_len << 2) | KIND_INLINE);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.len = 0;
|
assert!(start <= self.data.cap);
|
||||||
}
|
|
||||||
|
|
||||||
self.cap -= start;
|
self.data.ptr = self.data.ptr.offset(start as isize);
|
||||||
|
|
||||||
|
// TODO: This could probably be optimized with some bit fiddling
|
||||||
|
if self.data.len >= start {
|
||||||
|
self.data.len -= start;
|
||||||
|
} else {
|
||||||
|
self.data.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.data.cap -= start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the end index of this window to the index specified.
|
/// Changes the end index of this window to the index specified.
|
||||||
@@ -427,21 +566,33 @@ impl Inner {
|
|||||||
/// This method will panic if `start` is out of bounds for the underlying
|
/// This method will panic if `start` is out of bounds for the underlying
|
||||||
/// slice.
|
/// slice.
|
||||||
unsafe fn set_end(&mut self, end: usize) {
|
unsafe fn set_end(&mut self, end: usize) {
|
||||||
assert!(end <= self.cap);
|
|
||||||
debug_assert!(self.is_shared());
|
debug_assert!(self.is_shared());
|
||||||
|
|
||||||
self.cap = end;
|
if self.is_inline() {
|
||||||
self.len = cmp::min(self.len, end);
|
assert!(end <= INLINE_CAP);
|
||||||
|
let new_len = cmp::min(self.inline_len(), end);
|
||||||
|
|
||||||
|
self.arc.set((new_len << 2) | KIND_INLINE);
|
||||||
|
} else {
|
||||||
|
assert!(end <= self.data.cap);
|
||||||
|
debug_assert!(self.is_shared());
|
||||||
|
|
||||||
|
self.data.cap = end;
|
||||||
|
self.data.len = cmp::min(self.data.len, end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if it is safe to mutate the memory
|
/// Checks if it is safe to mutate the memory
|
||||||
fn is_mut_safe(&mut self) -> bool {
|
fn is_mut_safe(&mut self) -> bool {
|
||||||
unsafe {
|
match self.kind() {
|
||||||
(*self.arc.get()).as_mut()
|
Kind::Static => false,
|
||||||
// Check if there is only one outstanding reference to the memory
|
Kind::Arc => {
|
||||||
.map(|a| Arc::get_mut(a).is_some())
|
unsafe {
|
||||||
// If there is no arc, then this is a unique pointer
|
let arc: &mut Shared = mem::transmute(&mut self.arc);
|
||||||
.unwrap_or(true)
|
Arc::get_mut(arc).is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Kind::Vec | Kind::Inline => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,42 +600,90 @@ impl Inner {
|
|||||||
/// it can be done safely. As such, this fn is not public, instead other
|
/// it can be done safely. As such, this fn is not public, instead other
|
||||||
/// fns will use this one while maintaining the guarantees.
|
/// fns will use this one while maintaining the guarantees.
|
||||||
fn shallow_clone(&self) -> Inner {
|
fn shallow_clone(&self) -> Inner {
|
||||||
let arc = unsafe {
|
match self.kind() {
|
||||||
match *self.arc.get() {
|
Kind::Vec => {
|
||||||
Some(ref arc) => {
|
unsafe {
|
||||||
// Already backed by an arc, just clone it
|
|
||||||
arc.clone()
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// Promote this `Bytes` to an arc, and clone it
|
// Promote this `Bytes` to an arc, and clone it
|
||||||
let v = Vec::from_raw_parts(self.ptr, self.len, self.cap);
|
let v = Vec::from_raw_parts(
|
||||||
|
self.data.ptr,
|
||||||
|
self.data.len,
|
||||||
|
self.data.cap);
|
||||||
|
|
||||||
let a = Arc::new(v);
|
let a = Arc::new(v);
|
||||||
|
self.arc.set(mem::transmute(a.clone()));
|
||||||
|
|
||||||
*self.arc.get() = Some(a.clone());
|
Inner {
|
||||||
|
data: self.data,
|
||||||
a
|
arc: Cell::new(mem::transmute(a)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
Kind::Arc => {
|
||||||
|
unsafe {
|
||||||
|
let arc: &Shared = mem::transmute(&self.arc);
|
||||||
|
|
||||||
Inner {
|
Inner {
|
||||||
arc: UnsafeCell::new(Some(arc)),
|
data: self.data,
|
||||||
.. *self
|
arc: Cell::new(mem::transmute(arc.clone())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Kind::Inline | Kind::Static => {
|
||||||
|
Inner {
|
||||||
|
data: self.data,
|
||||||
|
arc: Cell::new(self.arc.get()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn kind(&self) -> Kind {
|
||||||
|
let arc = self.arc.get();
|
||||||
|
|
||||||
|
if arc == 0 {
|
||||||
|
return Kind::Vec
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind = arc & KIND_MASK;
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
0 => Kind::Arc,
|
||||||
|
KIND_INLINE => Kind::Inline,
|
||||||
|
KIND_STATIC => Kind::Static,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_inline(&self) -> bool {
|
||||||
|
self.arc.get() & KIND_MASK == KIND_INLINE
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn is_shared(&self) -> bool {
|
fn is_shared(&self) -> bool {
|
||||||
unsafe { (*self.arc.get()).is_some() }
|
self.kind() != Kind::Vec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Inner {
|
impl Drop for Inner {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if !self.is_shared() {
|
match self.kind() {
|
||||||
unsafe {
|
Kind::Vec => {
|
||||||
// Not shared, manually free
|
unsafe {
|
||||||
let _ = Vec::from_raw_parts(self.ptr, self.len, self.cap);
|
// Not shared, manually free
|
||||||
|
let _ = Vec::from_raw_parts(
|
||||||
|
self.data.ptr,
|
||||||
|
self.data.len,
|
||||||
|
self.data.cap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Kind::Arc => {
|
||||||
|
unsafe {
|
||||||
|
let _: Arc<UnsafeCell<Vec<u8>>> = mem::transmute(self.arc.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -537,10 +736,12 @@ impl From<Vec<u8>> for BytesMut {
|
|||||||
|
|
||||||
BytesMut {
|
BytesMut {
|
||||||
inner: Inner {
|
inner: Inner {
|
||||||
ptr: ptr,
|
data: Data {
|
||||||
len: len,
|
ptr: ptr,
|
||||||
cap: cap,
|
len: len,
|
||||||
arc: UnsafeCell::new(None),
|
cap: cap,
|
||||||
|
},
|
||||||
|
arc: Cell::new(0),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -651,9 +852,6 @@ impl<'a, T: ?Sized> PartialEq<&'a T> for Bytes
|
|||||||
|
|
||||||
impl Clone for BytesMut {
|
impl Clone for BytesMut {
|
||||||
fn clone(&self) -> BytesMut {
|
fn clone(&self) -> BytesMut {
|
||||||
let mut v = Vec::with_capacity(self.len());
|
BytesMut::from_slice(self.as_ref())
|
||||||
v.extend_from_slice(&self[..]);
|
|
||||||
|
|
||||||
BytesMut::from(v)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -81,14 +81,14 @@ fn slice() {
|
|||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn slice_oob_1() {
|
fn slice_oob_1() {
|
||||||
let a = Bytes::from_slice(b"hello world");
|
let a = Bytes::from_slice(b"hello world");
|
||||||
a.slice(5, 20);
|
a.slice(5, 25);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn slice_oob_2() {
|
fn slice_oob_2() {
|
||||||
let a = Bytes::from_slice(b"hello world");
|
let a = Bytes::from_slice(b"hello world");
|
||||||
a.slice(15, 20);
|
a.slice(25, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -110,14 +110,14 @@ fn split_off() {
|
|||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn split_off_oob() {
|
fn split_off_oob() {
|
||||||
let mut hello = Bytes::from_slice(b"helloworld");
|
let mut hello = Bytes::from_slice(b"helloworld");
|
||||||
hello.split_off(11);
|
hello.split_off(25);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn split_off_oob_mut() {
|
fn split_off_oob_mut() {
|
||||||
let mut hello = BytesMut::from_slice(b"helloworld");
|
let mut hello = BytesMut::from_slice(b"helloworld");
|
||||||
hello.split_off(11);
|
hello.split_off(25);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -151,14 +151,14 @@ fn drain_to() {
|
|||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn drain_to_oob() {
|
fn drain_to_oob() {
|
||||||
let mut hello = Bytes::from_slice(b"helloworld");
|
let mut hello = Bytes::from_slice(b"helloworld");
|
||||||
hello.drain_to(11);
|
hello.drain_to(30);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn drain_to_oob_mut() {
|
fn drain_to_oob_mut() {
|
||||||
let mut hello = BytesMut::from_slice(b"helloworld");
|
let mut hello = BytesMut::from_slice(b"helloworld");
|
||||||
hello.drain_to(11);
|
hello.drain_to(30);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user