Skip to main content

vortex_buffer/
buffer.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::any::type_name;
5use std::cmp::Ordering;
6use std::collections::Bound;
7use std::fmt::Debug;
8use std::fmt::Formatter;
9use std::hash::Hash;
10use std::hash::Hasher;
11use std::ops::Deref;
12use std::ops::RangeBounds;
13use std::ptr::NonNull;
14use std::sync::Arc;
15
16use bytes::Buf;
17use bytes::Bytes;
18use vortex_error::VortexExpect;
19use vortex_error::vortex_panic;
20
21use crate::Alignment;
22use crate::Allocation;
23use crate::BufferAllocatorRef;
24use crate::BufferBacking;
25use crate::BufferMut;
26use crate::ByteBuffer;
27use crate::debug::TruncatedDebug;
28use crate::trusted_len::TrustedLen;
29
30/// An immutable buffer of items of `T`.
31///
32/// Zero-sized element types are rejected at compile time when constructing a buffer.
33///
34/// ```compile_fail
35/// use vortex_buffer::Buffer;
36/// let _ = Buffer::<()>::empty();
37/// ```
38///
39/// ```compile_fail
40/// use vortex_buffer::Buffer;
41/// let _ = Buffer::from(vec![(); 3]);
42/// ```
43///
44/// ```compile_fail
45/// use vortex_buffer::{Buffer, ByteBuffer};
46/// let _ = Buffer::<()>::from_byte_buffer(ByteBuffer::empty());
47/// ```
48///
49/// ```compile_fail
50/// use bytes::Bytes;
51/// use vortex_buffer::{Alignment, Buffer};
52/// let _ = Buffer::<()>::from_bytes_aligned(Bytes::new(), Alignment::none());
53/// ```
54#[derive(Clone)]
55pub struct Buffer<T> {
56    /// The first element in this view; may dangle for an empty buffer.
57    pub(crate) ptr: NonNull<T>,
58    /// The number of initialized `T` values visible from `ptr`.
59    pub(crate) length: usize,
60    /// The minimum alignment promised for `ptr` and preserved by aligned slices.
61    pub(crate) alignment: Alignment,
62    /// Shared ownership of the storage containing `ptr`, if any; `Buffer::empty` has no backing.
63    pub(crate) backing: Option<Arc<BufferBacking>>,
64}
65
66// SAFETY: Buffer is an immutable view over backing memory. Its pointer remains valid while the
67// backing is live, and sharing elements follows the same bounds as sharing a slice.
68unsafe impl<T: Send> Send for Buffer<T> {}
69// SAFETY: see the Send implementation above.
70unsafe impl<T: Sync> Sync for Buffer<T> {}
71
72impl<T> Default for Buffer<T> {
73    fn default() -> Self {
74        Self {
75            ptr: empty_ptr(),
76            length: 0,
77            alignment: Alignment::of::<T>(),
78            backing: None,
79        }
80    }
81}
82
83impl<T: PartialEq> PartialEq for Buffer<T> {
84    #[inline]
85    fn eq(&self, other: &Self) -> bool {
86        self.as_slice() == other.as_slice()
87    }
88}
89
90impl<T: Eq> Eq for Buffer<T> {}
91
92impl<T: Ord> Ord for Buffer<T> {
93    #[inline]
94    fn cmp(&self, other: &Self) -> Ordering {
95        self.as_slice().cmp(other.as_slice())
96    }
97}
98
99impl<T: PartialOrd> PartialOrd for Buffer<T> {
100    #[inline]
101    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
102        self.as_slice().partial_cmp(other.as_slice())
103    }
104}
105
106impl<T: Hash> Hash for Buffer<T> {
107    #[inline]
108    fn hash<H: Hasher>(&self, state: &mut H) {
109        self.as_slice().hash(state)
110    }
111}
112
113impl<T> Buffer<T> {
114    pub(crate) fn from_allocation(
115        allocation: Allocation,
116        offset: usize,
117        length: usize,
118        alignment: Alignment,
119    ) -> Self {
120        // SAFETY: BufferMut keeps offset within allocation, including for empty buffers.
121        let ptr = unsafe { allocation.ptr().add(offset).cast() };
122        Self {
123            ptr,
124            length,
125            alignment,
126            backing: Some(Arc::new(BufferBacking::Owned(allocation))),
127        }
128    }
129
130    fn from_bytes(bytes: Bytes, alignment: Alignment) -> Self {
131        let length = bytes.len() / size_of::<T>();
132        if length == 0 {
133            return Self::empty_aligned(alignment);
134        }
135        let ptr =
136            NonNull::new(bytes.as_ptr().cast_mut().cast()).vortex_expect("Bytes pointer is null");
137        Self {
138            ptr,
139            length,
140            alignment,
141            backing: Some(Arc::new(BufferBacking::Bytes(bytes))),
142        }
143    }
144
145    #[cfg(feature = "arrow")]
146    pub(crate) fn from_arrow_owner(
147        arrow: arrow_buffer::Buffer,
148        length: usize,
149        alignment: Alignment,
150    ) -> Self {
151        if length == 0 {
152            return Self::empty_aligned(alignment);
153        }
154        let ptr = NonNull::new(arrow.as_ptr().cast_mut().cast())
155            .vortex_expect("Arrow buffer pointer is null");
156        Self {
157            ptr,
158            length,
159            alignment,
160            backing: Some(Arc::new(BufferBacking::Arrow(arrow))),
161        }
162    }
163
164    /// Returns a new `Buffer<T>` copied from the provided `Vec<T>`, `&[T]`, etc.
165    ///
166    /// Due to our underlying usage of `bytes::Bytes`, we are unable to take zero-copy ownership
167    /// of the provided `Vec<T>` while maintaining the ability to convert it back into a mutable
168    /// buffer. We could fix this by forking `Bytes`, or in many other complex ways, but for now
169    /// callers should prefer to construct `Buffer<T>` from a `BufferMut<T>`.
170    pub fn copy_from(values: impl AsRef<[T]>) -> Self
171    where
172        T: Copy,
173    {
174        BufferMut::copy_from(values).freeze()
175    }
176
177    /// Returns a new `Buffer<T>` copied with the provided allocator.
178    pub fn copy_from_in(values: impl AsRef<[T]>, allocator: BufferAllocatorRef) -> Self
179    where
180        T: Copy,
181    {
182        BufferMut::copy_from_in(values, allocator).freeze()
183    }
184
185    /// Returns a new `Buffer<T>` copied from the provided slice and with the requested alignment.
186    ///
187    /// The allocation is over-aligned to [`Alignment::DEFAULT_ALIGNMENT`] when that is larger than
188    /// `alignment`. Use [`copy_from_preferred_aligned`] to control the over-alignment.
189    ///
190    /// [`copy_from_preferred_aligned`]: Self::copy_from_preferred_aligned
191    pub fn copy_from_aligned(values: impl AsRef<[T]>, alignment: Alignment) -> Self
192    where
193        T: Copy,
194    {
195        Self::copy_from_preferred_aligned(values, alignment, Some(Alignment::DEFAULT_ALIGNMENT))
196    }
197
198    /// Returns a new `Buffer<T>` copied from the provided slice and with the requested alignment.
199    ///
200    /// The buffer reports `alignment`, but the underlying allocation is over-aligned to the larger
201    /// of `alignment` and `preferred_alignment`.
202    pub fn copy_from_preferred_aligned(
203        values: impl AsRef<[T]>,
204        alignment: Alignment,
205        preferred_alignment: Option<Alignment>,
206    ) -> Self
207    where
208        T: Copy,
209    {
210        BufferMut::copy_from_preferred_aligned(values, alignment, preferred_alignment).freeze()
211    }
212
213    /// Create a new zeroed `Buffer` with the given value.
214    pub fn zeroed(len: usize) -> Self {
215        Self::zeroed_aligned(len, Alignment::of::<T>())
216    }
217
218    /// Create a new zeroed `Buffer` with the provided allocator.
219    pub fn zeroed_in(len: usize, allocator: BufferAllocatorRef) -> Self {
220        BufferMut::zeroed_in(len, allocator).freeze()
221    }
222
223    /// Create a new zeroed `Buffer` with the requested alignment.
224    ///
225    /// The allocation is over-aligned to [`Alignment::DEFAULT_ALIGNMENT`] when that is larger than
226    /// `alignment`. Use [`zeroed_preferred_aligned`] to control the over-alignment.
227    ///
228    /// [`zeroed_preferred_aligned`]: Self::zeroed_preferred_aligned
229    pub fn zeroed_aligned(len: usize, alignment: Alignment) -> Self {
230        Self::zeroed_preferred_aligned(len, alignment, Some(Alignment::DEFAULT_ALIGNMENT))
231    }
232
233    /// Create a new zeroed `Buffer` with the requested alignment.
234    ///
235    /// The buffer reports `alignment`, but the underlying allocation is over-aligned to the larger
236    /// of `alignment` and `preferred_alignment`.
237    pub fn zeroed_preferred_aligned(
238        len: usize,
239        alignment: Alignment,
240        preferred_alignment: Option<Alignment>,
241    ) -> Self {
242        BufferMut::zeroed_preferred_aligned(len, alignment, preferred_alignment).freeze()
243    }
244
245    /// Create a new empty `ByteBuffer` with the provided alignment.
246    pub fn empty() -> Self {
247        Self::empty_aligned(Alignment::of::<T>())
248    }
249
250    /// Create a new empty `ByteBuffer` with the provided alignment.
251    ///
252    /// This does not allocate. Empty buffers use an aligned dangling pointer.
253    pub fn empty_aligned(alignment: Alignment) -> Self {
254        const { assert!(size_of::<T>() != 0, "ZSTs are not supported") };
255        if !alignment.is_aligned_to(Alignment::of::<T>()) {
256            misaligned_scalar_type(alignment, Alignment::of::<T>());
257        }
258        Self {
259            ptr: empty_ptr(),
260            length: 0,
261            alignment,
262            backing: None,
263        }
264    }
265
266    /// Create a new full `ByteBuffer` with the given value.
267    pub fn full(item: T, len: usize) -> Self
268    where
269        T: Copy,
270    {
271        BufferMut::full(item, len).freeze()
272    }
273
274    /// Create a full `Buffer` with the given value and allocator.
275    pub fn full_in(item: T, len: usize, allocator: BufferAllocatorRef) -> Self
276    where
277        T: Copy,
278    {
279        BufferMut::full_in(item, len, allocator).freeze()
280    }
281
282    /// Create a `Buffer<T>` zero-copy from a `ByteBuffer`.
283    ///
284    /// ## Panics
285    ///
286    /// Panics if the buffer is not aligned to the size of `T`, or the length is not a multiple of
287    /// the size of `T`.
288    pub fn from_byte_buffer(buffer: ByteBuffer) -> Self {
289        // TODO(ngates): should this preserve the current alignment of the buffer?
290        Self::from_byte_buffer_aligned(buffer, Alignment::of::<T>())
291    }
292
293    /// Create a `Buffer<T>` zero-copy from a `ByteBuffer`.
294    ///
295    /// ## Panics
296    ///
297    /// Panics if the buffer is not aligned to the given alignment, if the length is not a multiple
298    /// of the size of `T`, or if the given alignment is not aligned to that of `T`.
299    pub fn from_byte_buffer_aligned(buffer: ByteBuffer, alignment: Alignment) -> Self {
300        const { assert!(size_of::<T>() != 0, "ZSTs are not supported") };
301        if !alignment.is_aligned_to(Alignment::of::<T>()) {
302            incompatible_scalar_alignment(alignment, Alignment::of::<T>());
303        }
304        if !alignment.is_ptr_aligned(buffer.as_ptr()) {
305            buffer_not_aligned(alignment);
306        }
307        if !buffer.len().is_multiple_of(size_of::<T>()) {
308            buffer_len_not_multiple(buffer.len(), size_of::<T>());
309        }
310        Self {
311            ptr: buffer.ptr.cast(),
312            length: buffer.length / size_of::<T>(),
313            alignment,
314            backing: buffer.backing,
315        }
316    }
317
318    /// Create a `Buffer<T>` zero-copy from a `Bytes`.
319    ///
320    /// ## Panics
321    ///
322    /// Panics if the buffer is not aligned to the size of `T`, or the length is not a multiple of
323    /// the size of `T`.
324    pub fn from_bytes_aligned(bytes: Bytes, alignment: Alignment) -> Self {
325        const { assert!(size_of::<T>() != 0, "ZSTs are not supported") };
326        if !alignment.is_aligned_to(Alignment::of::<T>()) {
327            incompatible_scalar_alignment(alignment, Alignment::of::<T>());
328        }
329        if !alignment.is_ptr_aligned(bytes.as_ptr()) {
330            bytes_not_aligned(alignment);
331        }
332        if !bytes.len().is_multiple_of(size_of::<T>()) {
333            bytes_len_not_multiple(bytes.len(), size_of::<T>());
334        }
335        Self::from_bytes(bytes, alignment)
336    }
337
338    /// Create a buffer with values from the TrustedLen iterator.
339    /// Should be preferred over `from_iter` when the iterator is known to be `TrustedLen`.
340    pub fn from_trusted_len_iter<I: TrustedLen<Item = T>>(iter: I) -> Self {
341        BufferMut::from_trusted_len_iter(iter).freeze()
342    }
343
344    /// Clear the buffer, preserving existing capacity.
345    pub fn clear(&mut self) {
346        self.length = 0;
347    }
348
349    /// Returns the length of the buffer in elements of type T.
350    #[allow(clippy::inline_always)]
351    #[inline(always)]
352    pub fn len(&self) -> usize {
353        self.length
354    }
355
356    /// Returns whether the buffer is empty.
357    #[allow(clippy::inline_always)]
358    #[inline(always)]
359    pub fn is_empty(&self) -> bool {
360        self.length == 0
361    }
362
363    /// Returns the alignment of the buffer.
364    #[allow(clippy::inline_always)]
365    #[inline(always)]
366    pub fn alignment(&self) -> Alignment {
367        self.alignment
368    }
369
370    pub(crate) fn allocator(&self) -> &BufferAllocatorRef {
371        match self.backing.as_deref() {
372            Some(backing) => backing.allocator(),
373            None => BufferAllocatorRef::static_ref(),
374        }
375    }
376
377    /// Returns a raw pointer to the buffer's data.
378    #[allow(clippy::inline_always)]
379    #[inline(always)]
380    pub fn as_ptr(&self) -> *const T {
381        self.ptr.as_ptr()
382    }
383
384    /// Returns a slice over the buffer of elements of type T.
385    #[allow(clippy::inline_always)]
386    #[inline(always)]
387    pub fn as_slice(&self) -> &[T] {
388        // SAFETY: ptr points into the live backing and construction checks its alignment.
389        unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.length) }
390    }
391
392    /// Return a view over the buffer as an opaque byte slice.
393    #[allow(clippy::inline_always)]
394    #[inline(always)]
395    pub fn as_bytes(&self) -> &[u8] {
396        // SAFETY: the element range is initialized and remains live through backing.
397        unsafe {
398            std::slice::from_raw_parts(self.ptr.as_ptr().cast(), size_of_val(self.as_slice()))
399        }
400    }
401
402    /// Returns an iterator over the buffer of elements of type T.
403    pub fn iter(&self) -> Iter<'_, T> {
404        Iter {
405            inner: self.as_slice().iter(),
406        }
407    }
408
409    /// Returns a slice of self for the provided range.
410    ///
411    /// # Panics
412    ///
413    /// Requires that `begin <= end` and `end <= self.len()`.
414    /// Also requires that both `begin` and `end` are aligned to the buffer's required alignment.
415    #[allow(clippy::inline_always)]
416    #[inline(always)]
417    pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
418        self.slice_with_alignment(range, self.alignment)
419    }
420
421    /// Returns a slice of self for the provided range, with no guarantees about the resulting
422    /// alignment.
423    ///
424    /// # Panics
425    ///
426    /// Requires that `begin <= end` and `end <= self.len()`.
427    #[allow(clippy::inline_always)]
428    #[inline(always)]
429    pub fn slice_unaligned(&self, range: impl RangeBounds<usize>) -> Self {
430        self.slice_with_alignment(range, Alignment::of::<u8>())
431    }
432
433    /// Returns a slice of self for the provided range, ensuring the resulting slice has the
434    /// given alignment.
435    ///
436    /// # Panics
437    ///
438    /// Requires that `begin <= end` and `end <= self.len()`.
439    /// Also requires that both `begin` and `end` are aligned to the given alignment.
440    pub fn slice_with_alignment(
441        &self,
442        range: impl RangeBounds<usize>,
443        alignment: Alignment,
444    ) -> Self {
445        let len = self.len();
446        let begin = match range.start_bound() {
447            Bound::Included(&n) => n,
448            Bound::Excluded(&n) => n.checked_add(1).vortex_expect("out of range"),
449            Bound::Unbounded => 0,
450        };
451        let end = match range.end_bound() {
452            Bound::Included(&n) => n.checked_add(1).vortex_expect("out of range"),
453            Bound::Excluded(&n) => n,
454            Bound::Unbounded => len,
455        };
456
457        if begin > end {
458            slice_range_inverted(begin, end);
459        }
460        if end > len {
461            slice_range_out_of_bounds(end, len);
462        }
463
464        if end == begin {
465            // We prefer to return a new empty buffer instead of sharing this one and creating a
466            // strong reference just to hold an empty slice.
467            return Self::empty_aligned(alignment);
468        }
469
470        let begin_byte = begin * size_of::<T>();
471        if !alignment.is_offset_aligned(begin_byte) {
472            slice_start_misaligned(alignment, begin_byte);
473        }
474        if !alignment.is_aligned_to(Alignment::of::<T>()) {
475            vortex_panic!("Slice alignment must at least align to type T")
476        }
477
478        Self {
479            // SAFETY: begin is in bounds and the alignment check applies to the new pointer.
480            ptr: unsafe { self.ptr.add(begin) },
481            length: end - begin,
482            alignment,
483            backing: self.backing.clone(),
484        }
485    }
486
487    /// Returns a slice of self that is equivalent to the given subset.
488    ///
489    /// When processing the buffer you will often end up with `&[T]` that is a subset
490    /// of the underlying buffer. This function turns the slice into a slice of the buffer
491    /// it has been taken from.
492    ///
493    /// # Panics:
494    /// Requires that the given sub slice is in fact contained within the Bytes buffer; otherwise this function will panic.
495    #[allow(clippy::inline_always)]
496    #[inline(always)]
497    pub fn slice_ref(&self, subset: &[T]) -> Self {
498        self.slice_ref_with_alignment(subset, Alignment::of::<T>())
499    }
500
501    /// Returns a slice of self that is equivalent to the given subset.
502    ///
503    /// When processing the buffer you will often end up with `&[T]` that is a subset
504    /// of the underlying buffer. This function turns the slice into a slice of the buffer
505    /// it has been taken from.
506    ///
507    /// # Panics:
508    /// Requires that the given sub slice is in fact contained within the Bytes buffer; otherwise this function will panic.
509    /// Also requires that the given alignment aligns to the type of slice and is smaller or equal to the buffers alignment
510    pub fn slice_ref_with_alignment(&self, subset: &[T], alignment: Alignment) -> Self {
511        if !alignment.is_aligned_to(Alignment::of::<T>()) {
512            vortex_panic!("slice_ref alignment must at least align to type T")
513        }
514
515        if !self.alignment.is_aligned_to(alignment) {
516            vortex_panic!("slice_ref subset alignment must at least align to the buffer alignment")
517        }
518
519        if !alignment.is_ptr_aligned(subset.as_ptr()) {
520            vortex_panic!("slice_ref subset must be aligned to {:?}", alignment);
521        }
522
523        let start = self.as_ptr().addr();
524        let end = start + size_of_val(self.as_slice());
525        let subset_start = subset.as_ptr().addr();
526        let subset_end = subset_start
527            .checked_add(size_of_val(subset))
528            .vortex_expect("slice_ref address overflow");
529        if subset_start < start || subset_end > end {
530            vortex_panic!("slice_ref subset must be contained in the buffer");
531        }
532
533        Self {
534            ptr: NonNull::new(subset.as_ptr().cast_mut()).vortex_expect("slice pointer is null"),
535            length: subset.len(),
536            alignment,
537            backing: self.backing.clone(),
538        }
539    }
540
541    /// Returns the underlying bytes without copying.
542    pub fn into_bytes(self) -> Bytes {
543        if let Some(backing) = self.backing.as_ref()
544            && let BufferBacking::Bytes(bytes) = backing.as_ref()
545        {
546            let offset = self.ptr.cast::<u8>().addr().get() - bytes.as_ptr().addr();
547            let length = self.length * size_of::<T>();
548            if offset == 0 && length == bytes.len() && Arc::strong_count(backing) == 1 {
549                return match self.backing {
550                    Some(backing) => match Arc::try_unwrap(backing) {
551                        Ok(BufferBacking::Bytes(bytes)) => bytes,
552                        _ => unreachable!(),
553                    },
554                    None => unreachable!(),
555                };
556            }
557            return bytes.slice(offset..offset + length);
558        }
559        match self.backing {
560            Some(backing) => Bytes::from_owner(BufferBytesOwner {
561                ptr: self.ptr.cast(),
562                length: self.length * size_of::<T>(),
563                backing,
564            }),
565            None => Bytes::new(),
566        }
567    }
568
569    /// Return the ByteBuffer for this `Buffer<T>`.
570    pub fn into_byte_buffer(self) -> ByteBuffer {
571        ByteBuffer {
572            ptr: self.ptr.cast(),
573            length: self.length * size_of::<T>(),
574            alignment: self.alignment,
575            backing: self.backing,
576        }
577    }
578
579    /// Try to convert self into `BufferMut<T>` if there is only a single strong reference.
580    pub fn try_into_mut(self) -> Result<BufferMut<T>, Self> {
581        let Self {
582            ptr,
583            length,
584            alignment,
585            backing,
586        } = self;
587        let Some(backing) = backing else {
588            return Ok(BufferMut::empty_aligned(alignment));
589        };
590        if !matches!(backing.as_ref(), BufferBacking::Owned(_)) {
591            return Err(Self {
592                ptr,
593                length,
594                alignment,
595                backing: Some(backing),
596            });
597        }
598        match Arc::try_unwrap(backing) {
599            Ok(BufferBacking::Owned(allocation)) => {
600                let offset = ptr.addr().get() - allocation.ptr().addr().get();
601                let capacity = if allocation.size() == 0 {
602                    0
603                } else {
604                    (allocation.size() - offset) / size_of::<T>()
605                };
606                Ok(BufferMut {
607                    allocation,
608                    ptr,
609                    length,
610                    capacity,
611                    alignment,
612                    _marker: Default::default(),
613                })
614            }
615            Ok(_) => unreachable!(),
616            Err(backing) => Err(Self {
617                ptr,
618                length,
619                alignment,
620                backing: Some(backing),
621            }),
622        }
623    }
624
625    /// Convert self into `BufferMut<T>`, cloning the data if there are multiple strong references.
626    pub fn into_mut(self) -> BufferMut<T>
627    where
628        T: Copy,
629    {
630        self.try_into_mut().unwrap_or_else(|buffer| {
631            let allocator = buffer.allocator().clone();
632            BufferMut::<T>::copy_from_aligned_in(&buffer, buffer.alignment, allocator)
633        })
634    }
635
636    /// Returns whether a `Buffer<T>` is aligned to the given alignment.
637    pub fn is_aligned(&self, alignment: Alignment) -> bool {
638        alignment.is_ptr_aligned(self.as_ptr())
639    }
640
641    /// Return a `Buffer<T>` with the given alignment. Where possible, this will be zero-copy.
642    pub fn aligned(mut self, alignment: Alignment) -> Self
643    where
644        T: Copy,
645    {
646        if alignment.is_ptr_aligned(self.as_ptr()) {
647            self.alignment = alignment;
648            self
649        } else {
650            #[cfg(feature = "warn-copy")]
651            {
652                let bt = std::backtrace::Backtrace::capture();
653                tracing::warn!(
654                    "Buffer is not aligned to requested alignment {alignment}, copying: {bt}"
655                )
656            }
657            let allocator = self.allocator().clone();
658            BufferMut::copy_from_aligned_in(self, alignment, allocator).freeze()
659        }
660    }
661
662    /// Return a `Buffer<T>` with the given alignment. Panics if the buffer is not aligned.
663    pub fn ensure_aligned(mut self, alignment: Alignment) -> Self {
664        if alignment.is_ptr_aligned(self.as_ptr()) {
665            self.alignment = alignment;
666            self
667        } else {
668            vortex_panic!("Buffer is not aligned to requested alignment {}", alignment)
669        }
670    }
671}
672
673impl<T> Buffer<T> {
674    /// Transmute a `Buffer<T>` into a `Buffer<U>`.
675    ///
676    /// # Safety
677    ///
678    /// The caller must ensure that all possible bit representations of type `T` are valid when
679    /// interpreted as type `U`.
680    /// See [`std::mem::transmute`] for more details.
681    ///
682    /// # Panics
683    ///
684    /// Panics if the type `U` does not have the same size and alignment as `T`.
685    pub unsafe fn transmute<U>(self) -> Buffer<U> {
686        assert_eq!(size_of::<T>(), size_of::<U>(), "Buffer type size mismatch");
687        assert_eq!(
688            align_of::<T>(),
689            align_of::<U>(),
690            "Buffer type alignment mismatch"
691        );
692
693        Buffer {
694            ptr: self.ptr.cast(),
695            length: self.length,
696            alignment: self.alignment,
697            backing: self.backing,
698        }
699    }
700}
701
702/// An iterator over Buffer elements.
703///
704/// This is an analog to the `std::slice::Iter` type.
705pub struct Iter<'a, T> {
706    inner: std::slice::Iter<'a, T>,
707}
708
709impl<'a, T> Iterator for Iter<'a, T> {
710    type Item = &'a T;
711
712    #[inline]
713    fn next(&mut self) -> Option<Self::Item> {
714        self.inner.next()
715    }
716
717    #[inline]
718    fn size_hint(&self) -> (usize, Option<usize>) {
719        self.inner.size_hint()
720    }
721
722    #[inline]
723    fn count(self) -> usize {
724        self.inner.count()
725    }
726
727    #[inline]
728    fn last(self) -> Option<Self::Item> {
729        self.inner.last()
730    }
731
732    #[inline]
733    fn nth(&mut self, n: usize) -> Option<Self::Item> {
734        self.inner.nth(n)
735    }
736}
737
738impl<T> ExactSizeIterator for Iter<'_, T> {
739    #[inline]
740    fn len(&self) -> usize {
741        self.inner.len()
742    }
743}
744
745impl<T: Debug> Debug for Buffer<T> {
746    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
747        f.debug_struct(&format!("Buffer<{}>", type_name::<T>()))
748            .field("length", &self.length)
749            .field("alignment", &self.alignment)
750            .field("as_slice", &TruncatedDebug(self.as_slice()))
751            .finish()
752    }
753}
754
755impl<T> Deref for Buffer<T> {
756    type Target = [T];
757
758    #[inline]
759    fn deref(&self) -> &Self::Target {
760        self.as_slice()
761    }
762}
763
764impl<T> AsRef<[T]> for Buffer<T> {
765    #[inline]
766    fn as_ref(&self) -> &[T] {
767        self.as_slice()
768    }
769}
770
771impl<T> FromIterator<T> for Buffer<T> {
772    #[inline]
773    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
774        BufferMut::from_iter(iter).freeze()
775    }
776}
777
778impl<T> From<Vec<T>> for Buffer<T>
779where
780    T: Send + Sync + 'static,
781{
782    fn from(value: Vec<T>) -> Self {
783        const { assert!(size_of::<T>() != 0, "ZSTs are not supported") };
784        let length = value.len();
785        let alignment = Alignment::of::<T>();
786        if std::mem::needs_drop::<T>() {
787            // Keep the typed owner so its elements are dropped.
788            Self {
789                ptr: NonNull::new(value.as_ptr().cast_mut())
790                    .vortex_expect("a Vec always has a non-null pointer"),
791                length,
792                alignment,
793                backing: Some(Arc::new(BufferBacking::External {
794                    _owner: Box::new(value),
795                })),
796            }
797        } else {
798            Self::from_allocation(Allocation::from_vec(value), 0, length, alignment)
799        }
800    }
801}
802
803impl From<Bytes> for ByteBuffer {
804    fn from(bytes: Bytes) -> Self {
805        Self::from_bytes(bytes, Alignment::of::<u8>())
806    }
807}
808
809impl Buf for ByteBuffer {
810    #[inline]
811    fn remaining(&self) -> usize {
812        self.len()
813    }
814
815    #[inline]
816    fn chunk(&self) -> &[u8] {
817        self.as_slice()
818    }
819
820    #[inline]
821    fn advance(&mut self, cnt: usize) {
822        if !self.alignment.is_offset_aligned(cnt) {
823            vortex_panic!(
824                "Cannot advance buffer by {} items, resulting alignment is not {}",
825                cnt,
826                self.alignment
827            );
828        }
829        assert!(cnt <= self.length, "cannot advance past the buffer length");
830        // SAFETY: cnt is within the initialized byte range.
831        self.ptr = unsafe { self.ptr.add(cnt) };
832        self.length -= cnt;
833    }
834}
835
836struct BufferBytesOwner {
837    ptr: NonNull<u8>,
838    length: usize,
839    backing: Arc<BufferBacking>,
840}
841
842// SAFETY: the owner exposes immutable initialized bytes and keeps their backing live.
843unsafe impl Send for BufferBytesOwner {}
844unsafe impl Sync for BufferBytesOwner {}
845
846impl AsRef<[u8]> for BufferBytesOwner {
847    fn as_ref(&self) -> &[u8] {
848        let _ = &self.backing;
849        // SAFETY: ptr and length came from a live Buffer.
850        unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.length) }
851    }
852}
853
854fn empty_ptr<T>() -> NonNull<T> {
855    let addr = 1usize << (usize::BITS - 1);
856    NonNull::new(std::ptr::without_provenance_mut(addr)).vortex_expect("empty pointer is non-null")
857}
858
859/// Owned iterator over a [`Buffer`].
860pub struct BufferIterator<T: Copy> {
861    // Keep the buffer alive for the duration of the iteration.
862    _buffer: Buffer<T>,
863    ptr: *const T,
864    end: *const T,
865}
866
867// SAFETY: `BufferIterator` is a `Buffer<T>` plus two cursors into it, so it can safely be
868// `Send`/`Sync` exactly when `Buffer<T>` is. Same bounds as `std::vec::IntoIter`.
869unsafe impl<T: Copy + Send> Send for BufferIterator<T> {}
870unsafe impl<T: Copy + Sync> Sync for BufferIterator<T> {}
871
872impl<T: Copy> Iterator for BufferIterator<T> {
873    type Item = T;
874
875    #[inline]
876    fn next(&mut self) -> Option<Self::Item> {
877        if self.ptr == self.end {
878            None
879        } else {
880            // SAFETY: ptr is within the buffer and has not reached end.
881            let value = unsafe { self.ptr.read() };
882            self.ptr = unsafe { self.ptr.add(1) };
883            Some(value)
884        }
885    }
886
887    #[inline]
888    fn size_hint(&self) -> (usize, Option<usize>) {
889        let remaining = unsafe { self.end.offset_from(self.ptr) } as usize;
890        (remaining, Some(remaining))
891    }
892}
893
894impl<T: Copy> ExactSizeIterator for BufferIterator<T> {}
895
896impl<T: Copy> IntoIterator for Buffer<T> {
897    type Item = T;
898    type IntoIter = BufferIterator<T>;
899
900    #[inline]
901    fn into_iter(self) -> Self::IntoIter {
902        let ptr = self.as_slice().as_ptr();
903        let end = unsafe { ptr.add(self.len()) };
904        BufferIterator {
905            _buffer: self,
906            ptr,
907            end,
908        }
909    }
910}
911
912impl<T> From<BufferMut<T>> for Buffer<T> {
913    #[inline]
914    fn from(value: BufferMut<T>) -> Self {
915        value.freeze()
916    }
917}
918
919// If panic helpers are inlined, this makes callers of BufferMut's functions
920// not inline-eligible. This is more ugly than direct panics but helps with
921// performance
922
923#[cold]
924#[inline(never)]
925fn misaligned_scalar_type(alignment: Alignment, scalar_align: Alignment) -> ! {
926    vortex_panic!("Alignment {alignment} must align to the scalar type's alignment {scalar_align}")
927}
928
929#[cold]
930#[inline(never)]
931fn incompatible_scalar_alignment(alignment: Alignment, scalar_align: Alignment) -> ! {
932    vortex_panic!(
933        "Alignment {alignment} must be compatible with the scalar type's alignment {scalar_align}"
934    )
935}
936
937#[cold]
938#[inline(never)]
939fn buffer_not_aligned(alignment: Alignment) -> ! {
940    vortex_panic!("Buffer must align to the requested alignment {alignment}")
941}
942
943#[cold]
944#[inline(never)]
945fn bytes_not_aligned(alignment: Alignment) -> ! {
946    vortex_panic!("Bytes alignment must align to the requested alignment {alignment}")
947}
948
949#[cold]
950#[inline(never)]
951fn buffer_len_not_multiple(len: usize, scalar_size: usize) -> ! {
952    vortex_panic!("Buffer length {len} must be a multiple of the scalar type's size {scalar_size}")
953}
954
955#[cold]
956#[inline(never)]
957fn bytes_len_not_multiple(len: usize, scalar_size: usize) -> ! {
958    vortex_panic!("Bytes length {len} must be a multiple of the scalar type's size {scalar_size}")
959}
960
961#[cold]
962#[inline(never)]
963fn slice_range_inverted(begin: usize, end: usize) -> ! {
964    vortex_panic!("range start must not be greater than end: {begin:?} <= {end:?}")
965}
966
967#[cold]
968#[inline(never)]
969fn slice_range_out_of_bounds(end: usize, len: usize) -> ! {
970    vortex_panic!("range end out of bounds: {end:?} > {len:?}")
971}
972
973#[cold]
974#[inline(never)]
975fn slice_start_misaligned(alignment: Alignment, begin_byte: usize) -> ! {
976    vortex_panic!("range start must be aligned to {alignment:?}, byte {begin_byte}")
977}
978
979#[cfg(test)]
980mod test {
981    use std::mem::align_of;
982    use std::sync::Arc;
983    use std::sync::atomic::AtomicUsize;
984    use std::sync::atomic::Ordering;
985
986    use bytes::Buf;
987    use bytes::Bytes;
988
989    use crate::Alignment;
990    use crate::Buffer;
991    use crate::BufferBacking;
992    use crate::ByteBuffer;
993    use crate::buffer;
994
995    #[test]
996    fn align() {
997        let buf = buffer![0u8, 1, 2];
998        let aligned = buf.aligned(Alignment::new(32));
999        assert_eq!(aligned.alignment(), Alignment::new(32));
1000        assert_eq!(aligned.as_slice(), &[0, 1, 2]);
1001    }
1002
1003    #[test]
1004    fn buffer_iterator_send_sync() {
1005        fn assert_send_sync<T: Send + Sync>(_: &T) {}
1006
1007        let mut iter = buffer![0i32, 1, 2, 3].into_iter();
1008        assert_send_sync(&iter);
1009        iter.next();
1010        let remaining: Vec<i32> = std::thread::spawn(move || iter.collect()).join().unwrap();
1011        assert_eq!(remaining, vec![1, 2, 3]);
1012    }
1013
1014    #[test]
1015    fn slice() {
1016        let buf = buffer![0, 1, 2, 3, 4];
1017        assert_eq!(buf.slice(1..3).as_slice(), &[1, 2]);
1018        assert_eq!(buf.slice(1..=3).as_slice(), &[1, 2, 3]);
1019    }
1020
1021    #[test]
1022    fn slice_unaligned() {
1023        let buf = buffer![0i32, 1, 2, 3, 4].into_byte_buffer();
1024        // With a regular slice, this would panic. See [`slice_bad_alignment`].
1025        let sliced = buf.slice_unaligned(1..2);
1026        // Verify the slice has the expected length (1 byte from index 1 to 2).
1027        assert_eq!(sliced.len(), 1);
1028        // The original buffer has i32 values [0, 1, 2, 3, 4].
1029        // In little-endian bytes, 0i32 = [0, 0, 0, 0], so byte at index 1 is 0.
1030        assert_eq!(sliced.as_slice(), &[0]);
1031    }
1032
1033    #[test]
1034    #[should_panic]
1035    fn slice_bad_alignment() {
1036        let buf = buffer![0i32, 1, 2, 3, 4].into_byte_buffer();
1037        // We should only be able to slice this buffer on 4-byte (i32) boundaries.
1038        buf.slice(1..2);
1039    }
1040
1041    #[test]
1042    fn bytes_buf() {
1043        let mut buf = ByteBuffer::copy_from("helloworld".as_bytes());
1044        assert_eq!(buf.remaining(), 10);
1045        assert_eq!(buf.chunk(), b"helloworld");
1046
1047        buf.advance(5);
1048        assert_eq!(buf.remaining(), 5);
1049        assert_eq!(buf.as_slice(), b"world");
1050        assert_eq!(buf.chunk(), b"world");
1051    }
1052
1053    #[test]
1054    fn buffer_zeroed() {
1055        const LEN: usize = 17;
1056
1057        let buf = Buffer::<u32>::zeroed(LEN);
1058
1059        assert!(buf.is_aligned(Alignment::of::<u32>()));
1060        assert_eq!(buf.as_slice(), &[0; LEN]);
1061    }
1062
1063    #[test]
1064    fn buffer_zeroed_aligned() {
1065        const LEN: usize = 17;
1066        let alignment = Alignment::new(64);
1067
1068        let buf = Buffer::<u32>::zeroed_aligned(LEN, alignment);
1069
1070        assert!(buf.is_aligned(alignment));
1071        assert_eq!(buf.as_slice(), &[0; LEN]);
1072    }
1073
1074    #[test]
1075    fn copy_from_over_aligns_to_default() {
1076        let values = [1u32, 2, 3];
1077        let buf = Buffer::<u32>::copy_from(values);
1078
1079        // The buffer reports the scalar type's alignment, ...
1080        assert_eq!(buf.alignment(), Alignment::of::<u32>());
1081        // ... but the underlying allocation is over-aligned to DEFAULT_ALIGNMENT.
1082        assert!(buf.is_aligned(Alignment::DEFAULT_ALIGNMENT));
1083        assert_eq!(buf.as_slice(), &values);
1084    }
1085
1086    #[test]
1087    fn zeroed_over_aligns_to_default() {
1088        const LEN: usize = 17;
1089
1090        let buf = Buffer::<u32>::zeroed(LEN);
1091
1092        assert_eq!(buf.alignment(), Alignment::of::<u32>());
1093        assert!(buf.is_aligned(Alignment::DEFAULT_ALIGNMENT));
1094        assert_eq!(buf.as_slice(), &[0; LEN]);
1095    }
1096
1097    #[test]
1098    fn from_vec() {
1099        let vec = vec![1, 2, 3, 4, 5];
1100        let buff = Buffer::from(vec.clone());
1101        assert!(buff.is_aligned(Alignment::of::<i32>()));
1102        assert_eq!(vec, buff.as_ref());
1103    }
1104
1105    #[test]
1106    fn from_vec_adopts_allocation() {
1107        let mut vec = Vec::with_capacity(16);
1108        vec.extend([1u32, 2, 3, 4, 5]);
1109        let ptr = vec.as_ptr();
1110        let capacity = vec.capacity();
1111
1112        let buffer = Buffer::from(vec);
1113        assert_eq!(buffer.as_ptr(), ptr);
1114
1115        let Ok(mut buffer) = buffer.try_into_mut() else {
1116            panic!("Vec-backed buffer should be uniquely owned")
1117        };
1118        assert_eq!(buffer.capacity(), capacity);
1119        assert_eq!(buffer.allocation.alignment(), align_of::<u32>());
1120
1121        buffer.extend(6..=32);
1122        assert_eq!(buffer.as_slice(), (1..=32).collect::<Vec<_>>());
1123        assert_eq!(buffer.allocation.alignment(), align_of::<u32>());
1124    }
1125
1126    #[test]
1127    fn byte_owner_preserves_slice_and_lifetime() {
1128        struct Owner {
1129            values: Vec<u8>,
1130            drops: Arc<AtomicUsize>,
1131        }
1132
1133        impl AsRef<[u8]> for Owner {
1134            fn as_ref(&self) -> &[u8] {
1135                &self.values[1..4]
1136            }
1137        }
1138
1139        impl Drop for Owner {
1140            fn drop(&mut self) {
1141                self.drops.fetch_add(1, Ordering::Relaxed);
1142            }
1143        }
1144
1145        let drops = Arc::new(AtomicUsize::new(0));
1146        let owner = Owner {
1147            values: vec![0, 1, 2, 3, 4],
1148            drops: Arc::clone(&drops),
1149        };
1150        let ptr = owner.as_ref().as_ptr();
1151        let buffer = ByteBuffer::from(Bytes::from_owner(owner));
1152        assert_eq!(buffer.as_ptr(), ptr);
1153        assert_eq!(buffer.as_slice(), [1, 2, 3]);
1154        let view = buffer.slice(1..);
1155        drop(buffer);
1156        assert_eq!(drops.load(Ordering::Relaxed), 0);
1157        assert_eq!(view.as_slice(), [2, 3]);
1158        drop(view);
1159        assert_eq!(drops.load(Ordering::Relaxed), 1);
1160    }
1161
1162    #[test]
1163    fn bytes_round_trip_reuses_owner() {
1164        let bytes = Bytes::from_static(&[1, 2, 3, 4]);
1165        let ptr = bytes.as_ptr();
1166
1167        let buffer = ByteBuffer::from(bytes);
1168        assert!(matches!(
1169            buffer.backing.as_deref(),
1170            Some(BufferBacking::Bytes(_))
1171        ));
1172        let bytes = buffer.into_bytes();
1173
1174        assert_eq!(bytes.as_ptr(), ptr);
1175        assert_eq!(bytes.as_ref(), &[1, 2, 3, 4]);
1176    }
1177
1178    #[test]
1179    fn external_try_into_mut_preserves_backing() {
1180        let buffer = ByteBuffer::from(Bytes::from_static(&[1, 2, 3, 4]));
1181        let Some(original_backing) = buffer.backing.as_ref() else {
1182            panic!("external buffer has no backing")
1183        };
1184        let backing = Arc::as_ptr(original_backing);
1185
1186        let Err(buffer) = buffer.try_into_mut() else {
1187            panic!("external buffer became mutable")
1188        };
1189
1190        let Some(new_backing) = buffer.backing.as_ref() else {
1191            panic!("external buffer has no backing")
1192        };
1193        assert_eq!(Arc::as_ptr(new_backing), backing);
1194    }
1195
1196    #[test]
1197    fn from_u8_vec_preserves_capacity() {
1198        let mut vec = Vec::with_capacity(16);
1199        vec.extend([1u8, 2, 3]);
1200
1201        let buffer = Buffer::from(vec);
1202        let Ok(buffer) = buffer.try_into_mut() else {
1203            panic!("Vec-backed buffer should be uniquely owned")
1204        };
1205        assert_eq!(buffer.capacity(), 16);
1206    }
1207
1208    #[test]
1209    fn sliced_buffer_into_mut_has_safe_capacity() {
1210        let mut original = crate::BufferMut::with_capacity(128);
1211        original.extend(0u32..100);
1212        let original = original.freeze();
1213        let sliced = original.slice(64..96);
1214        drop(original);
1215
1216        let Ok(mut sliced) = sliced.try_into_mut() else {
1217            panic!("uniquely owned slice should become mutable")
1218        };
1219        let ptr = sliced.as_ptr();
1220        let capacity = sliced.capacity();
1221        sliced.push_n(0, capacity - sliced.len());
1222        assert_eq!(sliced.len(), capacity);
1223        assert_eq!(sliced.as_ptr(), ptr);
1224        sliced.push(42);
1225        assert_eq!(&sliced[..32], (64u32..96).collect::<Vec<_>>());
1226        assert_eq!(&sliced[32..capacity], vec![0; capacity - 32]);
1227        assert_eq!(sliced[capacity], 42);
1228    }
1229
1230    #[test]
1231    fn from_vec_preserves_drop_glue() {
1232        struct DropValue(Arc<AtomicUsize>);
1233
1234        impl Drop for DropValue {
1235            fn drop(&mut self) {
1236                self.0.fetch_add(1, Ordering::Relaxed);
1237            }
1238        }
1239
1240        let drops = Arc::new(AtomicUsize::new(0));
1241        let values = (0..3)
1242            .map(|_| DropValue(Arc::clone(&drops)))
1243            .collect::<Vec<_>>();
1244        let buffer = Buffer::from(values);
1245
1246        assert_eq!(drops.load(Ordering::Relaxed), 0);
1247        drop(buffer);
1248        assert_eq!(drops.load(Ordering::Relaxed), 3);
1249    }
1250
1251    #[test]
1252    fn empty_aligned_max_alignment() {
1253        // Empty buffers are backed by a static and must satisfy any valid alignment.
1254        let buf = Buffer::<u8>::empty_aligned(Alignment::MAX);
1255        assert!(buf.is_empty());
1256        assert!(buf.is_aligned(Alignment::MAX));
1257    }
1258
1259    #[test]
1260    fn empty_has_no_backing() {
1261        assert!(Buffer::<u8>::empty().backing.is_none());
1262    }
1263
1264    #[test]
1265    fn empty_slice_preserves_alignment() {
1266        let buf = Buffer::<u64>::zeroed_aligned(8, Alignment::new(64));
1267        let sliced = buf.slice(0..0);
1268        assert!(sliced.is_empty());
1269        assert_eq!(sliced.alignment(), Alignment::new(64));
1270        assert!(sliced.is_aligned(Alignment::new(64)));
1271    }
1272
1273    #[test]
1274    fn empty_into_mut_preserves_alignment() {
1275        let buf = Buffer::<u8>::empty_aligned(Alignment::new(64));
1276        let buf_mut = buf.into_mut();
1277        assert_eq!(buf_mut.alignment(), Alignment::new(64));
1278        assert!(buf_mut.is_empty());
1279    }
1280
1281    #[test]
1282    fn test_slice_unaligned_end_pos() {
1283        let data = vec![0u8; 2];
1284        // Overalign the u8 vector.
1285        let aligned_buffer = Buffer::copy_from_aligned(&data, Alignment::new(8));
1286        // Previously, `Buffer::slice` incorrectly asserted that the end position
1287        // must be aligned. That assertion has been removed such that the end
1288        // position can be arbitrary and only the beginning of the slice needs
1289        // to be aligned.
1290        aligned_buffer.slice(0..1);
1291    }
1292
1293    #[test]
1294    fn test_empty_equality() {
1295        let a = Buffer::<u16>::empty();
1296        let b = Buffer::<u16>::empty();
1297
1298        assert_eq!(a, b);
1299    }
1300}