1use std::{
6 mem::ManuallyDrop,
7 ops::Deref,
8 sync::{
9 Arc,
10 atomic::{AtomicU64, Ordering, fence},
11 },
12};
13
14pub use crate::builder::Builder;
15
16#[cfg(target_pointer_width = "64")]
17const INLINE_SIZE: usize = 20;
18
19#[cfg(target_pointer_width = "32")]
20const INLINE_SIZE: usize = 16;
21
22const PREFIX_SIZE: usize = 4;
23
24#[repr(C)]
25struct HeapAllocationHeader {
26 ref_count: AtomicU64,
27}
28
29fn allocation_layout(data_len: usize) -> std::alloc::Layout {
30 let Some(total_size) = std::mem::size_of::<HeapAllocationHeader>().checked_add(data_len) else {
31 panic!("byte slice too long");
32 };
33 let alignment = std::mem::align_of::<HeapAllocationHeader>();
34 let Ok(layout) = std::alloc::Layout::from_size_align(total_size, alignment) else {
35 unreachable!("heap header alignment is always valid");
36 };
37 layout
38}
39
40#[repr(C)]
41struct ShortRepr {
42 len: u32,
43 data: [u8; INLINE_SIZE],
44}
45
46#[repr(C)]
47struct LongRepr {
48 len: u32,
49 prefix: [u8; PREFIX_SIZE],
50 heap: *const u8,
51 original_len: u32,
52 offset: u32,
53}
54
55#[repr(C)]
56pub union Trailer {
57 short: ManuallyDrop<ShortRepr>,
58 long: ManuallyDrop<LongRepr>,
59}
60
61impl Default for Trailer {
62 fn default() -> Self {
63 Self {
64 short: ManuallyDrop::new(ShortRepr {
65 len: 0,
66 data: [0; INLINE_SIZE],
67 }),
68 }
69 }
70}
71
72#[repr(C)]
90#[derive(Default)]
91pub struct ByteView {
92 trailer: Trailer,
93}
94
95#[allow(clippy::non_send_fields_in_send_ty)]
96unsafe impl Send for ByteView {}
97#[allow(clippy::non_send_fields_in_send_ty)]
98unsafe impl Sync for ByteView {}
99
100impl Clone for ByteView {
101 fn clone(&self) -> Self {
102 if !self.is_inline() {
103 self.get_heap_region()
104 .ref_count
105 .fetch_add(1, Ordering::Relaxed);
106 }
107
108 unsafe { std::ptr::read(self) }
111 }
112}
113
114impl Drop for ByteView {
115 fn drop(&mut self) {
116 if self.is_inline() {
117 return;
118 }
119
120 let heap_region = self.get_heap_region();
121
122 if heap_region.ref_count.fetch_sub(1, Ordering::Release) != 1 {
123 return;
124 }
125 fence(Ordering::Acquire);
126
127 unsafe {
128 let layout = allocation_layout(self.trailer.long.original_len as usize);
129 let ptr = self.trailer.long.heap.cast_mut();
130 std::alloc::dealloc(ptr, layout);
131 }
132 }
133}
134
135impl Eq for ByteView {}
136
137impl std::cmp::PartialEq for ByteView {
138 fn eq(&self, other: &Self) -> bool {
139 unsafe {
140 let a = std::ptr::from_ref(self).cast::<u64>().read_unaligned();
141 let b = std::ptr::from_ref(other).cast::<u64>().read_unaligned();
142
143 if a != b {
144 return false;
145 }
146 }
147
148 self.get(PREFIX_SIZE..).unwrap_or_default() == other.get(PREFIX_SIZE..).unwrap_or_default()
151 }
152}
153
154impl std::cmp::Ord for ByteView {
155 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
156 self.prefix().cmp(other.prefix()).then_with(|| {
157 self.get(PREFIX_SIZE..)
158 .unwrap_or_default()
159 .cmp(other.get(PREFIX_SIZE..).unwrap_or_default())
160 })
161 }
162}
163
164impl std::cmp::PartialOrd for ByteView {
165 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
166 Some(self.cmp(other))
167 }
168}
169
170impl std::fmt::Debug for ByteView {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 write!(f, "{:?}", &**self)
173 }
174}
175
176impl Deref for ByteView {
177 type Target = [u8];
178
179 fn deref(&self) -> &Self::Target {
180 if self.is_inline() {
181 self.get_short_slice()
182 } else {
183 self.get_long_slice()
184 }
185 }
186}
187
188impl std::hash::Hash for ByteView {
189 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
190 self.deref().hash(state);
191 }
192}
193
194pub struct Mutator<'a>(pub(crate) &'a mut ByteView);
197
198impl std::ops::Deref for Mutator<'_> {
199 type Target = [u8];
200
201 fn deref(&self) -> &Self::Target {
202 self.0
203 }
204}
205
206impl std::ops::DerefMut for Mutator<'_> {
207 fn deref_mut(&mut self) -> &mut Self::Target {
208 self.0.get_mut_slice()
209 }
210}
211
212impl Drop for Mutator<'_> {
213 fn drop(&mut self) {
214 self.0.update_prefix();
215 }
216}
217
218impl ByteView {
219 #[doc(hidden)]
220 #[must_use]
221 pub unsafe fn builder_unzeroed(len: usize) -> Builder {
222 unsafe { Builder::new(Self::with_size_unzeroed(len)) }
225 }
226
227 #[doc(hidden)]
228 #[must_use]
229 pub fn builder(len: usize) -> Builder {
230 Builder::new(Self::with_size(len))
231 }
232
233 fn prefix(&self) -> &[u8] {
234 let len = PREFIX_SIZE.min(self.len());
235
236 unsafe { self.trailer.short.data.get_unchecked(..len) }
238 }
239
240 fn is_inline(&self) -> bool {
241 self.len() <= INLINE_SIZE
242 }
243
244 pub(crate) fn update_prefix(&mut self) {
245 if !self.is_inline() {
246 unsafe {
247 let slice_ptr: &[u8] = &*self;
248 let slice_ptr = slice_ptr.as_ptr();
249
250 let prefix = (*self.trailer.long).prefix.as_mut_ptr();
251 std::ptr::copy_nonoverlapping(slice_ptr, prefix, PREFIX_SIZE);
252 }
253 }
254 }
255
256 pub fn get_mut(&mut self) -> Option<Mutator<'_>> {
258 if self.ref_count() == 1 {
259 Some(Mutator(self))
260 } else {
261 None
262 }
263 }
264
265 pub fn from_reader<R: std::io::Read>(reader: &mut R, len: usize) -> std::io::Result<Self> {
272 let mut s = unsafe { Self::with_size_unzeroed(len) };
276 {
277 let mut builder = Mutator(&mut s);
278 reader.read_exact(&mut builder)?;
279 }
280 Ok(s)
281 }
282
283 #[must_use]
285 pub fn fused(left: &[u8], right: &[u8]) -> Self {
286 let len = left.len() + right.len();
287 let mut builder = unsafe { Self::builder_unzeroed(len) };
288 let (left_target, right_target) = builder.split_at_mut(left.len());
289 left_target.copy_from_slice(left);
290 right_target.copy_from_slice(right);
291 builder.freeze()
292 }
293
294 #[must_use]
302 pub fn with_size(slice_len: usize) -> Self {
303 Self::with_size_zeroed(slice_len)
304 }
305
306 fn with_size_zeroed(slice_len: usize) -> Self {
312 let view = if slice_len <= INLINE_SIZE {
313 Self {
314 trailer: Trailer {
315 short: ManuallyDrop::new(ShortRepr {
316 #[allow(clippy::cast_possible_truncation)]
319 len: slice_len as u32,
320 data: [0; INLINE_SIZE],
321 }),
322 },
323 }
324 } else {
325 let Ok(len) = u32::try_from(slice_len) else {
326 panic!("byte slice too long");
327 };
328
329 unsafe {
330 let layout = allocation_layout(slice_len);
331
332 let heap_ptr = std::alloc::alloc_zeroed(layout);
334 if heap_ptr.is_null() {
335 std::alloc::handle_alloc_error(layout);
336 }
337
338 #[expect(
340 clippy::cast_ptr_alignment,
341 reason = "the allocation uses HeapAllocationHeader alignment"
342 )]
343 let heap_region = heap_ptr.cast::<HeapAllocationHeader>();
344 let heap_region = &*heap_region;
345 heap_region.ref_count.store(1, Ordering::Release);
346
347 Self {
348 trailer: Trailer {
349 long: ManuallyDrop::new(LongRepr {
350 len,
351 prefix: [0; PREFIX_SIZE],
352 heap: heap_ptr,
353 original_len: len,
354 offset: 0,
355 }),
356 },
357 }
358 }
359 };
360
361 debug_assert_eq!(1, view.ref_count());
362
363 view
364 }
365
366 #[doc(hidden)]
372 #[must_use]
373 pub unsafe fn with_size_unzeroed(slice_len: usize) -> Self {
374 let view = if slice_len <= INLINE_SIZE {
375 Self {
376 trailer: Trailer {
377 short: ManuallyDrop::new(ShortRepr {
378 #[allow(clippy::cast_possible_truncation)]
381 len: slice_len as u32,
382 data: [0; INLINE_SIZE],
383 }),
384 },
385 }
386 } else {
387 let Ok(len) = u32::try_from(slice_len) else {
388 panic!("byte slice too long");
389 };
390
391 unsafe {
392 let layout = allocation_layout(slice_len);
393
394 let heap_ptr = std::alloc::alloc(layout);
395 if heap_ptr.is_null() {
396 std::alloc::handle_alloc_error(layout);
397 }
398
399 #[expect(
401 clippy::cast_ptr_alignment,
402 reason = "the allocation uses HeapAllocationHeader alignment"
403 )]
404 let heap_region = heap_ptr.cast::<HeapAllocationHeader>();
405 let heap_region = &*heap_region;
406 heap_region.ref_count.store(1, Ordering::Release);
407
408 Self {
409 trailer: Trailer {
410 long: ManuallyDrop::new(LongRepr {
411 len,
412 prefix: [0; PREFIX_SIZE],
413 heap: heap_ptr,
414 original_len: len,
415 offset: 0,
416 }),
417 },
418 }
419 }
420 };
421
422 debug_assert_eq!(1, view.ref_count());
423
424 view
425 }
426
427 #[must_use]
435 pub fn new(slice: &[u8]) -> Self {
436 let slice_len = slice.len();
437
438 let mut view = unsafe { Self::with_size_unzeroed(slice_len) };
439
440 if view.is_inline() {
441 unsafe {
444 let data_ptr = std::ptr::addr_of_mut!((*view.trailer.short).data).cast();
445 std::ptr::copy_nonoverlapping(slice.as_ptr(), data_ptr, slice_len);
446 }
447 } else {
448 let long_repr = unsafe { &mut *view.trailer.long };
449
450 #[allow(clippy::indexing_slicing)]
453 long_repr.prefix.copy_from_slice(&slice[0..PREFIX_SIZE]);
454
455 view.get_mut_slice().copy_from_slice(slice);
457 }
458
459 debug_assert_eq!(1, view.ref_count());
460
461 view
462 }
463
464 unsafe fn data_ptr(&self) -> *const u8 {
465 const HEADER_SIZE: usize = std::mem::size_of::<HeapAllocationHeader>();
466
467 debug_assert!(!self.is_inline());
468
469 unsafe {
472 self.trailer
473 .long
474 .heap
475 .add(HEADER_SIZE)
476 .add(self.trailer.long.offset as usize)
477 }
478 }
479
480 unsafe fn data_ptr_mut(&mut self) -> *mut u8 {
481 const HEADER_SIZE: usize = std::mem::size_of::<HeapAllocationHeader>();
482
483 debug_assert!(!self.is_inline());
484
485 unsafe {
488 self.trailer
489 .long
490 .heap
491 .add(HEADER_SIZE)
492 .add(self.trailer.long.offset as usize)
493 .cast_mut()
494 }
495 }
496
497 fn get_heap_region(&self) -> &HeapAllocationHeader {
498 debug_assert!(
499 !self.is_inline(),
500 "inline slice does not have a heap allocation"
501 );
502
503 unsafe {
504 let ptr = self.trailer.long.heap;
505 #[expect(
506 clippy::cast_ptr_alignment,
507 reason = "heap pointers come from a HeapAllocationHeader-aligned allocation"
508 )]
509 let heap_region: *const HeapAllocationHeader = ptr.cast::<HeapAllocationHeader>();
510 &*heap_region
511 }
512 }
513
514 #[doc(hidden)]
516 #[must_use]
517 pub fn ref_count(&self) -> u64 {
518 if self.is_inline() {
519 1
520 } else {
521 self.get_heap_region().ref_count.load(Ordering::Acquire)
522 }
523 }
524
525 #[must_use]
527 pub fn to_detached(&self) -> Self {
528 Self::new(self)
529 }
530
531 #[must_use]
546 pub fn slice(&self, range: impl std::ops::RangeBounds<usize>) -> Self {
547 use core::ops::Bound;
548
549 let self_len = self.len();
553
554 let begin = match range.start_bound() {
555 Bound::Included(&n) => n,
556 Bound::Excluded(&n) => n
557 .checked_add(1)
558 .unwrap_or_else(|| panic!("range start out of bounds")),
559 Bound::Unbounded => 0,
560 };
561
562 let end = match range.end_bound() {
563 Bound::Included(&n) => n
564 .checked_add(1)
565 .unwrap_or_else(|| panic!("range end out of bounds")),
566 Bound::Excluded(&n) => n,
567 Bound::Unbounded => self_len,
568 };
569
570 assert!(
571 begin <= end,
572 "range start must not be greater than end: {begin:?} <= {end:?}",
573 );
574 assert!(
575 end <= self_len,
576 "range end out of bounds: {end:?} <= {self_len:?}",
577 );
578
579 let new_len = end - begin;
580 let Ok(len) = u32::try_from(new_len) else {
581 unreachable!("a ByteView range always fits in u32");
582 };
583 let Ok(begin_u32) = u32::try_from(begin) else {
584 unreachable!("a ByteView offset always fits in u32");
585 };
586
587 if new_len <= INLINE_SIZE {
591 let mut child = Self {
592 trailer: Trailer {
593 short: ManuallyDrop::new(ShortRepr {
594 len,
595 data: [0; INLINE_SIZE],
596 }),
597 },
598 };
599
600 let Some(slice) = self.get(begin..end) else {
601 unreachable!("range was validated above");
602 };
603 debug_assert_eq!(slice.len(), new_len);
604
605 let data_ptr = unsafe { &mut (*child.trailer.short).data };
606
607 unsafe {
608 std::ptr::copy_nonoverlapping(slice.as_ptr(), data_ptr.as_mut_ptr(), new_len);
609 }
610
611 child
612 } else {
613 let heap_region = self.get_heap_region();
615 heap_region.ref_count.fetch_add(1, Ordering::Relaxed);
616
617 let mut child = Self {
618 trailer: Trailer {
622 long: ManuallyDrop::new(LongRepr {
623 len,
624 prefix: [0; PREFIX_SIZE],
625 heap: unsafe { self.trailer.long.heap },
626 offset: unsafe { self.trailer.long.offset } + begin_u32,
627 original_len: unsafe { self.trailer.long.original_len },
628 }),
629 },
630 };
631
632 let Some(prefix) = self.get(begin..(begin + PREFIX_SIZE)) else {
633 unreachable!("non-inline ranges contain a full prefix");
634 };
635 debug_assert_eq!(prefix.len(), 4);
636
637 unsafe {
638 (*child.trailer.long).prefix.copy_from_slice(prefix);
639 }
640
641 child
642 }
643 }
644
645 pub fn starts_with<T: AsRef<[u8]>>(&self, needle: T) -> bool {
647 let needle = needle.as_ref();
648 let prefix_len = PREFIX_SIZE.min(needle.len());
649
650 unsafe {
651 let needle_prefix: &[u8] = needle.get_unchecked(..prefix_len);
652
653 if !self.prefix().starts_with(needle_prefix) {
654 return false;
655 }
656 }
657
658 if needle.len() <= PREFIX_SIZE {
659 true
660 } else {
661 self.get(PREFIX_SIZE..)
662 .zip(needle.get(PREFIX_SIZE..))
663 .is_some_and(|(bytes, remaining)| bytes.starts_with(remaining))
664 }
665 }
666
667 #[must_use]
669 pub fn is_empty(&self) -> bool {
670 self.len() == 0
671 }
672
673 #[must_use]
675 pub fn len(&self) -> usize {
676 unsafe { self.trailer.short.len as usize }
677 }
678
679 pub(crate) fn get_mut_slice(&mut self) -> &mut [u8] {
680 let len = self.len();
681
682 if self.is_inline() {
683 unsafe { std::slice::from_raw_parts_mut((*self.trailer.short).data.as_mut_ptr(), len) }
684 } else {
685 unsafe { std::slice::from_raw_parts_mut(self.data_ptr_mut(), len) }
686 }
687 }
688
689 fn get_short_slice(&self) -> &[u8] {
690 let len = self.len();
691
692 debug_assert!(
693 len <= INLINE_SIZE,
694 "cannot get short slice - slice is not inlined",
695 );
696
697 unsafe { std::slice::from_raw_parts((*self.trailer.short).data.as_ptr(), len) }
699 }
700
701 fn get_long_slice(&self) -> &[u8] {
702 let len = self.len();
703
704 debug_assert!(
705 len > INLINE_SIZE,
706 "cannot get long slice - slice is inlined"
707 );
708
709 unsafe { std::slice::from_raw_parts(self.data_ptr(), len) }
711 }
712}
713
714impl std::borrow::Borrow<[u8]> for ByteView {
715 fn borrow(&self) -> &[u8] {
716 self
717 }
718}
719
720impl AsRef<[u8]> for ByteView {
721 fn as_ref(&self) -> &[u8] {
722 self
723 }
724}
725
726impl FromIterator<u8> for ByteView {
727 fn from_iter<T>(iter: T) -> Self
728 where
729 T: IntoIterator<Item = u8>,
730 {
731 Self::from(iter.into_iter().collect::<Vec<u8>>())
732 }
733}
734
735impl From<&[u8]> for ByteView {
736 fn from(value: &[u8]) -> Self {
737 Self::new(value)
738 }
739}
740
741impl From<Arc<[u8]>> for ByteView {
742 fn from(value: Arc<[u8]>) -> Self {
743 Self::new(&value)
744 }
745}
746
747impl From<Vec<u8>> for ByteView {
748 fn from(value: Vec<u8>) -> Self {
749 Self::new(&value)
750 }
751}
752
753impl From<&str> for ByteView {
754 fn from(value: &str) -> Self {
755 Self::from(value.as_bytes())
756 }
757}
758
759impl From<String> for ByteView {
760 fn from(value: String) -> Self {
761 Self::from(value.as_bytes())
762 }
763}
764
765impl From<Arc<str>> for ByteView {
766 fn from(value: Arc<str>) -> Self {
767 Self::from(&*value)
768 }
769}
770
771impl<const N: usize> From<[u8; N]> for ByteView {
772 fn from(value: [u8; N]) -> Self {
773 Self::from(value.as_slice())
774 }
775}
776
777impl<const N: usize> From<&[u8; N]> for ByteView {
778 fn from(value: &[u8; N]) -> Self {
779 Self::from(value.as_slice())
780 }
781}
782
783#[cfg(feature = "serde")]
784mod serde {
785 use super::ByteView;
786 use serde::de::{self, Visitor};
787 use serde::{Deserialize, Deserializer, Serialize, Serializer};
788 use std::fmt;
789
790 impl Serialize for ByteView {
791 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
792 where
793 S: Serializer,
794 {
795 serializer.serialize_bytes(self)
796 }
797 }
798
799 impl<'de> Deserialize<'de> for ByteView {
800 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
801 where
802 D: Deserializer<'de>,
803 {
804 struct ByteViewVisitor;
805
806 impl<'de> Visitor<'de> for ByteViewVisitor {
807 type Value = ByteView;
808
809 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
810 formatter.write_str("a byte array")
811 }
812
813 fn visit_bytes<E>(self, v: &[u8]) -> Result<ByteView, E>
814 where
815 E: de::Error,
816 {
817 Ok(ByteView::new(v))
818 }
819
820 fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
821 where
822 A: de::SeqAccess<'de>,
823 {
824 let bytes: Vec<u8> =
825 Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
826
827 Ok(ByteView::new(&bytes))
828 }
829 }
830
831 deserializer.deserialize_bytes(ByteViewVisitor)
832 }
833 }
834}
835
836#[cfg(test)]
837mod tests {
838 use super::{ByteView, HeapAllocationHeader};
839 use std::io::Cursor;
840
841 #[test]
842 #[cfg(target_pointer_width = "64")]
843 fn memsize() {
844 use crate::byteview::{LongRepr, ShortRepr, Trailer};
845
846 assert_eq!(
847 std::mem::size_of::<ShortRepr>(),
848 std::mem::size_of::<LongRepr>()
849 );
850 assert_eq!(
851 std::mem::size_of::<Trailer>(),
852 std::mem::size_of::<LongRepr>()
853 );
854
855 assert_eq!(24, std::mem::size_of::<ByteView>());
856 assert_eq!(
857 32,
858 std::mem::size_of::<ByteView>() + std::mem::size_of::<HeapAllocationHeader>()
859 );
860 }
861
862 #[test]
863 fn sliced_clone() {
864 let s = ByteView::from([
865 1, 255, 255, 255, 251, 255, 255, 255, 255, 255, 1, 21, 255, 255, 255, 255, 5, 255, 255,
866 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 4, 3, 255,
867 255, 0, 0, 255, 0, 0, 0, 254, 2, 0, 0, 0, 5, 2, 42, 0, 0, 0, 1, 0, 0, 0, 44, 0, 0, 0,
868 2, 0, 0, 0,
869 ]);
870 let slice = s.slice(12..(12 + 21));
871
872 #[allow(clippy::redundant_clone)]
873 let cloned = slice.clone();
874
875 assert_eq!(slice.prefix(), cloned.prefix());
876 assert_eq!(slice, cloned);
877 }
878
879 #[test]
880 fn sized_slice_ref() {
881 let b = b"hello";
882 let _bytes = ByteView::from(b);
883 }
884
885 #[test]
886 fn fuse_empty() {
887 let bytes = ByteView::fused(&[], &[]);
888 assert_eq!(&*bytes, &[] as &[u8]);
889 }
890
891 #[test]
892 fn fuse_one() {
893 let bytes = ByteView::fused(b"abc", &[]);
894 assert_eq!(&*bytes, b"abc");
895 }
896
897 #[test]
898 fn fuse_two() {
899 let bytes = ByteView::fused(b"abc", b"def");
900 assert_eq!(&*bytes, b"abcdef");
901 }
902
903 #[test]
904 fn empty_slice() {
905 let bytes = ByteView::with_size_zeroed(0);
906 assert_eq!(&*bytes, &[] as &[u8]);
907 }
908
909 #[test]
910 fn dealloc_order() {
911 let bytes = ByteView::new(&(0..32).collect::<Vec<_>>());
912 let bytes_slice = bytes.slice(..31);
913 drop(bytes);
914 drop(bytes_slice);
915 }
916
917 #[test]
918 fn dealloc_order_2() {
919 let bytes = ByteView::new(&(0..32).collect::<Vec<_>>());
920 let bytes_slice = bytes.slice(..31);
921 let bytes_slice_2 = bytes.slice(..5);
922 let bytes_slice_3 = bytes.slice(..6);
923
924 drop(bytes);
925 drop(bytes_slice);
926 drop(bytes_slice_2);
927 drop(bytes_slice_3);
928 }
929
930 #[test]
931 fn from_reader_1() -> std::io::Result<()> {
932 let str = b"abcdef";
933 let mut cursor = Cursor::new(str);
934
935 let a = ByteView::from_reader(&mut cursor, 6)?;
936 assert_eq!(&*a, b"abcdef");
937
938 Ok(())
939 }
940
941 #[test]
942 fn cmp_misc_1() {
943 let a = ByteView::from("abcdef");
944 let b = ByteView::from("abcdefhelloworldhelloworld");
945 assert!(a < b);
946 }
947
948 #[test]
949 fn get_mut() {
950 let mut slice = ByteView::with_size(4);
951 assert_eq!(4, slice.len());
952 assert_eq!([0, 0, 0, 0], &*slice);
953
954 {
955 let Some(mut mutator) = slice.get_mut() else {
956 panic!("new ByteView must be uniquely owned");
957 };
958 mutator.copy_from_slice(&[1, 2, 3, 4]);
959 }
960
961 assert_eq!(4, slice.len());
962 assert_eq!([1, 2, 3, 4], &*slice);
963 assert_eq!([1, 2, 3, 4], slice.prefix());
964 }
965
966 #[test]
967 fn get_mut_long() {
968 let mut slice = ByteView::with_size(30);
969 assert_eq!(30, slice.len());
970 assert_eq!([0; 30], &*slice);
971
972 {
973 let Some(mut mutator) = slice.get_mut() else {
974 panic!("new ByteView must be uniquely owned");
975 };
976 let Some(prefix) = mutator.get_mut(..4) else {
977 panic!("ByteView is expected to contain four bytes");
978 };
979 prefix.copy_from_slice(&[1, 2, 3, 4]);
980 }
981
982 assert_eq!(30, slice.len());
983 assert_eq!(
984 [
985 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
986 0, 0
987 ],
988 &*slice
989 );
990 assert_eq!([1, 2, 3, 4], slice.prefix());
991 }
992
993 #[test]
994 fn nostr() {
995 let slice = ByteView::from("");
996 assert_eq!(0, slice.len());
997 assert_eq!(&*slice, b"");
998 assert_eq!(1, slice.ref_count());
999 assert!(slice.is_inline());
1000 }
1001
1002 #[test]
1003 fn default_str() {
1004 let slice = ByteView::default();
1005 assert_eq!(0, slice.len());
1006 assert_eq!(&*slice, b"");
1007 assert_eq!(1, slice.ref_count());
1008 assert!(slice.is_inline());
1009 }
1010
1011 #[test]
1012 fn short_str() {
1013 let slice = ByteView::from("abcdef");
1014 assert_eq!(6, slice.len());
1015 assert_eq!(&*slice, b"abcdef");
1016 assert_eq!(1, slice.ref_count());
1017 assert_eq!(&slice.prefix(), b"abcd");
1018 assert!(slice.is_inline());
1019 }
1020
1021 #[test]
1022 #[cfg(target_pointer_width = "64")]
1023 fn medium_str() {
1024 let slice = ByteView::from("abcdefabcdef");
1025 assert_eq!(12, slice.len());
1026 assert_eq!(&*slice, b"abcdefabcdef");
1027 assert_eq!(1, slice.ref_count());
1028 assert_eq!(&slice.prefix(), b"abcd");
1029 assert!(slice.is_inline());
1030 }
1031
1032 #[test]
1033 #[cfg(target_pointer_width = "64")]
1034 fn medium_long_str() {
1035 let slice = ByteView::from("abcdefabcdefabcdabcd");
1036 assert_eq!(20, slice.len());
1037 assert_eq!(&*slice, b"abcdefabcdefabcdabcd");
1038 assert_eq!(1, slice.ref_count());
1039 assert_eq!(&slice.prefix(), b"abcd");
1040 assert!(slice.is_inline());
1041 }
1042
1043 #[test]
1044 #[cfg(target_pointer_width = "64")]
1045 fn medium_str_clone() {
1046 let slice = ByteView::from("abcdefabcdefabcdefab");
1047 let copy = slice.clone();
1048 assert_eq!(slice, copy);
1049 assert_eq!(copy.prefix(), slice.prefix());
1050
1051 assert_eq!(1, slice.ref_count());
1052
1053 drop(copy);
1054 assert_eq!(1, slice.ref_count());
1055 }
1056
1057 #[test]
1058 fn long_str() {
1059 let slice = ByteView::from("abcdefabcdefabcdefababcd");
1060 assert_eq!(24, slice.len());
1061 assert_eq!(&*slice, b"abcdefabcdefabcdefababcd");
1062 assert_eq!(1, slice.ref_count());
1063 assert_eq!(&slice.prefix(), b"abcd");
1064 assert!(!slice.is_inline());
1065 }
1066
1067 #[test]
1068 fn long_str_clone() {
1069 let slice = ByteView::from("abcdefabcdefabcdefababcd");
1070 let copy = slice.clone();
1071 assert_eq!(slice, copy);
1072 assert_eq!(copy.prefix(), slice.prefix());
1073
1074 assert_eq!(2, slice.ref_count());
1075
1076 drop(copy);
1077 assert_eq!(1, slice.ref_count());
1078 }
1079
1080 #[test]
1081 fn long_str_slice_full() {
1082 let slice = ByteView::from("helloworld_thisisalongstring");
1083
1084 let copy = slice.slice(..);
1085 assert_eq!(copy, slice);
1086
1087 assert_eq!(2, slice.ref_count());
1088
1089 drop(copy);
1090 assert_eq!(1, slice.ref_count());
1091 }
1092
1093 #[test]
1094 #[cfg(target_pointer_width = "64")]
1095 fn long_str_slice() {
1096 let slice = ByteView::from("helloworld_thisisalongstring");
1097
1098 let copy = slice.slice(11..);
1099 assert_eq!(b"thisisalongstring", &*copy);
1100 assert_eq!(©.prefix(), b"this");
1101
1102 assert_eq!(1, slice.ref_count());
1103
1104 drop(copy);
1105 assert_eq!(1, slice.ref_count());
1106 }
1107
1108 #[test]
1109 #[cfg(target_pointer_width = "64")]
1110 fn long_str_slice_twice() {
1111 let slice = ByteView::from("helloworld_thisisalongstring");
1112
1113 let copy = slice.slice(11..);
1114 assert_eq!(b"thisisalongstring", &*copy);
1115
1116 let copycopy = copy.slice(..);
1117 assert_eq!(copy, copycopy);
1118
1119 assert_eq!(1, slice.ref_count());
1120
1121 drop(copy);
1122 assert_eq!(1, slice.ref_count());
1123
1124 drop(slice);
1125 assert_eq!(1, copycopy.ref_count());
1126 }
1127
1128 #[test]
1129 #[cfg(target_pointer_width = "64")]
1130 fn long_str_slice_downgrade() {
1131 let slice = ByteView::from("helloworld_thisisalongstring");
1132
1133 let copy = slice.slice(11..);
1134 assert_eq!(b"thisisalongstring", &*copy);
1135
1136 let copycopy = copy.slice(0..4);
1137 assert_eq!(b"this", &*copycopy);
1138
1139 {
1140 let copycopy = copy.slice(0..=4);
1141 assert_eq!(b"thisi", &*copycopy);
1142 assert_eq!(Some(b't'), copycopy.first().copied());
1143 }
1144
1145 assert_eq!(1, slice.ref_count());
1146
1147 drop(copy);
1148 assert_eq!(1, slice.ref_count());
1149
1150 drop(copycopy);
1151 assert_eq!(1, slice.ref_count());
1152 }
1153
1154 #[test]
1155 fn short_str_clone() {
1156 let slice = ByteView::from("abcdef");
1157 let copy = slice.clone();
1158 assert_eq!(slice, copy);
1159
1160 assert_eq!(1, slice.ref_count());
1161
1162 drop(slice);
1163 assert_eq!(&*copy, b"abcdef");
1164
1165 assert_eq!(1, copy.ref_count());
1166 }
1167
1168 #[test]
1169 fn short_str_slice_full() {
1170 let slice = ByteView::from("abcdef");
1171 let copy = slice.slice(..);
1172 assert_eq!(slice, copy);
1173
1174 assert_eq!(1, slice.ref_count());
1175
1176 drop(slice);
1177 assert_eq!(&*copy, b"abcdef");
1178
1179 assert_eq!(1, copy.ref_count());
1180 }
1181
1182 #[test]
1183 fn short_str_slice_part() {
1184 let slice = ByteView::from("abcdef");
1185 let copy = slice.slice(3..);
1186
1187 assert_eq!(1, slice.ref_count());
1188
1189 drop(slice);
1190 assert_eq!(&*copy, b"def");
1191
1192 assert_eq!(1, copy.ref_count());
1193 }
1194
1195 #[test]
1196 fn short_str_slice_empty() {
1197 let slice = ByteView::from("abcdef");
1198 let copy = slice.slice(0..0);
1199
1200 assert_eq!(1, slice.ref_count());
1201
1202 drop(slice);
1203 assert_eq!(&*copy, b"");
1204
1205 assert_eq!(1, copy.ref_count());
1206 }
1207
1208 #[test]
1209 fn tiny_str_starts_with() {
1210 let a = ByteView::from("abc");
1211 assert!(a.starts_with(b"ab"));
1212 assert!(!a.starts_with(b"b"));
1213 }
1214
1215 #[test]
1216 fn long_str_starts_with() {
1217 let a = ByteView::from("abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef");
1218 assert!(a.starts_with(b"abcdef"));
1219 assert!(!a.starts_with(b"def"));
1220 }
1221
1222 #[test]
1223 fn tiny_str_cmp() {
1224 let a = ByteView::from("abc");
1225 let b = ByteView::from("def");
1226 assert!(a < b);
1227 }
1228
1229 #[test]
1230 fn tiny_str_eq() {
1231 let a = ByteView::from("abc");
1232 let b = ByteView::from("def");
1233 assert_ne!(a, b);
1234 }
1235
1236 #[test]
1237 fn long_str_eq() {
1238 let a = ByteView::from("abcdefabcdefabcdefabcdef");
1239 let b = ByteView::from("xycdefabcdefabcdefabcdef");
1240 assert_ne!(a, b);
1241 }
1242
1243 #[test]
1244 fn long_str_cmp() {
1245 let a = ByteView::from("abcdefabcdefabcdefabcdef");
1246 let b = ByteView::from("xycdefabcdefabcdefabcdef");
1247 assert!(a < b);
1248 }
1249
1250 #[test]
1251 fn long_str_eq_2() {
1252 let a = ByteView::from("abcdefabcdefabcdefabcdef");
1253 let b = ByteView::from("abcdefabcdefabcdefabcdef");
1254 assert_eq!(a, b);
1255 }
1256
1257 #[test]
1258 fn long_str_cmp_2() {
1259 let a = ByteView::from("abcdefabcdefabcdefabcdef");
1260 let b = ByteView::from("abcdefabcdefabcdefabcdeg");
1261 assert!(a < b);
1262 }
1263
1264 #[test]
1265 fn long_str_cmp_3() {
1266 let a = ByteView::from("abcdefabcdefabcdefabcde");
1267 let b = ByteView::from("abcdefabcdefabcdefabcdef");
1268 assert!(a < b);
1269 }
1270
1271 #[test]
1272 fn cmp_fuzz_1() {
1273 let a = ByteView::from([0]);
1274 let b = ByteView::from([]);
1275
1276 assert!(a > b);
1277 assert_ne!(a, b);
1278 }
1279
1280 #[test]
1281 fn cmp_fuzz_2() {
1282 let a = ByteView::from([0, 0]);
1283 let b = ByteView::from([0]);
1284
1285 assert!(a > b);
1286 assert_ne!(a, b);
1287 }
1288
1289 #[test]
1290 fn cmp_fuzz_3() {
1291 let a = ByteView::from([255, 255, 12, 255, 0]);
1292 let b = ByteView::from([255, 255, 12, 255]);
1293
1294 assert!(a > b);
1295 assert_ne!(a, b);
1296 }
1297
1298 #[test]
1299 fn cmp_fuzz_4() {
1300 let a = ByteView::from([
1301 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1302 ]);
1303 let b = ByteView::from([
1304 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
1305 ]);
1306
1307 assert!(a > b);
1308 assert_ne!(a, b);
1309 }
1310}