Skip to main content

bytes_pool/
lib.rs

1//! A pool for creating byte-slices and strings that can be cheaply cloned and shared across threads
2//! without allocating memory. Byte-slices are shared as [`Bytes`], and strings are shared as
3//! [`ByteString`]s.
4//!
5//! Internally, a `BytesPool` is a wrapper around a [`BytesMut`] buffer from the [`bytes`] crate.
6//! It shares data by appending the data to its buffer and then splitting the buffer off with
7//! [`BytesMut::split`]. This only allocates memory if the buffer needs to resize.
8
9#![no_std]
10
11pub use bytes::Bytes;
12pub use bytestring::ByteString;
13
14use bytes::BytesMut;
15
16/// A pool for creating byte-slices and strings that can be cheaply cloned and shared across threads
17/// without allocating memory. Byte-slices are shared as [`Bytes`], and strings are shared as
18/// [`ByteString`]s.
19///
20/// Internally, a `BytesPool` is a wrapper around a [`BytesMut`] buffer from the [`bytes`] crate.
21/// It shares data by appending the data to its buffer and then splitting the buffer off with
22/// [`BytesMut::split`]. This only allocates memory if the buffer needs to resize.
23#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct BytesPool {
25    inner: BytesMut,
26}
27
28impl BytesPool {
29    /// Creates a new `BytesPool` with default capacity.
30    ///
31    /// Resulting object has unspecified capacity.
32    /// This function does not allocate.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use bytes_pool::BytesPool;
38    ///
39    /// let mut pool = BytesPool::new();
40    /// ```
41    #[inline]
42    pub fn new() -> Self {
43        Self {
44            inner: BytesMut::new(),
45        }
46    }
47
48    /// Creates a new `BytesPool` with the specified capacity.
49    ///
50    /// The returned `BytesPool` will be able to hold at least `capacity` bytes
51    /// without reallocating.
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// use bytes_pool::BytesPool;
57    ///
58    /// let mut pool = BytesPool::with_capacity(64);
59    /// ```
60    #[inline]
61    pub fn with_capacity(capacity: usize) -> Self {
62        Self {
63            inner: BytesMut::with_capacity(capacity),
64        }
65    }
66
67    /// Returns the number of bytes the `BytesPool` can hold without reallocating.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use bytes_pool::BytesPool;
73    ///
74    /// let b = BytesPool::with_capacity(64);
75    /// assert_eq!(b.capacity(), 64);
76    /// ```
77    #[inline]
78    pub fn capacity(&self) -> usize {
79        self.inner.capacity()
80    }
81
82    /// Creates an immutable slice of bytes that can be shared across threads and cheaply cloned.
83    ///
84    /// No allocation is performed unless the internal buffer needs to be resized to accomodate
85    /// the additional bytes.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use bytes_pool::BytesPool;
91    ///
92    /// let mut pool = BytesPool::with_capacity(64);
93    ///
94    /// let bytes = pool.share_bytes(b"hello world");
95    ///
96    /// assert_eq!(bytes, &b"hello world"[..]);
97    /// ```
98    #[inline]
99    pub fn share_bytes(&mut self, bytes: &[u8]) -> Bytes {
100        self.inner.extend_from_slice(bytes);
101        self.inner.split().freeze()
102    }
103
104    /// Creates an immutable string that can be shared across threads and cheaply cloned.
105    ///
106    /// No allocation is performed unless the internal buffer needs to be resized to accomodate
107    /// the additional bytes.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use bytes_pool::BytesPool;
113    ///
114    /// let mut pool = BytesPool::with_capacity(64);
115    ///
116    /// let s = pool.share_str("hello world");
117    ///
118    /// assert_eq!(s, "hello world");
119    /// ```
120    #[inline]
121    pub fn share_str(&mut self, s: &str) -> ByteString {
122        let bytes = self.share_bytes(s.as_bytes());
123        // SAFETY: `self.inner` contains only valid UTF-8.
124        unsafe { ByteString::from_bytes_unchecked(bytes) }
125    }
126
127    /// Reserves capacity for at least `additional` bytes to be inserted
128    /// into the given `BytesPool`.
129    ///
130    /// More than `additional` bytes may be reserved in order to avoid frequent
131    /// reallocations. A call to `reserve` may result in an allocation.
132    ///
133    /// This function performs the same optimizations as [`BytesMut::reserve`].
134    ///
135    /// # Examples
136    ///
137    /// ```
138    /// use bytes_pool::BytesPool;
139    ///
140    /// let mut pool = BytesPool::with_capacity(128);
141    /// let bytes = pool.share_bytes(&[0; 64][..]);
142    ///
143    /// assert_eq!(pool.capacity(), 64);
144    ///
145    /// pool.reserve(128);
146    ///
147    /// assert_eq!(pool.capacity(), 128);
148    /// ```
149    ///
150    /// # Panics
151    ///
152    /// Panics if the new capacity overflows `usize`.
153    #[inline]
154    pub fn reserve(&mut self, additional: usize) {
155        self.inner.reserve(additional);
156    }
157
158    /// Attempts to cheaply reclaim already allocated capacity for at least `additional` more
159    /// bytes to be inserted into the given `BytesPool` and returns `true` if it succeeded.
160    ///
161    /// `try_reclaim` behaves exactly like [`reserve`](BytesPool::reserve), except that it never
162    /// allocates new storage  and returns a `bool` indicating whether it was successful in doing
163    /// so:
164    ///
165    /// `try_reclaim` returns false under these conditions:
166    ///  - The spare capacity left is less than `additional` bytes AND
167    ///  - The existing allocation cannot be reclaimed cheaply or it was less than
168    ///    `additional` bytes in size
169    ///
170    /// Reclaiming the allocation cheaply is possible if the `BytesPool` has no outstanding
171    /// references through `Bytes` or `ByteString`s which point to its underlying storage.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// use bytes_pool::BytesPool;
177    ///
178    /// let mut pool = BytesPool::with_capacity(64);
179    /// assert_eq!(true, pool.try_reclaim(64));
180    /// assert_eq!(64, pool.capacity());
181    ///
182    /// let mut bytes = pool.share_bytes(b"abcd");
183    /// assert_eq!(60, pool.capacity());
184    /// assert_eq!(false, pool.try_reclaim(64));
185    /// // pool has capacity for 60 bytes
186    /// assert_eq!(true, pool.try_reclaim(60));
187    ///
188    /// drop(bytes);
189    ///
190    /// assert_eq!(true, pool.try_reclaim(64));
191    /// ```
192    #[inline]
193    #[must_use = "consider BytesPool::reserve if you need an infallible reservation"]
194    pub fn try_reclaim(&mut self, additional: usize) -> bool {
195        self.inner.try_reclaim(additional)
196    }
197}