Skip to main content

byteview/
byteview.rs

1// Copyright (c) 2024-present, fjall-rs
2// This source code is licensed under both the Apache 2.0 and MIT License
3// (found in the LICENSE-* files in the repository)
4
5use 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/// An immutable byte slice
73///
74/// Will be inlined (no pointer dereference or heap allocation)
75/// if it is 20 characters or shorter (on a 64-bit system).
76///
77/// A single heap allocation will be shared between multiple slices.
78/// Even subslices of that heap allocation can be cloned without additional heap allocation.
79///
80/// [`ByteView`] does not guarantee any sort of alignment for zero-copy (de)serialization.
81///
82/// The design is very similar to:
83///
84/// - [Polars' strings](<https://pola.rs/posts/polars-string-type>)
85/// - [CedarDB's German strings](<https://cedardb.com/blog/german_strings>)
86/// - [Umbra's string](<https://db.in.tum.de/~freitag/papers/p29-neumann-cidr20.pdf>)
87/// - [Velox' String View](https://facebookincubator.github.io/velox/develop/vectors.html)
88/// - [Apache Arrow's String View](https://arrow.apache.org/docs/cpp/api/datatype.html#_CPPv4N5arrow14BinaryViewType6c_typeE)
89#[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        // SAFETY: Inline views own no external resource. Heap views share their
109        // allocation, whose reference count was incremented above.
110        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        // The first word contains the length and cached four-byte prefix.
149        // Compare only the bytes that were not already checked.
150        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
194/// RAII guard for [`ByteView::get_mut`], so the prefix gets
195/// updated properly when the mutation is done
196pub 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        // SAFETY: The caller is responsible for initializing every byte before
223        // the returned builder is frozen.
224        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        // SAFETY: Both trailer layouts have the prefix stored at the same position
237        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    /// Returns a mutable reference into the given byteview, if there are no other pointers to the same allocation.
257    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    /// Creates a byteview and populates it with `len` bytes
266    /// from the given reader.
267    ///
268    /// # Errors
269    ///
270    /// Returns an error if an I/O error occurred.
271    pub fn from_reader<R: std::io::Read>(reader: &mut R, len: usize) -> std::io::Result<Self> {
272        // NOTE: We can use _unzeroed to skip zeroing of the heap allocated slice
273        // because we receive the `len` parameter
274        // If the reader does not give us exactly `len` bytes, `read_exact` fails anyway
275        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    /// Fuses two byte slices into a single byteview.
284    #[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    /// Creates a new zeroed, fixed-length byteview.
295    ///
296    /// Use [`ByteView::get_mut`] to mutate the content.
297    ///
298    /// # Panics
299    ///
300    /// Panics if the length does not fit in a u32 (4 GiB).
301    #[must_use]
302    pub fn with_size(slice_len: usize) -> Self {
303        Self::with_size_zeroed(slice_len)
304    }
305
306    /// Creates a new zeroed, fixed-length byteview.
307    ///
308    /// # Panics
309    ///
310    /// Panics if the length does not fit in a u32 (4 GiB).
311    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                        // SAFETY: We know slice_len is INLINE_SIZE or less, so it must be
317                        // a valid u32
318                        #[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                // IMPORTANT: Zero-allocate the region
333                let heap_ptr = std::alloc::alloc_zeroed(layout);
334                if heap_ptr.is_null() {
335                    std::alloc::handle_alloc_error(layout);
336                }
337
338                // Set ref count
339                #[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    /// Creates a new fixed-length byteview, **with uninitialized contents**.
367    ///
368    /// # Panics
369    ///
370    /// Panics if the length does not fit in a u32 (4 GiB).
371    #[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                        // SAFETY: We know slice_len is INLINE_SIZE or less, so it must be
379                        // a valid u32
380                        #[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                // Set ref count
400                #[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    /// Creates a new byteview from an existing byte slice.
428    ///
429    /// Will heap-allocate the slice if it has at least length 21.
430    ///
431    /// # Panics
432    ///
433    /// Panics if the length does not fit in a u32 (4 GiB).
434    #[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            // SAFETY: We check for inlinability
442            // so we know the the input slice fits our buffer
443            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            // Copy prefix
451            // SAFETY: We know that there are at least 4 bytes in the input slice
452            #[allow(clippy::indexing_slicing)]
453            long_repr.prefix.copy_from_slice(&slice[0..PREFIX_SIZE]);
454
455            // Copy byte slice into heap allocation
456            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        // SAFETY: The non-inline representation is active, and its allocation
470        // contains the header followed by `original_len` data bytes.
471        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        // SAFETY: The non-inline representation is active, and its allocation
486        // contains the header followed by `original_len` data bytes.
487        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    /// Returns the ref_count of the underlying heap allocation.
515    #[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    /// Clones the contents of this slice into an independently tracked slice.
526    #[must_use]
527    pub fn to_detached(&self) -> Self {
528        Self::new(self)
529    }
530
531    /// Clones the given range of the existing byteview without heap allocation.
532    ///
533    /// # Examples
534    ///
535    /// ```
536    /// # use byteview::ByteView;
537    /// let slice = ByteView::from("helloworld_thisisalongstring");
538    /// let copy = slice.slice(11..);
539    /// assert_eq!(b"thisisalongstring", &*copy);
540    /// ```
541    ///
542    /// # Panics
543    ///
544    /// Panics if the slice is out of bounds.
545    #[must_use]
546    pub fn slice(&self, range: impl std::ops::RangeBounds<usize>) -> Self {
547        use core::ops::Bound;
548
549        // Credits: This is essentially taken from
550        // https://github.com/tokio-rs/bytes/blob/291df5acc94b82a48765e67eeb1c1a2074539e68/src/bytes.rs#L264
551
552        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        // Target and destination slices are inlined
588        // so we just need to memcpy the struct, and replace
589        // the inline slice with the requested range
590        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            // IMPORTANT: Increase ref count
614            let heap_region = self.get_heap_region();
615            heap_region.ref_count.fetch_add(1, Ordering::Relaxed);
616
617            let mut child = Self {
618                // SAFETY: self.data must be defined
619                // we cannot get a range larger than our own slice
620                // so we cannot be inlined while the requested slice is not inlinable
621                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    /// Returns `true` if `needle` is a prefix of the slice or equal to the slice.
646    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    /// Returns `true` if the slice is empty.
668    #[must_use]
669    pub fn is_empty(&self) -> bool {
670        self.len() == 0
671    }
672
673    /// Returns the amount of bytes in the slice.
674    #[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        // SAFETY: Shall only be called if slice is inlined
698        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        // SAFETY: Shall only be called if slice is heap allocated
710        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!(&copy.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}