planck_noalloc/
smallbytestr.rs1use alloc::vec::Vec;
20
21use crate::smallstr::SmallStr;
22use crate::smallvec::SmallVec;
23
24#[derive(Clone, Default)]
29pub struct SmallByteStr<const N: usize> {
30 buf: SmallVec<u8, N>,
31}
32
33impl<const N: usize> SmallByteStr<N> {
34 #[must_use]
36 pub const fn new() -> Self {
37 Self {
38 buf: SmallVec::new(),
39 }
40 }
41
42 #[must_use]
44 pub fn with_capacity(cap: usize) -> Self {
45 Self {
46 buf: SmallVec::with_capacity(cap),
47 }
48 }
49
50 #[must_use]
52 pub fn from_bytes(bytes: &[u8]) -> Self {
53 let mut bs = Self::with_capacity(bytes.len());
54 bs.buf.extend(bytes.iter().copied());
55 bs
56 }
57
58 pub fn push_bytes(&mut self, bytes: &[u8]) {
60 self.buf.extend(bytes.iter().copied());
61 }
62
63 pub fn push(&mut self, byte: u8) {
65 self.buf.push(byte);
66 }
67
68 #[must_use]
70 pub fn pop(&mut self) -> Option<u8> {
71 self.buf.pop()
72 }
73
74 pub fn clear(&mut self) {
76 self.buf.clear();
77 }
78
79 #[must_use]
81 pub fn len(&self) -> usize {
82 self.buf.len()
83 }
84
85 #[must_use]
87 pub fn is_empty(&self) -> bool {
88 self.buf.is_empty()
89 }
90
91 #[must_use]
93 pub fn capacity(&self) -> usize {
94 self.buf.capacity()
95 }
96
97 #[must_use]
99 pub fn is_inline(&self) -> bool {
100 self.buf.is_inline()
101 }
102
103 pub fn reserve(&mut self, additional: usize) {
105 self.buf.reserve(additional);
106 }
107
108 pub fn shrink_to_fit(&mut self) {
110 self.buf.shrink_to_fit();
111 }
112
113 pub fn spill(&mut self) {
115 self.buf.spill();
116 }
117
118 #[must_use]
120 pub fn as_bytes(&self) -> &[u8] {
121 self.buf.as_slice()
122 }
123
124 #[must_use]
126 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
127 self.buf.as_mut_slice()
128 }
129
130 #[must_use]
132 pub fn into_bytes(self) -> SmallVec<u8, N> {
133 self.buf
134 }
135
136 #[must_use]
138 pub fn into_vec(self) -> Vec<u8> {
139 self.buf.into_vec()
140 }
141
142 pub fn truncate(&mut self, new_len: usize) {
146 self.buf.truncate(new_len);
147 }
148
149 pub fn into_small_str(self) -> Result<SmallStr<N>, Self> {
155 if core::str::from_utf8(self.buf.as_slice()).is_ok() {
156 Ok(unsafe { SmallStr::from_small_vec_unchecked(self.buf) })
158 } else {
159 Err(self)
160 }
161 }
162}
163
164impl<const N: usize> core::fmt::Debug for SmallByteStr<N> {
165 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
166 write!(f, "SmallByteStr({:?})", self.buf.as_slice())
168 }
169}
170
171impl<const N: usize> core::ops::Deref for SmallByteStr<N> {
172 type Target = [u8];
173
174 fn deref(&self) -> &[u8] {
175 self.as_bytes()
176 }
177}
178
179impl<const N: usize> core::ops::DerefMut for SmallByteStr<N> {
180 fn deref_mut(&mut self) -> &mut [u8] {
181 self.as_bytes_mut()
182 }
183}
184
185impl<const N: usize> PartialEq for SmallByteStr<N> {
186 fn eq(&self, other: &Self) -> bool {
187 self.as_bytes() == other.as_bytes()
188 }
189}
190
191impl<const N: usize> Eq for SmallByteStr<N> {}
192
193impl<const N: usize> PartialOrd for SmallByteStr<N> {
194 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
195 Some(self.cmp(other))
196 }
197}
198
199impl<const N: usize> Ord for SmallByteStr<N> {
200 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
201 self.as_bytes().cmp(other.as_bytes())
202 }
203}
204
205impl<const N: usize> core::hash::Hash for SmallByteStr<N> {
206 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
207 self.as_bytes().hash(state);
208 }
209}
210
211impl<const N: usize> From<&[u8]> for SmallByteStr<N> {
212 fn from(bytes: &[u8]) -> Self {
213 Self::from_bytes(bytes)
214 }
215}
216
217impl<const N: usize> From<Vec<u8>> for SmallByteStr<N> {
218 fn from(v: Vec<u8>) -> Self {
219 Self {
220 buf: SmallVec::from(v),
221 }
222 }
223}
224
225impl<const N: usize> From<&str> for SmallByteStr<N> {
226 fn from(s: &str) -> Self {
227 Self::from_bytes(s.as_bytes())
228 }
229}
230
231impl<const N: usize> Extend<u8> for SmallByteStr<N> {
232 fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
233 self.buf.extend(iter);
234 }
235}
236
237impl<const N: usize> FromIterator<u8> for SmallByteStr<N> {
238 fn from_iter<I: IntoIterator<Item = u8>>(iter: I) -> Self {
239 Self {
240 buf: SmallVec::from_iter(iter),
241 }
242 }
243}
244
245impl<const N: usize> AsRef<[u8]> for SmallByteStr<N> {
246 fn as_ref(&self) -> &[u8] {
247 self.as_bytes()
248 }
249}
250
251impl<const N: usize> core::borrow::Borrow<[u8]> for SmallByteStr<N> {
252 fn borrow(&self) -> &[u8] {
253 self.as_bytes()
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 extern crate alloc;
260 use alloc::vec;
261
262 use super::*;
263
264 #[test]
265 fn new_is_empty() {
266 let bs = SmallByteStr::<8>::new();
267 assert!(bs.is_empty());
268 assert_eq!(bs.len(), 0);
269 }
270
271 #[test]
272 fn push_and_pop() {
273 let mut bs = SmallByteStr::<8>::new();
274 bs.push(0x41);
275 bs.push(0x42);
276 assert_eq!(bs.as_bytes(), b"AB");
277 assert_eq!(bs.pop(), Some(0x42));
278 assert_eq!(bs.pop(), Some(0x41));
279 assert_eq!(bs.pop(), None);
280 }
281
282 #[test]
283 fn from_bytes() {
284 let bs = SmallByteStr::<8>::from_bytes(b"hello");
285 assert_eq!(bs.as_bytes(), b"hello");
286 assert!(bs.is_inline());
287 }
288
289 #[test]
290 fn push_bytes() {
291 let mut bs = SmallByteStr::<16>::new();
292 bs.push_bytes(b"hello");
293 bs.push_bytes(b" world");
294 assert_eq!(bs.as_bytes(), b"hello world");
295 }
296
297 #[test]
298 fn spills_to_heap() {
299 let mut bs = SmallByteStr::<2>::new();
300 bs.push(1);
301 bs.push(2);
302 assert!(bs.is_inline());
303 bs.push(3);
304 assert!(!bs.is_inline());
305 assert_eq!(bs.as_bytes(), &[1, 2, 3]);
306 }
307
308 #[test]
309 fn clear() {
310 let mut bs = SmallByteStr::<8>::from_bytes(b"hello");
311 bs.clear();
312 assert!(bs.is_empty());
313 }
314
315 #[test]
316 fn truncate() {
317 let mut bs = SmallByteStr::<8>::from_bytes(b"hello");
318 bs.truncate(3);
319 assert_eq!(bs.as_bytes(), b"hel");
320 }
321
322 #[test]
323 fn into_vec() {
324 let bs = SmallByteStr::<8>::from_bytes(b"hi");
325 let v = bs.into_vec();
326 assert_eq!(v, vec![b'h', b'i']);
327 }
328
329 #[test]
330 fn into_bytes() {
331 let bs = SmallByteStr::<8>::from_bytes(b"hi");
332 let sv = bs.into_bytes();
333 assert_eq!(sv.as_slice(), b"hi");
334 }
335
336 #[test]
337 fn into_small_str_valid() {
338 let bs = SmallByteStr::<8>::from_bytes(b"hello");
339 let s = bs.into_small_str().unwrap();
340 assert_eq!(s.as_str(), "hello");
341 }
342
343 #[test]
344 fn into_small_str_invalid() {
345 let bs = SmallByteStr::<8>::from_bytes(&[0xFF, 0xFE]);
346 let err = bs.into_small_str().unwrap_err();
347 assert_eq!(err.as_bytes(), &[0xFF, 0xFE]);
348 }
349
350 #[test]
351 fn from_slice() {
352 let bs: SmallByteStr<8> = b"hello".as_slice().into();
353 assert_eq!(bs.as_bytes(), b"hello");
354 }
355
356 #[test]
357 fn from_vec() {
358 let bs: SmallByteStr<8> = SmallByteStr::from(vec![1, 2, 3]);
359 assert_eq!(bs.as_bytes(), &[1, 2, 3]);
360 }
361
362 #[test]
363 fn from_str() {
364 let bs: SmallByteStr<8> = SmallByteStr::from("hi");
365 assert_eq!(bs.as_bytes(), b"hi");
366 }
367
368 #[test]
369 fn extend_and_from_iter() {
370 let bs: SmallByteStr<4> = (0..5u8).collect();
371 assert_eq!(bs.as_bytes(), &[0, 1, 2, 3, 4]);
372 }
373
374 #[test]
375 fn eq_and_ord() {
376 let a = SmallByteStr::<8>::from_bytes(b"abc");
377 let b = SmallByteStr::<8>::from_bytes(b"abc");
378 let c = SmallByteStr::<8>::from_bytes(b"abd");
379 assert_eq!(a, b);
380 assert!(a < c);
381 }
382
383 #[test]
384 fn debug_format() {
385 let bs = SmallByteStr::<8>::from_bytes(b"hi");
386 let s = alloc::format!("{bs:?}");
387 assert!(s.contains("SmallByteStr"));
388 }
389
390 #[test]
391 fn deref_and_deref_mut() {
392 let mut bs = SmallByteStr::<8>::from_bytes(b"hi");
393 assert_eq!(bs.len(), 2);
394 assert!(bs.contains(&b'h'));
395 bs[0] = b'H';
396 assert_eq!(bs.as_bytes(), b"Hi");
397 }
398
399 #[test]
400 fn as_bytes_mut() {
401 let mut bs = SmallByteStr::<8>::from_bytes(b"hello");
402 bs.as_bytes_mut()[0] = b'H';
403 assert_eq!(bs.as_bytes(), b"Hello");
404 }
405
406 #[test]
407 fn clone() {
408 let bs = SmallByteStr::<8>::from_bytes(b"hello");
409 let bs2 = bs.clone();
410 assert_eq!(bs.as_bytes(), bs2.as_bytes());
411 }
412
413 #[test]
414 fn reserve_and_shrink() {
415 let mut bs = SmallByteStr::<8>::from_bytes(b"hi");
416 bs.reserve(100);
417 assert!(!bs.is_inline());
418 bs.shrink_to_fit();
419 assert!(bs.is_inline());
420 assert_eq!(bs.as_bytes(), b"hi");
421 }
422
423 #[test]
424 fn with_capacity() {
425 let bs = SmallByteStr::<4>::with_capacity(3);
426 assert!(bs.is_inline());
427 let bs = SmallByteStr::<4>::with_capacity(10);
428 assert!(!bs.is_inline());
429 }
430
431 #[test]
432 fn as_ref_and_borrow() {
433 let bs = SmallByteStr::<8>::from_bytes(b"hi");
434 let r: &[u8] = bs.as_ref();
435 assert_eq!(r, b"hi");
436 let b: &[u8] = core::borrow::Borrow::borrow(&bs);
437 assert_eq!(b, b"hi");
438 }
439}