1use std::fmt;
2use std::hash::{Hash, Hasher};
3use std::mem::ManuallyDrop;
4use std::ops::Deref;
5use std::ops::DerefMut;
6use std::ptr::NonNull;
7
8use crate::{HeapBytesGrowable, InlineBytes23, INLINE_MASK};
9
10pub union CompactBytes {
41 heap: ManuallyDrop<HeapBytesGrowable>,
42 inline: InlineBytes23,
43}
44
45unsafe impl Send for CompactBytes {}
47
48unsafe impl Sync for CompactBytes {}
51
52static_assertions::assert_eq_align!(
53 InlineBytes23,
54 HeapBytesGrowable,
55 CompactBytes,
56 Vec<u8>,
57 usize
58);
59static_assertions::assert_eq_size!(InlineBytes23, HeapBytesGrowable, CompactBytes, Vec<u8>);
60
61static_assertions::const_assert_eq!(std::mem::size_of::<CompactBytes>(), 24);
62
63impl CompactBytes {
64 pub const MAX_INLINE: usize = 23;
66
67 pub const MIN_HEAP: usize = HeapBytesGrowable::MIN_ALLOCATION_SIZE;
69 pub const MAX_HEAP: usize = HeapBytesGrowable::MAX_ALLOCATION_SIZE;
71
72 #[inline]
89 pub fn new(slice: &[u8]) -> Self {
90 if slice.len() <= Self::MAX_INLINE {
91 let inline = unsafe { InlineBytes23::new(slice) };
93 CompactBytes { inline }
94 } else {
95 let heap = ManuallyDrop::new(HeapBytesGrowable::new(slice));
96 CompactBytes { heap }
97 }
98 }
99
100 #[inline]
112 pub fn with_capacity(capacity: usize) -> Self {
113 if capacity <= Self::MAX_INLINE {
114 let inline = InlineBytes23::empty();
115 CompactBytes { inline }
116 } else {
117 let heap = ManuallyDrop::new(HeapBytesGrowable::with_capacity(capacity));
118 CompactBytes { heap }
119 }
120 }
121
122 #[inline]
134 pub const fn empty() -> Self {
135 let inline = InlineBytes23::empty();
136 CompactBytes { inline }
137 }
138
139 #[inline]
151 pub unsafe fn from_raw_parts(ptr: *mut u8, length: usize, capacity: usize) -> Self {
152 let heap = HeapBytesGrowable {
153 ptr: NonNull::new_unchecked(ptr),
154 len: length,
155 cap: capacity,
156 };
157 let heap = ManuallyDrop::new(heap);
158 CompactBytes { heap }
159 }
160
161 #[inline]
163 pub fn as_slice(&self) -> &[u8] {
164 let pointer = self.as_ptr();
165 let length = self.len();
166
167 unsafe { core::slice::from_raw_parts(pointer, length) }
168 }
169
170 #[inline]
172 pub fn as_mut_slice(&mut self) -> &mut [u8] {
173 let pointer = self.as_mut_ptr();
174 let length = self.len();
175
176 unsafe { core::slice::from_raw_parts_mut(pointer, length) }
177 }
178
179 #[inline(always)]
181 pub fn len(&self) -> usize {
182 let (mut length, heap_length) = unsafe { (self.inline.len(), self.heap.len) };
188 if self.spilled() {
189 length = heap_length;
190 }
191
192 length
193 }
194
195 #[inline]
197 pub fn is_empty(&self) -> bool {
198 self.len() == 0
199 }
200
201 #[inline(always)]
203 pub fn capacity(&self) -> usize {
204 let (mut capacity, heap_capacity) = unsafe { (Self::MAX_INLINE, self.heap.cap) };
211 if self.spilled() {
212 capacity = heap_capacity;
213 }
214 capacity
215 }
216
217 #[inline]
222 pub fn push(&mut self, byte: u8) {
223 self.extend_from_slice(&[byte]);
224 }
225
226 #[inline(always)]
228 pub fn extend_from_slice(&mut self, slice: &[u8]) {
229 self.reserve(slice.len());
231
232 let (ptr, len, cap) = self.as_mut_triple();
233 let push_ptr = unsafe { ptr.add(len) };
236
237 debug_assert!((cap - len) >= slice.len(), "failed to reserve enough space");
238
239 unsafe { std::ptr::copy_nonoverlapping(slice.as_ptr(), push_ptr, slice.len()) };
247
248 unsafe { self.set_len(len + slice.len()) };
250 }
251
252 #[inline]
254 pub fn clear(&mut self) {
255 self.truncate(0);
256 }
257
258 #[inline]
261 pub fn truncate(&mut self, new_len: usize) {
262 if new_len >= self.len() {
263 return;
264 }
265 unsafe { self.set_len(new_len) }
266 }
267
268 #[inline]
282 pub fn reserve(&mut self, additional: usize) {
283 let len = self.len();
284 let needed_capacity = len
285 .checked_add(additional)
286 .expect("attempt to reserve more than usize::MAX");
287
288 if self.capacity() >= needed_capacity {
290 return;
291 }
292
293 realloc(self, len, additional);
297
298 #[cold]
299 fn realloc(this: &mut CompactBytes, len: usize, additional: usize) {
300 if !this.spilled() {
309 let heap = HeapBytesGrowable::with_additional(this.as_slice(), additional);
310 *this = CompactBytes {
311 heap: ManuallyDrop::new(heap),
312 };
313 } else {
314 let heap_row = unsafe { &mut this.heap };
317
318 let amortized_capacity = HeapBytesGrowable::amortized_growth(len, additional);
319
320 if heap_row.realloc(amortized_capacity).is_err() {
322 let heap = HeapBytesGrowable::with_additional(this.as_slice(), additional);
323 let heap = ManuallyDrop::new(heap);
324 *this = CompactBytes { heap };
325 }
326 }
327 }
328 }
329
330 #[inline]
332 pub fn into_vec(self) -> Vec<u8> {
333 if self.spilled() {
334 let heap = unsafe { &self.heap };
338 let vec = unsafe { Vec::from_raw_parts(heap.ptr.as_ptr(), heap.len, heap.cap) };
339 std::mem::forget(self);
340
341 vec
342 } else {
343 self.as_slice().to_vec()
344 }
345 }
346
347 #[inline(always)]
349 pub fn spilled(&self) -> bool {
350 unsafe { self.inline.data < INLINE_MASK }
354 }
355
356 #[inline]
363 unsafe fn set_len(&mut self, new_len: usize) {
364 if self.spilled() {
365 self.heap.set_len(new_len);
366 } else {
367 self.inline.set_len(new_len);
368 }
369 }
370
371 #[inline(always)]
372 fn as_ptr(&self) -> *const u8 {
373 let mut pointer = self as *const Self as *const u8;
380 if self.spilled() {
381 pointer = unsafe { self.heap.ptr }.as_ptr()
382 }
383 pointer
384 }
385
386 #[inline(always)]
387 fn as_mut_ptr(&mut self) -> *mut u8 {
388 let mut pointer = self as *mut Self as *mut u8;
395 if self.spilled() {
396 pointer = unsafe { self.heap.ptr }.as_ptr()
397 }
398 pointer
399 }
400
401 #[inline(always)]
402 fn as_mut_triple(&mut self) -> (*mut u8, usize, usize) {
403 let ptr = self.as_mut_ptr();
404 let len = self.len();
405 let cap = self.capacity();
406
407 (ptr, len, cap)
408 }
409}
410
411impl Default for CompactBytes {
412 #[inline]
413 fn default() -> Self {
414 CompactBytes::new(&[])
415 }
416}
417
418impl Deref for CompactBytes {
419 type Target = [u8];
420
421 #[inline]
422 fn deref(&self) -> &[u8] {
423 self.as_slice()
424 }
425}
426
427impl DerefMut for CompactBytes {
428 #[inline]
429 fn deref_mut(&mut self) -> &mut [u8] {
430 self.as_mut_slice()
431 }
432}
433
434impl AsRef<[u8]> for CompactBytes {
435 #[inline]
436 fn as_ref(&self) -> &[u8] {
437 self.as_slice()
438 }
439}
440
441impl<T: AsRef<[u8]>> PartialEq<T> for CompactBytes {
442 #[inline]
443 fn eq(&self, other: &T) -> bool {
444 self.as_slice() == other.as_ref()
445 }
446}
447
448impl Eq for CompactBytes {}
449
450impl Hash for CompactBytes {
451 fn hash<H: Hasher>(&self, state: &mut H) {
452 self.as_slice().hash(state)
453 }
454}
455
456impl fmt::Debug for CompactBytes {
457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458 write!(f, "{:?}", self.as_slice())
459 }
460}
461
462impl Drop for CompactBytes {
463 #[inline]
464 fn drop(&mut self) {
465 #[cold]
468 fn outlined_drop(this: &mut CompactBytes) {
469 let heap = unsafe { &mut this.heap };
470 heap.dealloc();
471 }
472
473 if self.spilled() {
474 outlined_drop(self);
475 }
476 }
477}
478
479impl Clone for CompactBytes {
480 #[inline]
481 fn clone(&self) -> Self {
482 #[cold]
485 fn outlined_clone(this: &CompactBytes) -> CompactBytes {
486 CompactBytes::new(this.as_slice())
487 }
488
489 if self.spilled() {
490 outlined_clone(self)
491 } else {
492 let inline = unsafe { &self.inline };
493 CompactBytes { inline: *inline }
494 }
495 }
496
497 #[inline]
498 fn clone_from(&mut self, source: &Self) {
499 self.clear();
500 self.extend_from_slice(source.as_slice());
501 }
502}
503
504impl From<Vec<u8>> for CompactBytes {
505 #[inline]
506 fn from(mut value: Vec<u8>) -> Self {
507 if value.is_empty() {
508 let inline = InlineBytes23::empty();
509 return CompactBytes { inline };
510 }
511
512 let (ptr, len, cap) = (value.as_mut_ptr(), value.len(), value.capacity());
514 let ptr = unsafe { NonNull::new_unchecked(ptr) };
517 std::mem::forget(value);
519
520 let heap = HeapBytesGrowable { ptr, len, cap };
521 CompactBytes {
522 heap: ManuallyDrop::new(heap),
523 }
524 }
525}
526
527impl serde::Serialize for CompactBytes {
528 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
529 self.as_slice().serialize(serializer)
530 }
531}
532
533impl<'de> serde::Deserialize<'de> for CompactBytes {
534 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
535 deserialize_compact_bytes(deserializer)
536 }
537}
538
539fn deserialize_compact_bytes<'de: 'a, 'a, D: serde::Deserializer<'de>>(
540 deserializer: D,
541) -> Result<CompactBytes, D::Error> {
542 struct CompactBytesVisitor;
543
544 impl<'a> serde::de::Visitor<'a> for CompactBytesVisitor {
545 type Value = CompactBytes;
546
547 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
548 formatter.write_str("bytes")
549 }
550
551 fn visit_seq<A: serde::de::SeqAccess<'a>>(
552 self,
553 mut seq: A,
554 ) -> Result<Self::Value, A::Error> {
555 let mut bytes = CompactBytes::default();
556 if let Some(capacity_hint) = seq.size_hint() {
557 bytes.reserve(capacity_hint);
558 }
559
560 while let Some(elem) = seq.next_element::<u8>()? {
561 bytes.push(elem)
562 }
563
564 Ok(bytes)
565 }
566
567 fn visit_borrowed_bytes<E: serde::de::Error>(self, v: &'a [u8]) -> Result<Self::Value, E> {
568 Ok(CompactBytes::new(v))
569 }
570
571 fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
572 Ok(CompactBytes::new(v))
573 }
574 }
575
576 deserializer.deserialize_bytes(CompactBytesVisitor)
577}
578
579#[cfg(test)]
580mod test {
581 use proptest::prelude::*;
582 use test_case::test_case;
583 use test_strategy::proptest;
584
585 use super::{CompactBytes, HeapBytesGrowable};
586
587 #[test]
588 fn test_bitcode() {
589 let obj = CompactBytes::new(b"hello world");
590 let encoded = bitcode::serialize(&obj).unwrap();
591 let decoded: CompactBytes = bitcode::deserialize(&encoded).unwrap();
592 assert_eq!(obj.as_slice(), decoded.as_slice());
593 }
594
595 #[test]
596 fn test_empty() {
597 let obj = const { CompactBytes::empty() };
598 assert_eq!(obj.as_slice(), [0u8; 0].as_slice());
599 assert!(obj.is_empty());
600 assert!(!obj.spilled())
601 }
602
603 #[test]
604 #[cfg_attr(miri, ignore)]
605 fn test_discriminant() {
606 let mut buf = vec![0u8; 32];
607 let heap = HeapBytesGrowable {
608 ptr: unsafe { std::ptr::NonNull::new_unchecked(buf.as_mut_ptr()) },
609 len: 0,
610 cap: usize::MAX >> 1,
611 };
612 let repr = CompactBytes {
613 heap: std::mem::ManuallyDrop::new(heap),
614 };
615 assert!(repr.spilled());
616 std::mem::forget(repr);
618
619 let bad_heap = HeapBytesGrowable {
620 ptr: unsafe { std::ptr::NonNull::new_unchecked(buf.as_mut_ptr()) },
621 len: 0,
622 cap: usize::MAX,
623 };
624 let repr = CompactBytes {
625 heap: std::mem::ManuallyDrop::new(bad_heap),
626 };
627 assert!(!repr.spilled());
629 std::mem::forget(repr);
631 }
632
633 #[test_case(&[], 0 ; "empty")]
634 #[test_case(b"hello world", 11 ; "short")]
635 #[test_case(b"can fit 23 bytes inline", 23 ; "max_inline")]
636 #[test_case(b"24 bytes and will spill!", 24 ; "first_spill")]
637 #[test_case(b"i am very large and will spill to the heap", 42 ; "heap")]
638 fn smoketest_row(slice: &[u8], expected_len: usize) {
639 let repr = CompactBytes::new(slice);
640
641 assert_eq!(repr.len(), expected_len);
642 assert_eq!(repr.as_slice(), slice);
643 assert_eq!(repr.spilled(), expected_len > CompactBytes::MAX_INLINE);
644 }
645
646 #[test_case(&[], &[] ; "empty_empty")]
647 #[test_case(&[], &[1, 2, 3, 4] ; "empty_inline")]
648 #[test_case(&[], b"once extended I will end up on the heap" ; "empty_heap")]
649 #[test_case(&[1, 2], &[3, 4] ; "inline_inline")]
650 #[test_case(&[1, 2, 3, 4], b"i am some more bytes, i will be on the heap, woohoo!" ; "inline_heap")]
651 #[test_case(b"this row will start on the heap because it's large", b"and this will keep it on the heap" ; "heap_heap")]
652 fn smoketest_extend(initial: &[u8], other: &[u8]) {
653 let mut repr = CompactBytes::new(initial);
654 repr.extend_from_slice(other);
655
656 let mut control = initial.to_vec();
657 control.extend_from_slice(other);
658
659 assert_eq!(repr.len(), control.len());
660 assert_eq!(repr.as_slice(), control.as_slice());
661 }
662
663 #[test_case(&[] ; "empty")]
664 #[test_case(b"i am smol" ; "inline")]
665 #[test_case(b"i am large and will end up on the heap" ; "heap")]
666 fn smoketest_clear(initial: &[u8]) {
667 let mut repr = CompactBytes::new(initial);
668 let capacity = repr.capacity();
669 assert_eq!(repr.as_slice(), initial);
670
671 repr.clear();
672
673 assert!(repr.as_slice().is_empty());
674 assert_eq!(repr.len(), 0);
675
676 assert_eq!(repr.capacity(), capacity);
678 }
679
680 #[test_case(&[] ; "empty")]
681 #[test_case(b"smol" ; "inline")]
682 #[test_case(b"large large large large large large" ; "heap")]
683 fn smoketest_clone(initial: &[u8]) {
684 let repr_a = CompactBytes::new(initial);
685 let repr_b = repr_a.clone();
686
687 assert_eq!(repr_a.len(), repr_b.len());
688 assert_eq!(repr_a.capacity(), repr_b.capacity());
689 assert_eq!(repr_a.as_slice(), repr_b.as_slice());
690 }
691
692 #[test_case(&[], &[], false ; "empty_empty")]
693 #[test_case(&[], b"hello", false ; "empty_inline")]
694 #[test_case(&[], b"I am long and will be on the heap", true ; "empty_heap")]
695 #[test_case(b"short", &[], false ; "inline_empty")]
696 #[test_case(b"hello", b"world", false ; "inline_inline")]
697 #[test_case(b"i am short", b"I am long and will be on the heap", true ; "inline_heap")]
698 fn smoketest_clone_from(a: &[u8], b: &[u8], should_reallocate: bool) {
699 let mut a = CompactBytes::new(a);
700 let a_capacity = a.capacity();
701 let a_pointer = a.as_slice().as_ptr();
702
703 let b = CompactBytes::new(b);
704
705 a.clone_from(&b);
707
708 assert_eq!(a.capacity() != a_capacity, should_reallocate);
709 assert_eq!(a.as_slice().as_ptr() != a_pointer, should_reallocate);
710 }
711
712 #[test_case(vec![] ; "empty")]
713 #[test_case(vec![0, 1, 2, 3, 4] ; "inline")]
714 #[test_case(b"I am long and will be on the heap, yada yada yada".to_vec() ; "heap")]
715 fn smoketest_from_vec(initial: Vec<u8>) {
716 let control = initial.clone();
717 let pointer = initial.as_ptr();
718 let repr = CompactBytes::from(initial);
719
720 assert_eq!(control.len(), repr.len());
721 assert_eq!(control.as_slice(), repr.as_slice());
722
723 assert_eq!(repr.spilled(), !control.is_empty());
725 assert_eq!(repr.as_ptr() == pointer, !control.is_empty());
727 }
728
729 #[test]
730 fn test_cloning_inlines() {
731 let mut c = CompactBytes::with_capacity(48);
732 c.push(42);
733
734 assert_eq!(c.as_slice(), &[42]);
735 assert_eq!(c.capacity(), 48);
736 assert!(c.spilled());
737
738 let clone = c.clone();
739 assert_eq!(clone.as_slice(), &[42]);
740 assert_eq!(clone.capacity(), CompactBytes::MAX_INLINE);
741 assert!(!clone.spilled());
742 }
743
744 #[test]
745 fn test_cloning_drops_excess_capacity() {
746 let mut c = CompactBytes::with_capacity(48);
747 c.extend_from_slice(&[42; 32]);
748
749 assert_eq!(c.as_slice(), &[42; 32]);
750 assert_eq!(c.capacity(), 48);
751 assert_eq!(c.len(), 32);
752 assert!(c.spilled());
753
754 let clone = c.clone();
755 assert_eq!(clone.as_slice(), &[42; 32]);
756 assert_eq!(clone.capacity(), 32);
757 assert_eq!(clone.capacity(), clone.len());
758 assert!(clone.spilled());
759 }
760
761 #[proptest]
762 #[cfg_attr(miri, ignore)]
763 fn proptest_row(initial: Vec<u8>) {
764 let repr = CompactBytes::new(&initial);
765
766 prop_assert_eq!(repr.as_slice(), initial.as_slice());
767 prop_assert_eq!(repr.len(), initial.len());
768 }
769
770 #[proptest]
771 #[cfg_attr(miri, ignore)]
772 fn proptest_extend(initial: Vec<u8>, other: Vec<u8>) {
773 let mut repr = CompactBytes::new(&initial);
774 repr.extend_from_slice(other.as_slice());
775
776 let mut control = initial;
777 control.extend_from_slice(other.as_slice());
778
779 prop_assert_eq!(repr.as_slice(), control.as_slice());
780 prop_assert_eq!(repr.len(), control.len());
781 }
782
783 #[proptest]
784 #[cfg_attr(miri, ignore)]
785 fn proptest_clear(initial: Vec<u8>) {
786 let mut repr = CompactBytes::new(&initial);
787 let capacity = repr.capacity();
788
789 repr.clear();
790 assert!(repr.as_slice().is_empty());
791 assert_eq!(repr.len(), 0);
792
793 assert_eq!(repr.capacity(), capacity);
795 }
796
797 #[proptest]
798 #[cfg_attr(miri, ignore)]
799 fn proptest_clear_then_extend(initial: Vec<u8>, a: Vec<u8>) {
800 let mut repr = CompactBytes::new(&initial);
801 let capacity = repr.capacity();
802 let pointer = repr.as_slice().as_ptr();
803
804 repr.clear();
805 assert!(repr.as_slice().is_empty());
806 assert_eq!(repr.len(), 0);
807
808 assert_eq!(repr.capacity(), capacity);
810
811 repr.extend_from_slice(&a);
812 assert_eq!(repr.as_slice(), &a);
813 assert_eq!(repr.len(), a.len());
814
815 if a.len() < capacity {
817 assert_eq!(repr.capacity(), capacity);
818 assert_eq!(repr.as_slice().as_ptr(), pointer);
819 }
820 }
821
822 #[proptest]
823 #[cfg_attr(miri, ignore)]
824 fn proptest_clone(initial: Vec<u8>) {
825 let repr_a = CompactBytes::new(&initial);
826 let repr_b = repr_a.clone();
827
828 assert_eq!(repr_a.len(), repr_b.len());
829 assert_eq!(repr_a.capacity(), repr_b.capacity());
830 assert_eq!(repr_a.as_slice(), repr_b.as_slice());
831 }
832
833 #[proptest]
834 #[cfg_attr(miri, ignore)]
835 fn proptest_from_vec(initial: Vec<u8>) {
836 let control = initial.clone();
837 let pointer = initial.as_ptr();
838 let repr = CompactBytes::from(initial);
839
840 assert_eq!(control.len(), repr.len());
841 assert_eq!(control.as_slice(), repr.as_slice());
842
843 assert_eq!(repr.spilled(), !control.is_empty());
845 assert_eq!(repr.as_ptr() == pointer, !control.is_empty());
847 }
848
849 #[proptest]
850 #[cfg_attr(miri, ignore)]
851 fn proptest_serde(initial: Vec<u8>) {
852 let repr = CompactBytes::new(&initial);
853
854 let (repr_json, ctrl_json) = match (
855 serde_json::to_string(&repr),
856 serde_json::to_string(&initial),
857 ) {
858 (Ok(r), Ok(c)) => (r, c),
859 (Err(_), Err(_)) => return Ok(()),
860 (r, c) => panic!("Got mismatched results when serializing {r:?}, {c:?}"),
861 };
862
863 prop_assert_eq!(&repr_json, &ctrl_json);
864
865 let (repr_rnd_trip, ctrl_rnd_trip): (CompactBytes, Vec<u8>) = match (
866 serde_json::from_str(&repr_json),
867 serde_json::from_str(&ctrl_json),
868 ) {
869 (Ok(r), Ok(c)) => (r, c),
870 (Err(_), Err(_)) => return Ok(()),
871 (r, c) => panic!("Got mismatched results {r:?}, {c:?}"),
872 };
873
874 prop_assert_eq!(&repr, &repr_rnd_trip);
875 prop_assert_eq!(repr_rnd_trip, ctrl_rnd_trip);
876 }
877
878 #[test]
879 fn test_heap_bytes_capacity() {
880 let heap = HeapBytesGrowable::with_capacity(1);
881 drop(heap);
882 }
883}