Skip to content

Commit 22defe0

Browse files
committed
feat: create Bytes from std::sync::Arc
...via infallible (`Bytes::from_arc_projection`) and fallible (`Bytes::try_from_arc_projection`) constructors. tokio-rs#359 (comment) tokio-rs#437 (comment)
1 parent 19d1427 commit 22defe0

File tree

2 files changed

+233
-14
lines changed

2 files changed

+233
-14
lines changed

src/bytes.rs

Lines changed: 134 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use core::mem::{self, ManuallyDrop};
33
use core::ops::{Deref, RangeBounds};
44
use core::ptr::NonNull;
55
use core::{cmp, fmt, hash, ptr, slice, usize};
6+
use std::sync::Arc;
67

78
use alloc::{
89
alloc::{dealloc, Layout},
@@ -289,6 +290,74 @@ impl Bytes {
289290
ret
290291
}
291292

293+
/// Creates a new `Bytes` from an [`Arc<T>`] owner and a function that
294+
/// returns the buffer given a reference to the contained `T`.
295+
///
296+
/// `T` must be [`Sized`] rather than a trait object or slice.
297+
///
298+
/// The returned `Bytes` can be cloned via `Arc` referencing counting,
299+
/// whereas `Bytes` created via conversion from [`Vec`] must perform a new
300+
/// allocation internally on first clone to hold the reference count.
301+
/// This optimization is most significant if many `Bytes` instances are
302+
/// created from the same `owner` and subsequently cloned.
303+
///
304+
/// ```
305+
/// # use std::sync::Arc;
306+
/// # use bytes::Bytes;
307+
/// struct Pieces(Vec<Vec<u8>>);
308+
/// let pieces = Arc::new(Pieces(vec![b"hello".to_vec(), b"world".to_vec()]));
309+
/// let bytes: Vec<Bytes> = (0..2).map(|i| {
310+
/// Bytes::from_arc_projection(pieces.clone(), |p| &p.0[i])
311+
/// }).collect();
312+
/// let bytes_cloned = bytes.clone();
313+
/// assert_eq!(bytes[0], b"hello"[..]);
314+
/// assert_eq!(bytes_cloned[1], b"world"[..]);
315+
/// ```
316+
///
317+
/// See also [`Bytes::try_from_arc_projection`] for a fallible version.
318+
pub fn from_arc_projection<T: Sync + 'static>(
319+
owner: Arc<T>,
320+
projection: impl FnOnce(&T) -> &[u8],
321+
) -> Self {
322+
let buf = projection(&*owner);
323+
Bytes {
324+
ptr: buf.as_ptr(),
325+
len: buf.len(),
326+
data: AtomicPtr::new(Arc::into_raw(owner) as *mut ()),
327+
vtable: arcproj_vtable::<T>(),
328+
}
329+
}
330+
331+
/// Tries to creates a new `Bytes` from an [`Arc`] owner and a function that
332+
/// returns the buffer given a reference to the contained `T` or fails.
333+
///
334+
/// This is similar to [`Bytes::from_arc_projection`] but fallible.
335+
///
336+
/// ```
337+
/// # use std::sync::Arc;
338+
/// # use bytes::Bytes;
339+
/// struct Pieces(Vec<Vec<u8>>);
340+
/// let pieces = Arc::new(Pieces(vec![b"hello".to_vec(), b"world".to_vec()]));
341+
/// let bytes: Vec<Result<Bytes, &str>> = (0..3).map(|i| {
342+
/// Bytes::try_from_arc_projection(pieces.clone(), |p| {
343+
/// p.0.get(i).map(|v| &**v).ok_or("out of bounds")
344+
/// })
345+
/// }).collect();
346+
/// assert_eq!(bytes, [Ok(b"hello"[..].into()), Ok(b"world"[..].into()), Err("out of bounds")]);
347+
/// ```
348+
pub fn try_from_arc_projection<T: Sync + 'static, E>(
349+
owner: Arc<T>,
350+
projection: impl FnOnce(&T) -> Result<&[u8], E>,
351+
) -> Result<Self, E> {
352+
let buf = projection(&*owner)?;
353+
Ok(Bytes {
354+
ptr: buf.as_ptr(),
355+
len: buf.len(),
356+
data: AtomicPtr::new(Arc::into_raw(owner) as *mut ()),
357+
vtable: arcproj_vtable::<T>(),
358+
})
359+
}
360+
292361
/// Returns the number of bytes contained in this `Bytes`.
293362
///
294363
/// # Examples
@@ -322,8 +391,9 @@ impl Bytes {
322391
/// Returns true if this is the only reference to the data and
323392
/// `Into<BytesMut>` would avoid cloning the underlying buffer.
324393
///
325-
/// Always returns false if the data is backed by a [static slice](Bytes::from_static),
326-
/// or an [owner](Bytes::from_owner).
394+
/// Always returns false if the data is backed by a
395+
/// [static slice](Bytes::from_static), [owner](Bytes::from_owner),
396+
/// or [Arc projection](Bytes::from_arc_projection).
327397
///
328398
/// The result of this method may be invalidated immediately if another
329399
/// thread clones this value while this is being called. Ensure you have
@@ -627,8 +697,9 @@ impl Bytes {
627697
/// If `self` is not unique for the entire original buffer, this will fail
628698
/// and return self.
629699
///
630-
/// This will also always fail if the buffer was constructed via either
631-
/// [from_owner](Bytes::from_owner) or [from_static](Bytes::from_static).
700+
/// Always fails if the data is backed by a
701+
/// [static slice](Bytes::from_static), [owner](Bytes::from_owner),
702+
/// or [Arc projection](Bytes::from_arc_projection).
632703
///
633704
/// # Examples
634705
///
@@ -1073,13 +1144,17 @@ impl fmt::Debug for Vtable {
10731144
}
10741145
}
10751146

1147+
fn never_unique(_: &AtomicPtr<()>) -> bool {
1148+
false
1149+
}
1150+
10761151
// ===== impl StaticVtable =====
10771152

10781153
const STATIC_VTABLE: Vtable = Vtable {
10791154
clone: static_clone,
10801155
to_vec: static_to_vec,
10811156
to_mut: static_to_mut,
1082-
is_unique: static_is_unique,
1157+
is_unique: never_unique,
10831158
drop: static_drop,
10841159
};
10851160

@@ -1098,10 +1173,6 @@ unsafe fn static_to_mut(_: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesM
10981173
BytesMut::from(slice)
10991174
}
11001175

1101-
fn static_is_unique(_: &AtomicPtr<()>) -> bool {
1102-
false
1103-
}
1104-
11051176
unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) {
11061177
// nothing to drop for &'static [u8]
11071178
}
@@ -1152,10 +1223,6 @@ unsafe fn owned_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Byte
11521223
BytesMut::from_vec(owned_to_vec(data, ptr, len))
11531224
}
11541225

1155-
unsafe fn owned_is_unique(_data: &AtomicPtr<()>) -> bool {
1156-
false
1157-
}
1158-
11591226
unsafe fn owned_drop_impl(owned: *mut ()) {
11601227
let lifetime = owned.cast::<OwnedLifetime>();
11611228
let ref_cnt = &(*lifetime).ref_cnt;
@@ -1183,7 +1250,7 @@ static OWNED_VTABLE: Vtable = Vtable {
11831250
clone: owned_clone,
11841251
to_vec: owned_to_vec,
11851252
to_mut: owned_to_mut,
1186-
is_unique: owned_is_unique,
1253+
is_unique: never_unique,
11871254
drop: owned_drop,
11881255
};
11891256

@@ -1489,6 +1556,59 @@ unsafe fn shallow_clone_arc(shared: *mut Shared, ptr: *const u8, len: usize) ->
14891556
}
14901557
}
14911558

1559+
fn arcproj_vtable<T: Sync>() -> &'static Vtable {
1560+
// Produce vtable via const promotion to &'static.
1561+
// <https://users.rust-lang.org/t/custom-vtables-with-integers/78508/2>
1562+
trait V {
1563+
const VTABLE: Vtable;
1564+
}
1565+
impl<T: Sync> V for T {
1566+
const VTABLE: Vtable = Vtable {
1567+
clone: arcproj_clone::<T>,
1568+
to_vec: arcproj_to_vec::<T>,
1569+
to_mut: arcproj_to_mut::<T>,
1570+
is_unique: never_unique,
1571+
drop: arcproj_drop::<T>,
1572+
};
1573+
}
1574+
&<T as V>::VTABLE
1575+
}
1576+
1577+
unsafe fn arcproj_clone<T: Sync>(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
1578+
let arc = data.load(Ordering::Relaxed);
1579+
1580+
// Replicate `Arc::increment_strong_count`, which has a MSRV of 1.51.0.
1581+
let _ = std::mem::ManuallyDrop::new(Arc::<T>::from_raw(arc as *const T)).clone();
1582+
1583+
Bytes {
1584+
ptr,
1585+
len,
1586+
data: AtomicPtr::new(arc),
1587+
vtable: arcproj_vtable::<T>(),
1588+
}
1589+
}
1590+
1591+
unsafe fn arcproj_to_vec<T: Sync>(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
1592+
let vec = slice::from_raw_parts(ptr, len).to_vec();
1593+
arcproj_drop_impl::<T>(data);
1594+
vec
1595+
}
1596+
1597+
unsafe fn arcproj_to_mut<T: Sync>(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
1598+
let out = BytesMut::from(slice::from_raw_parts(ptr, len));
1599+
arcproj_drop_impl::<T>(data);
1600+
out
1601+
}
1602+
1603+
unsafe fn arcproj_drop<T: Sync>(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
1604+
arcproj_drop_impl::<T>(data);
1605+
}
1606+
1607+
unsafe fn arcproj_drop_impl<T: Sync>(data: &AtomicPtr<()>) {
1608+
// Replicate `Arc::decrement_strong_count`, which has a MSRV of 1.51.0.
1609+
drop(Arc::from_raw(data.load(Ordering::Relaxed) as *const T));
1610+
}
1611+
14921612
#[cold]
14931613
unsafe fn shallow_clone_vec(
14941614
atom: &AtomicPtr<()>,

tests/test_bytes.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,3 +1647,102 @@ fn owned_safe_drop_on_as_ref_panic() {
16471647
assert!(result.is_err());
16481648
assert_eq!(drop_counter.get(), 1);
16491649
}
1650+
1651+
#[test]
1652+
fn arcproj_is_unique_always_false() {
1653+
let b1 = Bytes::from_arc_projection(Arc::new([1, 2, 3, 4, 5, 6, 7]), |v| &v[..]);
1654+
assert!(!b1.is_unique()); // even if ref_cnt == 1
1655+
let b2 = b1.clone();
1656+
assert!(!b1.is_unique());
1657+
assert!(!b2.is_unique());
1658+
drop(b1);
1659+
assert!(!b2.is_unique()); // even if ref_cnt == 1
1660+
}
1661+
1662+
#[test]
1663+
fn arcproj_buf_sharing() {
1664+
let buf = [1, 2, 3, 4, 5, 6, 7];
1665+
let b1 = Bytes::from_arc_projection(Arc::new(buf), |v| &v[..]);
1666+
let b2 = b1.clone();
1667+
assert_eq!(&buf[..], &b1[..]);
1668+
assert_eq!(&buf[..], &b2[..]);
1669+
assert_eq!(b1.as_ptr(), b2.as_ptr());
1670+
assert_eq!(b1.len(), b2.len());
1671+
assert_eq!(b1.len(), buf.len());
1672+
}
1673+
1674+
#[test]
1675+
fn arcproj_buf_slicing() {
1676+
let b1 = Bytes::from_arc_projection(Arc::new(Vec::from(SHORT)), |v| &v[..]);
1677+
assert_eq!(SHORT, &b1[..]);
1678+
let b2 = b1.slice(1..(b1.len() - 1));
1679+
assert_eq!(&SHORT[1..(SHORT.len() - 1)], b2);
1680+
assert_eq!(unsafe { b1.as_ptr().add(1) }, b2.as_ptr());
1681+
assert_eq!(SHORT.len() - 2, b2.len());
1682+
}
1683+
1684+
#[test]
1685+
fn arcproj_dropped_exactly_once() {
1686+
let buf: [u8; 5] = [1, 2, 3, 4, 5];
1687+
let drop_counter = SharedAtomicCounter::new();
1688+
let owner = OwnedTester::new(buf, drop_counter.clone());
1689+
let b1 = Bytes::from_arc_projection(Arc::new(owner), |o| o.as_ref());
1690+
let b2 = b1.clone();
1691+
assert_eq!(drop_counter.get(), 0);
1692+
drop(b1);
1693+
assert_eq!(drop_counter.get(), 0);
1694+
let b3 = b2.slice(1..b2.len() - 1);
1695+
drop(b2);
1696+
assert_eq!(drop_counter.get(), 0);
1697+
drop(b3);
1698+
assert_eq!(drop_counter.get(), 1);
1699+
}
1700+
1701+
#[test]
1702+
fn arcproj_to_mut() {
1703+
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1704+
let drop_counter = SharedAtomicCounter::new();
1705+
let owner = OwnedTester::new(buf, drop_counter.clone());
1706+
let b1 = Bytes::from_arc_projection(Arc::new(owner), |o| o.as_ref());
1707+
1708+
// Holding an owner will fail converting to a BytesMut,
1709+
// even when the bytes instance has a ref_cnt == 1.
1710+
let b1 = b1.try_into_mut().unwrap_err();
1711+
1712+
// That said, it's still possible, just not cheap.
1713+
let bm1: BytesMut = b1.into();
1714+
let new_buf = &bm1[..];
1715+
assert_eq!(new_buf, &buf[..]);
1716+
1717+
// `.into::<BytesMut>()` has correctly dropped the owner
1718+
assert_eq!(drop_counter.get(), 1);
1719+
}
1720+
1721+
#[test]
1722+
fn arcproj_into_vec() {
1723+
let drop_counter = SharedAtomicCounter::new();
1724+
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1725+
let owner = OwnedTester::new(buf, drop_counter.clone());
1726+
let b1 = Bytes::from_arc_projection(Arc::new(owner), |o| o.as_ref());
1727+
1728+
let v1: Vec<u8> = b1.into();
1729+
assert_eq!(&v1[..], &buf[..]);
1730+
// into() vec will copy out of the owner and drop it
1731+
assert_eq!(drop_counter.get(), 1);
1732+
}
1733+
1734+
#[test]
1735+
#[cfg_attr(not(panic = "unwind"), ignore)]
1736+
fn arcproj_safe_drop_on_as_ref_panic() {
1737+
let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1738+
let drop_counter = SharedAtomicCounter::new();
1739+
let mut owner = OwnedTester::new(buf, drop_counter.clone());
1740+
owner.panic_as_ref = true;
1741+
1742+
let result = panic::catch_unwind(AssertUnwindSafe(|| {
1743+
let _ = Bytes::from_arc_projection(Arc::new(owner), |o| o.as_ref());
1744+
}));
1745+
1746+
assert!(result.is_err());
1747+
assert_eq!(drop_counter.get(), 1);
1748+
}

0 commit comments

Comments
 (0)