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