/// A cheaply cloneable and sliceable chunk of contiguous memory. /// /// `Bytes` is an efficient container for storing and operating on contiguous /// slices of memory. It is intended for use primarily in networking code, but /// could have applications elsewhere as well. /// /// `Bytes` values facilitate zero-copy network programming by allowing multiple /// `Bytes` objects to point to the same underlying memory. /// /// `Bytes` does not have a single implementation. It is an interface, whose /// exact behavior is implemented through dynamic dispatch in several underlying /// implementations of `Bytes`. /// /// All `Bytes` implementations must fulfill the following requirements: /// - They are cheaply cloneable and thereby shareable between an unlimited amount /// of components, for example by modifying a reference count. /// - Instances can be sliced to refer to a subset of the original buffer. /// /// ``` /// use bytes::Bytes; /// /// let mut mem = Bytes::from("Hello world"); /// let a = mem.slice(0..5); /// /// assert_eq!(a, "Hello"); /// /// let b = mem.split_to(6); /// /// assert_eq!(mem, "world"); /// assert_eq!(b, "Hello "); /// ``` /// /// # Memory layout /// /// The `Bytes` struct itself is fairly small, limited to 4 `usize` fields used /// to track information about which segment of the underlying memory the /// `Bytes` handle has access to. /// /// `Bytes` keeps both a pointer to the shared state containing the full memory /// slice and a pointer to the start of the region visible by the handle. /// `Bytes` also tracks the length of its view into the memory. /// /// # Sharing /// /// `Bytes` contains a vtable, which allows implementations of `Bytes` to define /// how sharing/cloning is implemented in detail. /// When `Bytes::clone()` is called, `Bytes` will call the vtable function for /// cloning the backing storage in order to share it behind multiple `Bytes` /// instances. /// /// For `Bytes` implementations which refer to constant memory (e.g. created /// via `Bytes::from_static()`) the cloning implementation will be a no-op. /// /// For `Bytes` implementations which point to a reference counted shared storage /// (e.g. an `Arc<[u8]>`), sharing will be implemented by increasing the /// reference count. /// /// Due to this mechanism, multiple `Bytes` instances may point to the same /// shared memory region. /// Each `Bytes` instance can point to different sections within that /// memory region, and `Bytes` instances may or may not have overlapping views /// into the memory. /// /// The following diagram visualizes a scenario where 2 `Bytes` instances make /// use of an `Arc`-based backing storage, and provide access to different views: /// /// ```text /// /// Arc ptrs ┌─────────┐ /// ________________________ / │ Bytes 2 │ /// / └─────────┘ /// / ┌───────────┐ | | /// |_________/ │ Bytes 1 │ | | /// | └───────────┘ | | /// | | | ___/ data | tail /// | data | tail |/ | /// v v v v /// ┌─────┬─────┬───────────┬───────────────┬─────┐ /// │ Arc │ │ │ │ │ /// └─────┴─────┴───────────┴───────────────┴─────┘ /// ``` pubstruct Bytes {
ptr: *const u8,
len: usize, // inlined "trait object"
data: AtomicPtr<()>,
vtable: &'static Vtable,
}
impl Bytes { /// Creates a new empty `Bytes`. /// /// This will not allocate and the returned `Bytes` handle will be empty. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let b = Bytes::new(); /// assert_eq!(&b[..], b""); /// ``` #[inline] #[cfg(not(all(loom, test)))] pubconstfn new() -> Self { // Make it a named const to work around // "unsizing casts are not allowed in const fn" const EMPTY: &[u8] = &[];
Bytes::from_static(EMPTY)
}
/// Creates a new empty `Bytes`. #[cfg(all(loom, test))] pubfn new() -> Self { const EMPTY: &[u8] = &[];
Bytes::from_static(EMPTY)
}
/// Creates a new `Bytes` from a static slice. /// /// The returned `Bytes` will point directly to the static slice. There is /// no allocating or copying. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let b = Bytes::from_static(b"hello"); /// assert_eq!(&b[..], b"hello"); /// ``` #[inline] #[cfg(not(all(loom, test)))] pubconstfn from_static(bytes: &'static [u8]) -> Self {
Bytes {
ptr: bytes.as_ptr(),
len: bytes.len(),
data: AtomicPtr::new(ptr::null_mut()),
vtable: &STATIC_VTABLE,
}
}
/// Creates a new `Bytes` from a static slice. #[cfg(all(loom, test))] pubfn from_static(bytes: &'static [u8]) -> Self {
Bytes {
ptr: bytes.as_ptr(),
len: bytes.len(),
data: AtomicPtr::new(ptr::null_mut()),
vtable: &STATIC_VTABLE,
}
}
/// Creates a new `Bytes` with length zero and the given pointer as the address. fn new_empty_with_ptr(ptr: *const u8) -> Self {
debug_assert!(!ptr.is_null());
// Detach this pointer's provenance from whichever allocation it came from, and reattach it // to the provenance of the fake ZST [u8;0] at the same address. let ptr = without_provenance(ptr as usize);
/// Create [Bytes] with a buffer whose lifetime is controlled /// via an explicit owner. /// /// A common use case is to zero-copy construct from mapped memory. /// /// ``` /// # struct File; /// # /// # impl File { /// # pub fn open(_: &str) -> Result<Self, ()> { /// # Ok(Self) /// # } /// # } /// # /// # mod memmap2 { /// # pub struct Mmap; /// # /// # impl Mmap { /// # pub unsafe fn map(_file: &super::File) -> Result<Self, ()> { /// # Ok(Self) /// # } /// # } /// # /// # impl AsRef<[u8]> for Mmap { /// # fn as_ref(&self) -> &[u8] { /// # b"buf" /// # } /// # } /// # } /// use bytes::Bytes; /// use memmap2::Mmap; /// /// # fn main() -> Result<(), ()> { /// let file = File::open("upload_bundle.tar.gz")?; /// let mmap = unsafe { Mmap::map(&file) }?; /// let b = Bytes::from_owner(mmap); /// # Ok(()) /// # } /// ``` /// /// The `owner` will be transferred to the constructed [Bytes] object, which /// will ensure it is dropped once all remaining clones of the constructed /// object are dropped. The owner will then be responsible for dropping the /// specified region of memory as part of its [Drop] implementation. /// /// Note that converting [Bytes] constructed from an owner into a [BytesMut] /// will always create a deep copy of the buffer into newly allocated memory. pubfn from_owner<T>(owner: T) -> Self where
T: AsRef<[u8]> + Send + 'static,
{ // Safety & Miri: // The ownership of `owner` is first transferred to the `Owned` wrapper and `Bytes` object. // This ensures that the owner is pinned in memory, allowing us to call `.as_ref()` safely // since the lifetime of the owner is controlled by the lifetime of the new `Bytes` object, // and the lifetime of the resulting borrowed `&[u8]` matches that of the owner. // Note that this remains safe so long as we only call `.as_ref()` once. // // There are some additional special considerations here: // * We rely on Bytes's Drop impl to clean up memory should `.as_ref()` panic. // * Setting the `ptr` and `len` on the bytes object last (after moving the owner to // Bytes) allows Miri checks to pass since it avoids obtaining the `&[u8]` slice // from a stack-owned Box. // More details on this: https://github.com/tokio-rs/bytes/pull/742/#discussion_r1813375863 // and: https://github.com/tokio-rs/bytes/pull/742/#discussion_r1813316032
let owned = Box::into_raw(Box::new(Owned {
ref_cnt: AtomicUsize::new(1),
owner,
}));
/// Returns the number of bytes contained in this `Bytes`. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let b = Bytes::from(&b"hello"[..]); /// assert_eq!(b.len(), 5); /// ``` #[inline] pubconstfn len(&self) -> usize { self.len
}
/// Returns true if the `Bytes` has a length of 0. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let b = Bytes::new(); /// assert!(b.is_empty()); /// ``` #[inline] pubconstfn is_empty(&self) -> bool { self.len == 0
}
/// Returns true if this is the only reference to the data and /// `Into<BytesMut>` would avoid cloning the underlying buffer. /// /// Always returns false if the data is backed by a [static slice](Bytes::from_static), /// or an [owner](Bytes::from_owner). /// /// The result of this method may be invalidated immediately if another /// thread clones this value while this is being called. Ensure you have /// unique access to this value (`&mut Bytes`) first if you need to be /// certain the result is valid (i.e. for safety reasons). /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let a = Bytes::from(vec![1, 2, 3]); /// assert!(a.is_unique()); /// let b = a.clone(); /// assert!(!a.is_unique()); /// ``` pubfn is_unique(&self) -> bool { unsafe { (self.vtable.is_unique)(&self.data) }
}
/// Creates `Bytes` instance from slice, by copying it. pubfn copy_from_slice(data: &[u8]) -> Self {
data.to_vec().into()
}
/// Returns a slice of self for the provided range. /// /// This will increment the reference count for the underlying memory and /// return a new `Bytes` handle set to the slice. /// /// This operation is `O(1)`. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let a = Bytes::from(&b"hello world"[..]); /// let b = a.slice(2..5); /// /// assert_eq!(&b[..], b"llo"); /// ``` /// /// # Panics /// /// Requires that `begin <= end` and `end <= self.len()`, otherwise slicing /// will panic. pubfn slice(&self, range: impl RangeBounds<usize>) -> Self { use core::ops::Bound;
let len = self.len();
let begin = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
Bound::Excluded(&n) => n,
Bound::Unbounded => len,
};
assert!(
begin <= end, "range start must not be greater than end: {:?} <= {:?}",
begin,
end,
);
assert!(
end <= len, "range end out of bounds: {:?} <= {:?}",
end,
len,
);
if end == begin { return Bytes::new_empty_with_ptr(self.ptr.wrapping_add(begin));
}
/// Returns a slice of self that is equivalent to the given `subset`. /// /// When processing a `Bytes` buffer with other tools, one often gets a /// `&[u8]` which is in fact a slice of the `Bytes`, i.e. a subset of it. /// This function turns that `&[u8]` into another `Bytes`, as if one had /// called `self.slice()` with the offsets that correspond to `subset`. /// /// This operation is `O(1)`. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let bytes = Bytes::from(&b"012345678"[..]); /// let as_slice = bytes.as_ref(); /// let subset = &as_slice[2..6]; /// let subslice = bytes.slice_ref(&subset); /// assert_eq!(&subslice[..], b"2345"); /// ``` /// /// # Panics /// /// Requires that the given `sub` slice is in fact contained within the /// `Bytes` buffer; otherwise this function will panic. pubfn slice_ref(&self, subset: &[u8]) -> Self { // Empty slice and empty Bytes may have their pointers reset // so explicitly allow empty slice to be a subslice of any slice. if subset.is_empty() { return Bytes::new();
}
let bytes_p = self.as_ptr() as usize; let bytes_len = self.len();
let sub_p = subset.as_ptr() as usize; let sub_len = subset.len();
assert!(
sub_p >= bytes_p, "subset pointer ({:p}) is smaller than self pointer ({:p})",
subset.as_ptr(), self.as_ptr(),
);
assert!(
sub_p + sub_len <= bytes_p + bytes_len, "subset is out of bounds: self = ({:p}, {}), subset = ({:p}, {})", self.as_ptr(),
bytes_len,
subset.as_ptr(),
sub_len,
);
let sub_offset = sub_p - bytes_p;
self.slice(sub_offset..(sub_offset + sub_len))
}
/// Splits the bytes into two at the given index. /// /// Afterwards `self` contains elements `[0, at)`, and the returned `Bytes` /// contains elements `[at, len)`. It's guaranteed that the memory does not /// move, that is, the address of `self` does not change, and the address of /// the returned slice is `at` bytes after that. /// /// This is an `O(1)` operation that just increases the reference count and /// sets a few indices. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let mut a = Bytes::from(&b"hello world"[..]); /// let b = a.split_off(5); /// /// assert_eq!(&a[..], b"hello"); /// assert_eq!(&b[..], b" world"); /// ``` /// /// # Panics /// /// Panics if `at > len`. #[must_use = "consider Bytes::truncate if you don't need the other half"] pubfn split_off(&mutself, at: usize) -> Self { if at == self.len() { return Bytes::new_empty_with_ptr(self.ptr.wrapping_add(at));
}
if at == 0 { return mem::replace(self, Bytes::new_empty_with_ptr(self.ptr));
}
assert!(
at <= self.len(), "split_off out of bounds: {:?} <= {:?}",
at, self.len(),
);
letmut ret = self.clone();
self.len = at;
unsafe { ret.inc_start(at) };
ret
}
/// Splits the bytes into two at the given index. /// /// Afterwards `self` contains elements `[at, len)`, and the returned /// `Bytes` contains elements `[0, at)`. /// /// This is an `O(1)` operation that just increases the reference count and /// sets a few indices. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let mut a = Bytes::from(&b"hello world"[..]); /// let b = a.split_to(5); /// /// assert_eq!(&a[..], b" world"); /// assert_eq!(&b[..], b"hello"); /// ``` /// /// # Panics /// /// Panics if `at > len`. #[must_use = "consider Bytes::advance if you don't need the other half"] pubfn split_to(&mutself, at: usize) -> Self { if at == self.len() { let end_ptr = self.ptr.wrapping_add(at); return mem::replace(self, Bytes::new_empty_with_ptr(end_ptr));
}
if at == 0 { return Bytes::new_empty_with_ptr(self.ptr);
}
assert!(
at <= self.len(), "split_to out of bounds: {:?} <= {:?}",
at, self.len(),
);
letmut ret = self.clone();
unsafe { self.inc_start(at) };
ret.len = at;
ret
}
/// Shortens the buffer, keeping the first `len` bytes and dropping the /// rest. /// /// If `len` is greater than the buffer's current length, this has no /// effect. /// /// The [split_off](`Self::split_off()`) method can emulate `truncate`, but this causes the /// excess bytes to be returned instead of dropped. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let mut buf = Bytes::from(&b"hello world"[..]); /// buf.truncate(5); /// assert_eq!(buf, b"hello"[..]); /// ``` #[inline] pubfn truncate(&mutself, len: usize) { if len < self.len { // The Vec "promotable" vtables do not store the capacity, // so we cannot truncate while using this repr. We *have* to // promote using `split_off` so the capacity can be stored. ifself.vtable as *const Vtable == &PROMOTABLE_EVEN_VTABLE
|| self.vtable as *const Vtable == &PROMOTABLE_ODD_VTABLE
{
drop(self.split_off(len));
} else { self.len = len;
}
}
}
/// Clears the buffer, removing all data. /// /// # Examples /// /// ``` /// use bytes::Bytes; /// /// let mut buf = Bytes::from(&b"hello world"[..]); /// buf.clear(); /// assert!(buf.is_empty()); /// ``` #[inline] pubfn clear(&mutself) { self.truncate(0);
}
/// Try to convert self into `BytesMut`. /// /// If `self` is unique for the entire original buffer, this will succeed /// and return a `BytesMut` with the contents of `self` without copying. /// If `self` is not unique for the entire original buffer, this will fail /// and return self. /// /// This will also always fail if the buffer was constructed via either /// [from_owner](Bytes::from_owner) or [from_static](Bytes::from_static). /// /// # Examples /// /// ``` /// use bytes::{Bytes, BytesMut}; /// /// let bytes = Bytes::from(b"hello".to_vec()); /// assert_eq!(bytes.try_into_mut(), Ok(BytesMut::from(&b"hello"[..]))); /// ``` pubfn try_into_mut(self) -> Result<BytesMut, Bytes> { ifself.is_unique() {
Ok(self.into())
} else {
Err(self)
}
}
impl From<Vec<u8>> for Bytes { fn from(vec: Vec<u8>) -> Bytes { letmut vec = ManuallyDrop::new(vec); let ptr = vec.as_mut_ptr(); let len = vec.len(); let cap = vec.capacity();
// Avoid an extra allocation if possible. if len == cap { let vec = ManuallyDrop::into_inner(vec); return Bytes::from(vec.into_boxed_slice());
}
let shared = Box::into_raw(shared); // The pointer should be aligned, so this assert should // always succeed.
debug_assert!( 0 == (shared as usize & KIND_MASK), "internal: Box<Shared> should have an aligned pointer",
);
Bytes {
ptr,
len,
data: AtomicPtr::new(shared as _),
vtable: &SHARED_VTABLE,
}
}
}
impl From<Box<[u8]>> for Bytes { fn from(slice: Box<[u8]>) -> Bytes { // Box<[u8]> doesn't contain a heap allocation for empty slices, // so the pointer isn't aligned enough for the KIND_VEC stashing to // work. if slice.is_empty() { return Bytes::new();
}
let len = slice.len(); let ptr = Box::into_raw(slice) as *mut u8;
impl From<Bytes> for BytesMut { /// Convert self into `BytesMut`. /// /// If `bytes` is unique for the entire original buffer, this will return a /// `BytesMut` with the contents of `bytes` without copying. /// If `bytes` is not unique for the entire original buffer, this will make /// a copy of `bytes` subset of the original buffer in a new `BytesMut`. /// /// # Examples /// /// ``` /// use bytes::{Bytes, BytesMut}; /// /// let bytes = Bytes::from(b"hello".to_vec()); /// assert_eq!(BytesMut::from(bytes), BytesMut::from(&b"hello"[..])); /// ``` fn from(bytes: Bytes) -> Self { let bytes = ManuallyDrop::new(bytes); unsafe { (bytes.vtable.into_mut)(&bytes.data, bytes.ptr, bytes.len) }
}
}
if kind == KIND_ARC {
shared_to_vec_impl(shared.cast(), ptr, len)
} else { // If Bytes holds a Vec, then the offset must be 0.
debug_assert_eq!(kind, KIND_VEC);
if kind == KIND_ARC {
shared_to_mut_impl(shared.cast(), ptr, len)
} else { // KIND_VEC is a view of an underlying buffer at a certain offset. // The ptr + len always represents the end of that buffer. // Before truncating it, it is first promoted to KIND_ARC. // Thus, we can safely reconstruct a Vec from it without leaking memory.
debug_assert_eq!(kind, KIND_VEC);
let buf = f(shared); let off = ptr.offset_from(buf) as usize; let cap = off + len; let v = Vec::from_raw_parts(buf, cap, cap);
letmut b = BytesMut::from_vec(v);
b.advance_unchecked(off);
b
}
}
unsafefn promotable_is_unique(data: &AtomicPtr<()>) -> bool { let shared = data.load(Ordering::Acquire); let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC { let ref_cnt = (*shared.cast::<Shared>()).ref_cnt.load(Ordering::Relaxed);
ref_cnt == 1
} else { true
}
}
unsafefn free_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) { let cap = offset.offset_from(buf) as usize + len;
dealloc(buf, Layout::from_size_align(cap, 1).unwrap())
}
// ===== impl SharedVtable =====
struct Shared { // Holds arguments to dealloc upon Drop, but otherwise doesn't use them
buf: *mut u8,
cap: usize,
ref_cnt: AtomicUsize,
}
impl Drop for Shared { fn drop(&mutself) { unsafe { dealloc(self.buf, Layout::from_size_align(self.cap, 1).unwrap()) }
}
}
// Assert that the alignment of `Shared` is divisible by 2. // This is a necessary invariant since we depend on allocating `Shared` a // shared object to implicitly carry the `KIND_ARC` flag in its pointer. // This flag is set when the LSB is 0. const _: [(); 0 - mem::align_of::<Shared>() % 2] = []; // Assert that the alignment of `Shared` is divisible by 2.
unsafefn shared_to_vec_impl(shared: *mut Shared, ptr: *const u8, len: usize) -> Vec<u8> { // Check that the ref_cnt is 1 (unique). // // If it is unique, then it is set to 0 with AcqRel fence for the same // reason in release_shared. // // Otherwise, we take the other branch and call release_shared. if (*shared)
.ref_cnt
.compare_exchange(1, 0, Ordering::AcqRel, Ordering::Relaxed)
.is_ok()
{ // Deallocate the `Shared` instance without running its destructor. let shared = *Box::from_raw(shared); let shared = ManuallyDrop::new(shared); let buf = shared.buf; let cap = shared.cap;
// Copy back buffer
ptr::copy(ptr, buf, len);
Vec::from_raw_parts(buf, len, cap)
} else { let v = slice::from_raw_parts(ptr, len).to_vec();
release_shared(shared);
v
}
}
unsafefn shared_to_mut_impl(shared: *mut Shared, ptr: *const u8, len: usize) -> BytesMut { // The goal is to check if the current handle is the only handle // that currently has access to the buffer. This is done by // checking if the `ref_cnt` is currently 1. // // The `Acquire` ordering synchronizes with the `Release` as // part of the `fetch_sub` in `release_shared`. The `fetch_sub` // operation guarantees that any mutations done in other threads // are ordered before the `ref_cnt` is decremented. As such, // this `Acquire` will guarantee that those mutations are // visible to the current thread. // // Otherwise, we take the other branch, copy the data and call `release_shared`. if (*shared).ref_cnt.load(Ordering::Acquire) == 1 { // Deallocate the `Shared` instance without running its destructor. let shared = *Box::from_raw(shared); let shared = ManuallyDrop::new(shared); let buf = shared.buf; let cap = shared.cap;
// Rebuild Vec let off = ptr.offset_from(buf) as usize; let v = Vec::from_raw_parts(buf, len + off, cap);
letmut b = BytesMut::from_vec(v);
b.advance_unchecked(off);
b
} else { // Copy the data from Shared in a new Vec, then release it let v = slice::from_raw_parts(ptr, len).to_vec();
release_shared(shared);
BytesMut::from_vec(v)
}
}
#[cold] unsafefn shallow_clone_vec(
atom: &AtomicPtr<()>,
ptr: *const (),
buf: *mut u8,
offset: *const u8,
len: usize,
) -> Bytes { // If the buffer is still tracked in a `Vec<u8>`. It is time to // promote the vec to an `Arc`. This could potentially be called // concurrently, so some care must be taken.
// First, allocate a new `Shared` instance containing the // `Vec` fields. It's important to note that `ptr`, `len`, // and `cap` cannot be mutated without having `&mut self`. // This means that these fields will not be concurrently // updated and since the buffer hasn't been promoted to an // `Arc`, those three fields still are the components of the // vector. let shared = Box::new(Shared {
buf,
cap: offset.offset_from(buf) as usize + len, // Initialize refcount to 2. One for this reference, and one // for the new clone that will be returned from // `shallow_clone`.
ref_cnt: AtomicUsize::new(2),
});
let shared = Box::into_raw(shared);
// The pointer should be aligned, so this assert should // always succeed.
debug_assert!( 0 == (shared as usize & KIND_MASK), "internal: Box<Shared> should have an aligned pointer",
);
// Try compare & swapping the pointer into the `arc` field. // `Release` is used synchronize with other threads that // will load the `arc` field. // // If the `compare_exchange` fails, then the thread lost the // race to promote the buffer to shared. The `Acquire` // ordering will synchronize with the `compare_exchange` // that happened in the other thread and the `Shared` // pointed to by `actual` will be visible. match atom.compare_exchange(ptr as _, shared as _, Ordering::AcqRel, Ordering::Acquire) {
Ok(actual) => {
debug_assert!(core::ptr::eq(actual, ptr)); // The upgrade was successful, the new handle can be // returned.
Bytes {
ptr: offset,
len,
data: AtomicPtr::new(shared as _),
vtable: &SHARED_VTABLE,
}
}
Err(actual) => { // The upgrade failed, a concurrent clone happened. Release // the allocation that was made in this thread, it will not // be needed. let shared = Box::from_raw(shared);
mem::forget(*shared);
// Buffer already promoted to shared storage, so increment ref // count.
shallow_clone_arc(actual as _, offset, len)
}
}
}
unsafefn release_shared(ptr: *mut Shared) { // `Shared` storage... follow the drop steps from Arc. if (*ptr).ref_cnt.fetch_sub(1, Ordering::Release) != 1 { return;
}
// This fence is needed to prevent reordering of use of the data and // deletion of the data. Because it is marked `Release`, the decreasing // of the reference count synchronizes with this `Acquire` fence. This // means that use of the data happens before decreasing the reference // count, which happens before this fence, which happens before the // deletion of the data. // // As explained in the [Boost documentation][1], // // > It is important to enforce any possible access to the object in one // > thread (through an existing reference) to *happen before* deleting // > the object in a different thread. This is achieved by a "release" // > operation after dropping a reference (any access to the object // > through this reference must obviously happened before), and an // > "acquire" operation before deleting the object. // // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) // // Thread sanitizer does not support atomic fences. Use an atomic load // instead.
(*ptr).ref_cnt.load(Ordering::Acquire);
// Drop the data
drop(Box::from_raw(ptr));
}
// Ideally we would always use this version of `ptr_map` since it is strict // provenance compatible, but it results in worse codegen. We will however still // use it on miri because it gives better diagnostics for people who test bytes // code with miri. // // See https://github.com/tokio-rs/bytes/pull/545 for more info. #[cfg(miri)] fn ptr_map<F>(ptr: *mut u8, f: F) -> *mut u8 where
F: FnOnce(usize) -> usize,
{ let old_addr = ptr as usize; let new_addr = f(old_addr); let diff = new_addr.wrapping_sub(old_addr);
ptr.wrapping_add(diff)
}
#[cfg(not(miri))] fn ptr_map<F>(ptr: *mut u8, f: F) -> *mut u8 where
F: FnOnce(usize) -> usize,
{ let old_addr = ptr as usize; let new_addr = f(old_addr);
new_addr as *mut u8
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.