• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 16992684502

15 Aug 2025 02:56PM UTC coverage: 87.875% (+0.2%) from 87.72%
16992684502

Pull #2456

github

web-flow
Merge 2d540e578 into 4a23f65b3
Pull Request #2456: feat: basic BoolBuffer / BoolBufferMut

1275 of 1428 new or added lines in 110 files covered. (89.29%)

334 existing lines in 31 files now uncovered.

57169 of 65057 relevant lines covered (87.88%)

658056.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.08
/vortex-buffer/src/buffer.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::any::type_name;
5
use std::cmp::Ordering;
6
use std::collections::Bound;
7
use std::fmt::{Debug, Formatter};
8
use std::hash::{Hash, Hasher};
9
use std::ops::{Deref, RangeBounds};
10

11
use bytes::{Buf, Bytes};
12
use vortex_error::{VortexExpect, vortex_panic};
13

14
use crate::debug::TruncatedDebug;
15
use crate::trusted_len::TrustedLen;
16
use crate::{Alignment, BitChunks, BufferMut, ByteBuffer};
17

18
/// An immutable buffer of items of `T`.
19
#[derive(Clone)]
20
pub struct Buffer<T> {
21
    pub(crate) bytes: Bytes,
22
    pub(crate) length: usize,
23
    pub(crate) alignment: Alignment,
24
    pub(crate) _marker: std::marker::PhantomData<T>,
25
}
26

27
impl<T> PartialEq for Buffer<T> {
28
    fn eq(&self, other: &Self) -> bool {
392,010✔
29
        self.bytes == other.bytes
392,010✔
30
    }
392,010✔
31
}
32

33
impl<T> Eq for Buffer<T> {}
34

35
impl<T> Ord for Buffer<T> {
36
    fn cmp(&self, other: &Self) -> Ordering {
9,853✔
37
        self.bytes.cmp(&other.bytes)
9,853✔
38
    }
9,853✔
39
}
40

41
impl<T> PartialOrd for Buffer<T> {
42
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
×
43
        Some(self.cmp(other))
×
44
    }
×
45
}
46

47
impl<T> Hash for Buffer<T> {
48
    fn hash<H: Hasher>(&self, state: &mut H) {
9,372✔
49
        self.bytes.as_ref().hash(state)
9,372✔
50
    }
9,372✔
51
}
52

53
impl<T> Buffer<T> {
54
    /// Returns a new `Buffer<T>` copied from the provided `Vec<T>`, `&[T]`, etc.
55
    ///
56
    /// Due to our underlying usage of `bytes::Bytes`, we are unable to take zero-copy ownership
57
    /// of the provided `Vec<T>` while maintaining the ability to convert it back into a mutable
58
    /// buffer. We could fix this by forking `Bytes`, or in many other complex ways, but for now
59
    /// callers should prefer to construct `Buffer<T>` from a `BufferMut<T>`.
60
    pub fn copy_from(values: impl AsRef<[T]>) -> Self {
18,272✔
61
        BufferMut::copy_from(values).freeze()
18,272✔
62
    }
18,272✔
63

64
    /// Returns a new `Buffer<T>` copied from the provided slice and with the requested alignment.
65
    pub fn copy_from_aligned(values: impl AsRef<[T]>, alignment: Alignment) -> Self {
3,701✔
66
        BufferMut::copy_from_aligned(values, alignment).freeze()
3,701✔
67
    }
3,701✔
68

69
    /// Create a new zeroed `Buffer` with the given value.
70
    pub fn zeroed(len: usize) -> Self {
29,512✔
71
        Self::zeroed_aligned(len, Alignment::of::<T>())
29,512✔
72
    }
29,512✔
73

74
    /// Create a new zeroed `Buffer` with the given value.
75
    pub fn zeroed_aligned(len: usize, alignment: Alignment) -> Self {
38,862✔
76
        BufferMut::zeroed_aligned(len, alignment).freeze()
38,862✔
77
    }
38,862✔
78

79
    /// Create a new empty `ByteBuffer` with the provided alignment.
80
    pub fn empty() -> Self {
580,567✔
81
        BufferMut::empty().freeze()
580,567✔
82
    }
580,567✔
83

84
    /// Create a new empty `ByteBuffer` with the provided alignment.
85
    pub fn empty_aligned(alignment: Alignment) -> Self {
10,452✔
86
        BufferMut::empty_aligned(alignment).freeze()
10,452✔
87
    }
10,452✔
88

89
    /// Create a new full `ByteBuffer` with the given value.
90
    pub fn full(item: T, len: usize) -> Self
273,612✔
91
    where
273,612✔
92
        T: Copy,
273,612✔
93
    {
94
        BufferMut::full(item, len).freeze()
273,612✔
95
    }
273,612✔
96

97
    /// Create a `Buffer<T>` zero-copy from a `ByteBuffer`.
98
    ///
99
    /// ## Panics
100
    ///
101
    /// Panics if the buffer is not aligned to the size of `T`, or the length is not a multiple of
102
    /// the size of `T`.
103
    pub fn from_byte_buffer(buffer: ByteBuffer) -> Self {
3,429,665✔
104
        // TODO(ngates): should this preserve the current alignment of the buffer?
105
        Self::from_byte_buffer_aligned(buffer, Alignment::of::<T>())
3,429,665✔
106
    }
3,429,665✔
107

108
    /// Create a `Buffer<T>` zero-copy from a `ByteBuffer`.
109
    ///
110
    /// ## Panics
111
    ///
112
    /// Panics if the buffer is not aligned to the given alignment, if the length is not a multiple
113
    /// of the size of `T`, or if the given alignment is not aligned to that of `T`.
114
    pub fn from_byte_buffer_aligned(buffer: ByteBuffer, alignment: Alignment) -> Self {
3,573,180✔
115
        Self::from_bytes_aligned(buffer.into_inner(), alignment)
3,573,180✔
116
    }
3,573,180✔
117

118
    /// Create a `Buffer<T>` zero-copy from a `Bytes`.
119
    ///
120
    /// ## Panics
121
    ///
122
    /// Panics if the buffer is not aligned to the size of `T`, or the length is not a multiple of
123
    /// the size of `T`.
124
    pub fn from_bytes_aligned(bytes: Bytes, alignment: Alignment) -> Self {
3,919,944✔
125
        if !alignment.is_aligned_to(Alignment::of::<T>()) {
3,919,944✔
126
            vortex_panic!(
×
127
                "Alignment {} must be compatible with the scalar type's alignment {}",
×
128
                alignment,
129
                Alignment::of::<T>(),
×
130
            );
131
        }
3,919,944✔
132
        if bytes.as_ptr().align_offset(*alignment) != 0 {
3,919,944✔
133
            vortex_panic!(
×
134
                "Bytes alignment must align to the requested alignment {}",
×
135
                alignment,
136
            );
137
        }
3,919,944✔
138
        if bytes.len() % size_of::<T>() != 0 {
3,919,944✔
139
            vortex_panic!(
×
140
                "Bytes length {} must be a multiple of the scalar type's size {}",
×
141
                bytes.len(),
×
142
                size_of::<T>()
143
            );
144
        }
3,919,944✔
145
        let length = bytes.len() / size_of::<T>();
3,919,944✔
146
        Self {
3,919,944✔
147
            bytes,
3,919,944✔
148
            length,
3,919,944✔
149
            alignment,
3,919,944✔
150
            _marker: Default::default(),
3,919,944✔
151
        }
3,919,944✔
152
    }
3,919,944✔
153

154
    /// Create a buffer with values from the TrustedLen iterator.
155
    /// Should be preferred over `from_iter` when the iterator is known to be `TrustedLen`.
156
    pub fn from_trusted_len_iter<I: TrustedLen<Item = T>>(iter: I) -> Self {
1,967✔
157
        let (_, high) = iter.size_hint();
1,967✔
158
        let mut buffer =
1,967✔
159
            BufferMut::with_capacity(high.vortex_expect("TrustedLen iterator has no upper bound"));
1,967✔
160
        buffer.extend_trusted(iter);
1,967✔
161
        buffer.freeze()
1,967✔
162
    }
1,967✔
163

164
    /// Returns the length of the buffer in elements of type T.
165
    #[inline(always)]
166
    pub fn len(&self) -> usize {
275,681,709✔
167
        self.length
275,681,709✔
168
    }
275,681,709✔
169

170
    /// Returns whether the buffer is empty.
171
    #[inline(always)]
172
    pub fn is_empty(&self) -> bool {
146,815✔
173
        self.length == 0
146,815✔
174
    }
146,815✔
175

176
    /// Returns the alignment of the buffer.
177
    #[inline(always)]
178
    pub fn alignment(&self) -> Alignment {
117,781✔
179
        self.alignment
117,781✔
180
    }
117,781✔
181

182
    /// Returns a slice over the buffer of elements of type T.
183
    #[inline(always)]
184
    pub fn as_slice(&self) -> &[T] {
219,404,167✔
185
        let raw_slice = self.bytes.as_ref();
219,404,167✔
186
        // SAFETY: alignment of Buffer is checked on construction
187
        unsafe { std::slice::from_raw_parts(raw_slice.as_ptr().cast(), self.length) }
219,404,167✔
188
    }
219,404,167✔
189

190
    /// Returns an iterator over the buffer of elements of type T.
191
    pub fn iter(&self) -> Iter<'_, T> {
54,080✔
192
        Iter {
54,080✔
193
            inner: self.as_slice().iter(),
54,080✔
194
        }
54,080✔
195
    }
54,080✔
196

197
    /// Returns a slice of self for the provided range.
198
    ///
199
    /// # Panics
200
    ///
201
    /// Requires that `begin <= end` and `end <= self.len()`.
202
    /// Also requires that both `begin` and `end` are aligned to the buffer's required alignment.
203
    #[inline(always)]
204
    pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
7,902,534✔
205
        self.slice_with_alignment(range, self.alignment)
7,902,534✔
206
    }
7,902,534✔
207

208
    /// Returns a slice of self for the provided range, with no guarantees about the resulting
209
    /// alignment.
210
    ///
211
    /// # Panics
212
    ///
213
    /// Requires that `begin <= end` and `end <= self.len()`.
214
    #[inline(always)]
215
    pub fn slice_unaligned(&self, range: impl RangeBounds<usize>) -> Self {
3✔
216
        self.slice_with_alignment(range, Alignment::of::<u8>())
3✔
217
    }
3✔
218

219
    /// Returns a slice of self for the provided range, ensuring the resulting slice has the
220
    /// given alignment.
221
    ///
222
    /// # Panics
223
    ///
224
    /// Requires that `begin <= end` and `end <= self.len()`.
225
    /// Also requires that both `begin` and `end` are aligned to the given alignment.
226
    pub fn slice_with_alignment(
8,706,929✔
227
        &self,
8,706,929✔
228
        range: impl RangeBounds<usize>,
8,706,929✔
229
        alignment: Alignment,
8,706,929✔
230
    ) -> Self {
8,706,929✔
231
        let len = self.len();
8,706,929✔
232
        let begin = match range.start_bound() {
8,706,929✔
233
            Bound::Included(&n) => n,
8,704,931✔
234
            Bound::Excluded(&n) => n.checked_add(1).vortex_expect("out of range"),
×
235
            Bound::Unbounded => 0,
1,998✔
236
        };
237
        let end = match range.end_bound() {
8,706,929✔
238
            Bound::Included(&n) => n.checked_add(1).vortex_expect("out of range"),
1✔
239
            Bound::Excluded(&n) => n,
8,706,928✔
240
            Bound::Unbounded => len,
×
241
        };
242

243
        if begin > end {
8,706,929✔
244
            vortex_panic!(
×
245
                "range start must not be greater than end: {:?} <= {:?}",
×
246
                begin,
247
                end
248
            );
249
        }
8,706,929✔
250
        if end > len {
8,706,929✔
251
            vortex_panic!("range end out of bounds: {:?} <= {:?}", end, len);
×
252
        }
8,706,929✔
253

254
        if end == begin {
8,706,929✔
255
            // We prefer to return a new empty buffer instead of sharing this one and creating a
256
            // strong reference just to hold an empty slice.
257
            return Self::empty_aligned(alignment);
10,452✔
258
        }
8,696,477✔
259

260
        let begin_byte = begin * size_of::<T>();
8,696,477✔
261
        let end_byte = end * size_of::<T>();
8,696,477✔
262

263
        if !begin_byte.is_multiple_of(*alignment) {
8,696,477✔
264
            vortex_panic!("range start must be aligned to {alignment:?}");
1✔
265
        }
8,696,476✔
266
        if !end_byte.is_multiple_of(*alignment) {
8,696,476✔
NEW
267
            vortex_panic!("range end must be aligned to {alignment:?}");
×
268
        }
8,696,476✔
269
        if !alignment.is_aligned_to(Alignment::of::<T>()) {
8,696,476✔
270
            vortex_panic!("Slice alignment must at least align to type T")
×
271
        }
8,696,476✔
272

273
        Self {
8,696,476✔
274
            bytes: self.bytes.slice(begin_byte..end_byte),
8,696,476✔
275
            length: end - begin,
8,696,476✔
276
            alignment,
8,696,476✔
277
            _marker: Default::default(),
8,696,476✔
278
        }
8,696,476✔
279
    }
8,706,928✔
280

281
    /// Returns a slice of self that is equivalent to the given subset.
282
    ///
283
    /// When processing the buffer you will often end up with &\[T\] that is a subset
284
    /// of the underlying buffer. This function turns the slice into a slice of the buffer
285
    /// it has been taken from.
286
    ///
287
    /// # Panics:
288
    /// Requires that the given sub slice is in fact contained within the Bytes buffer; otherwise this function will panic.
289
    #[inline(always)]
290
    pub fn slice_ref(&self, subset: &[T]) -> Self {
256,517✔
291
        self.slice_ref_with_alignment(subset, Alignment::of::<T>())
256,517✔
292
    }
256,517✔
293

294
    /// Returns a slice of self that is equivalent to the given subset.
295
    ///
296
    /// When processing the buffer you will often end up with &\[T\] that is a subset
297
    /// of the underlying buffer. This function turns the slice into a slice of the buffer
298
    /// it has been taken from.
299
    ///
300
    /// # Panics:
301
    /// Requires that the given sub slice is in fact contained within the Bytes buffer; otherwise this function will panic.
302
    /// Also requires that the given alignment aligns to the type of slice and is smaller or equal to the buffers alignment
303
    pub fn slice_ref_with_alignment(&self, subset: &[T], alignment: Alignment) -> Self {
328,527✔
304
        if !alignment.is_aligned_to(Alignment::of::<T>()) {
328,527✔
305
            vortex_panic!("slice_ref alignment must at least align to type T")
×
306
        }
328,527✔
307

308
        if !self.alignment.is_aligned_to(alignment) {
328,527✔
309
            vortex_panic!("slice_ref subset alignment must at least align to the buffer alignment")
×
310
        }
328,527✔
311

312
        if subset.as_ptr().align_offset(*alignment) != 0 {
328,527✔
313
            vortex_panic!("slice_ref subset must be aligned to {:?}", alignment);
×
314
        }
328,527✔
315

316
        let subset_u8 =
328,527✔
317
            unsafe { std::slice::from_raw_parts(subset.as_ptr().cast(), size_of_val(subset)) };
328,527✔
318

319
        Self {
328,527✔
320
            bytes: self.bytes.slice_ref(subset_u8),
328,527✔
321
            length: subset.len(),
328,527✔
322
            alignment,
328,527✔
323
            _marker: Default::default(),
328,527✔
324
        }
328,527✔
325
    }
328,527✔
326

327
    /// Returns the underlying aligned buffer.
328
    pub fn inner(&self) -> &Bytes {
×
329
        debug_assert_eq!(
×
330
            self.length * size_of::<T>(),
×
331
            self.bytes.len(),
×
332
            "Own length has to be the same as the underlying bytes length"
×
333
        );
334
        &self.bytes
×
335
    }
×
336

337
    /// Returns the underlying aligned buffer.
338
    pub fn into_inner(self) -> Bytes {
4,463,833✔
339
        debug_assert_eq!(
4,463,833✔
340
            self.length * size_of::<T>(),
4,463,833✔
341
            self.bytes.len(),
4,463,833✔
342
            "Own length has to be the same as the underlying bytes length"
×
343
        );
344
        self.bytes
4,463,833✔
345
    }
4,463,833✔
346

347
    /// Return the ByteBuffer for this `Buffer<T>`.
348
    pub fn into_byte_buffer(self) -> ByteBuffer {
6,391,943✔
349
        ByteBuffer {
6,391,943✔
350
            bytes: self.bytes,
6,391,943✔
351
            length: self.length * size_of::<T>(),
6,391,943✔
352
            alignment: self.alignment,
6,391,943✔
353
            _marker: Default::default(),
6,391,943✔
354
        }
6,391,943✔
355
    }
6,391,943✔
356

357
    /// Convert self into `BufferMut<T>`, copying if there are multiple strong references.
358
    pub fn into_mut(self) -> BufferMut<T> {
4,528✔
359
        self.try_into_mut()
4,528✔
360
            .unwrap_or_else(|buffer| BufferMut::<T>::copy_from(&buffer))
4,528✔
361
    }
4,528✔
362

363
    /// Try to convert self into `BufferMut<T>` if there is only a single strong reference.
364
    pub fn try_into_mut(self) -> Result<BufferMut<T>, Self> {
88,687✔
365
        self.bytes
88,687✔
366
            .try_into_mut()
88,687✔
367
            .map(|bytes| BufferMut {
88,687✔
368
                bytes,
38,067✔
369
                length: self.length,
38,067✔
370
                alignment: self.alignment,
38,067✔
371
                _marker: Default::default(),
38,067✔
372
            })
38,067✔
373
            .map_err(|bytes| Self {
88,687✔
374
                bytes,
50,620✔
375
                length: self.length,
50,620✔
376
                alignment: self.alignment,
50,620✔
377
                _marker: Default::default(),
50,620✔
378
            })
50,620✔
379
    }
88,687✔
380

381
    /// Returns an accessor which can be used to perform bitwise operations in u64 sized chunks
382
    pub fn bit_chunks(self, bit_offset: usize, bit_length: usize) -> BitChunks {
116,825✔
383
        BitChunks::new(self.into_byte_buffer(), bit_offset, bit_length)
116,825✔
384
    }
116,825✔
385

386
    /// Returns whether a `Buffer<T>` is aligned to the given alignment.
387
    pub fn is_aligned(&self, alignment: Alignment) -> bool {
8,311✔
388
        self.bytes.as_ptr().align_offset(*alignment) == 0
8,311✔
389
    }
8,311✔
390

391
    /// Return a `Buffer<T>` with the given alignment. Where possible, this will be zero-copy.
392
    pub fn aligned(mut self, alignment: Alignment) -> Self {
561,235✔
393
        if self.as_ptr().align_offset(*alignment) == 0 {
561,235✔
394
            self.alignment = alignment;
559,533✔
395
            self
559,533✔
396
        } else {
397
            #[cfg(feature = "warn-copy")]
398
            {
399
                let bt = std::backtrace::Backtrace::capture();
1,412✔
400
                log::warn!(
1,412✔
401
                    "Buffer is not aligned to requested alignment {alignment}, copying: {bt}"
402
                )
403
            }
404
            Self::copy_from_aligned(self, alignment)
1,702✔
405
        }
406
    }
561,235✔
407

408
    /// Return a `Buffer<T>` with the given alignment. Panics if the buffer is not aligned.
409
    pub fn ensure_aligned(mut self, alignment: Alignment) -> Self {
×
410
        if self.as_ptr().align_offset(*alignment) == 0 {
×
411
            self.alignment = alignment;
×
412
            self
×
413
        } else {
414
            vortex_panic!("Buffer is not aligned to requested alignment {}", alignment)
×
415
        }
416
    }
×
417

418
    /// Align the buffer to alignment of U
419
    pub fn align_to<U>(mut self) -> (Buffer<T>, Buffer<U>, Buffer<T>) {
72,440✔
420
        let offset = self.as_ptr().align_offset(align_of::<U>());
72,440✔
421
        if offset > self.len() {
72,440✔
NEW
422
            (
×
NEW
423
                self,
×
NEW
424
                Buffer::empty_aligned(Alignment::of::<U>()),
×
NEW
425
                Buffer::empty_aligned(Alignment::of::<T>()),
×
NEW
426
            )
×
427
        } else {
428
            let left = self.bytes.split_to(offset);
72,440✔
429
            self.length -= offset;
72,440✔
430
            let (us_len, _) = self.align_to_offsets::<U>();
72,440✔
431
            let trailer = self.bytes.split_off(us_len * size_of::<U>());
72,440✔
432
            (
72,440✔
433
                Buffer::from_bytes_aligned(left, Alignment::of::<T>()),
72,440✔
434
                Buffer::from_bytes_aligned(self.bytes, Alignment::of::<U>()),
72,440✔
435
                Buffer::from_bytes_aligned(trailer, Alignment::of::<T>()),
72,440✔
436
            )
72,440✔
437
        }
438
    }
72,440✔
439

440
    /// Adapted from standard library slice::align_to_offsets
441
    /// Function to calculate lengths of the middle and trailing slice for `align_to`.
442
    fn align_to_offsets<U>(&self) -> (usize, usize) {
72,440✔
443
        // What we gonna do about `rest` is figure out what multiple of `U`s we can put in a
444
        // lowest number of `T`s. And how many `T`s we need for each such "multiple".
445
        //
446
        // Consider for example T=u8 U=u16. Then we can put 1 U in 2 Ts. Simple. Now, consider
447
        // for example a case where size_of::<T> = 16, size_of::<U> = 24. We can put 2 Us in
448
        // place of every 3 Ts in the `rest` slice. A bit more complicated.
449
        //
450
        // Formula to calculate this is:
451
        //
452
        // Us = lcm(size_of::<T>, size_of::<U>) / size_of::<U>
453
        // Ts = lcm(size_of::<T>, size_of::<U>) / size_of::<T>
454
        //
455
        // Expanded and simplified:
456
        //
457
        // Us = size_of::<T> / gcd(size_of::<T>, size_of::<U>)
458
        // Ts = size_of::<U> / gcd(size_of::<T>, size_of::<U>)
459
        //
460
        // Luckily since all this is constant-evaluated... performance here matters not!
NEW
461
        const fn gcd(a: usize, b: usize) -> usize {
×
NEW
462
            if b == 0 { a } else { gcd(b, a % b) }
×
NEW
463
        }
×
464

465
        // Explicitly wrap the function call in a const block so it gets
466
        // constant-evaluated even in debug mode.
467
        let gcd: usize = const { gcd(size_of::<T>(), size_of::<U>()) };
72,440✔
468
        let ts: usize = size_of::<U>() / gcd;
72,440✔
469
        let us: usize = size_of::<T>() / gcd;
72,440✔
470

471
        // Armed with this knowledge, we can find how many `U`s we can fit!
472
        let us_len = self.len() / ts * us;
72,440✔
473
        // And how many `T`s will be in the trailing slice!
474
        let ts_len = self.len() % ts;
72,440✔
475
        (us_len, ts_len)
72,440✔
476
    }
72,440✔
477
}
478

479
/// An iterator over Buffer elements.
480
///
481
/// This is an analog to the `std::slice::Iter` type.
482
pub struct Iter<'a, T> {
483
    inner: std::slice::Iter<'a, T>,
484
}
485

486
impl<'a, T> Iterator for Iter<'a, T> {
487
    type Item = &'a T;
488

489
    fn next(&mut self) -> Option<Self::Item> {
93,863,655✔
490
        self.inner.next()
93,863,655✔
491
    }
93,863,655✔
492

493
    fn size_hint(&self) -> (usize, Option<usize>) {
12,556✔
494
        self.inner.size_hint()
12,556✔
495
    }
12,556✔
496

497
    fn count(self) -> usize {
×
498
        self.inner.count()
×
499
    }
×
500

501
    fn last(self) -> Option<Self::Item> {
×
502
        self.inner.last()
×
503
    }
×
504

505
    fn nth(&mut self, n: usize) -> Option<Self::Item> {
×
506
        self.inner.nth(n)
×
507
    }
×
508
}
509

510
impl<T> ExactSizeIterator for Iter<'_, T> {
511
    fn len(&self) -> usize {
×
512
        self.inner.len()
×
513
    }
×
514
}
515

516
impl<T: Debug> Debug for Buffer<T> {
517
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
80✔
518
        f.debug_struct(&format!("Buffer<{}>", type_name::<T>()))
80✔
519
            .field("length", &self.length)
80✔
520
            .field("alignment", &self.alignment)
80✔
521
            .field("as_slice", &TruncatedDebug(self.as_slice()))
80✔
522
            .finish()
80✔
523
    }
80✔
524
}
525

526
impl<T> Deref for Buffer<T> {
527
    type Target = [T];
528

529
    fn deref(&self) -> &Self::Target {
214,207,107✔
530
        self.as_slice()
214,207,107✔
531
    }
214,207,107✔
532
}
533

534
impl<T> AsRef<[T]> for Buffer<T> {
535
    fn as_ref(&self) -> &[T] {
5,036,246✔
536
        self.as_slice()
5,036,246✔
537
    }
5,036,246✔
538
}
539

540
impl<T> FromIterator<T> for Buffer<T> {
541
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
108,123✔
542
        BufferMut::from_iter(iter).freeze()
108,123✔
543
    }
108,123✔
544
}
545

546
/// Only for `Buffer<u8>` can we zero-copy from a `Vec<u8>` since we can use a 1-byte alignment.
547
impl From<Vec<u8>> for ByteBuffer {
548
    fn from(value: Vec<u8>) -> Self {
607,028✔
549
        Self::from(Bytes::from(value))
607,028✔
550
    }
607,028✔
551
}
552

553
/// Only for `Buffer<u8>` can we zero-copy from a `Bytes` since we can use a 1-byte alignment.
554
impl From<Bytes> for ByteBuffer {
555
    fn from(bytes: Bytes) -> Self {
607,802✔
556
        let length = bytes.len();
607,802✔
557
        Self {
607,802✔
558
            bytes,
607,802✔
559
            length,
607,802✔
560
            alignment: Alignment::of::<u8>(),
607,802✔
561
            _marker: Default::default(),
607,802✔
562
        }
607,802✔
563
    }
607,802✔
564
}
565

566
impl Buf for ByteBuffer {
567
    fn remaining(&self) -> usize {
2✔
568
        self.len()
2✔
569
    }
2✔
570

571
    fn chunk(&self) -> &[u8] {
2✔
572
        self.as_slice()
2✔
573
    }
2✔
574

575
    fn advance(&mut self, cnt: usize) {
1✔
576
        if !cnt.is_multiple_of(*self.alignment) {
1✔
577
            vortex_panic!(
×
578
                "Cannot advance buffer by {} items, resulting alignment is not {}",
×
579
                cnt,
580
                self.alignment
581
            );
582
        }
1✔
583
        self.bytes.advance(cnt);
1✔
584
        self.length -= cnt;
1✔
585
    }
1✔
586
}
587

588
/// Owned iterator over a [`Buffer`].
589
pub struct BufferIterator<T> {
590
    buffer: Buffer<T>,
591
    index: usize,
592
}
593

594
impl<T: Copy> Iterator for BufferIterator<T> {
595
    type Item = T;
596

597
    fn next(&mut self) -> Option<Self::Item> {
82,458,281✔
598
        (self.index < self.buffer.len()).then(move || {
82,458,281✔
599
            let value = self.buffer[self.index];
81,811,436✔
600
            self.index += 1;
81,811,436✔
601
            value
81,811,436✔
602
        })
81,811,436✔
603
    }
82,458,281✔
604

605
    fn size_hint(&self) -> (usize, Option<usize>) {
11,841✔
606
        let remaining = self.buffer.len() - self.index;
11,841✔
607
        (remaining, Some(remaining))
11,841✔
608
    }
11,841✔
609
}
610

611
impl<T: Copy> IntoIterator for Buffer<T> {
612
    type Item = T;
613
    type IntoIter = BufferIterator<T>;
614

615
    fn into_iter(self) -> Self::IntoIter {
645,692✔
616
        BufferIterator {
645,692✔
617
            buffer: self,
645,692✔
618
            index: 0,
645,692✔
619
        }
645,692✔
620
    }
645,692✔
621
}
622

623
impl<T> From<BufferMut<T>> for Buffer<T> {
624
    fn from(value: BufferMut<T>) -> Self {
68,021✔
625
        value.freeze()
68,021✔
626
    }
68,021✔
627
}
628

629
#[cfg(test)]
630
mod test {
631
    use bytes::Buf;
632

633
    use crate::{Alignment, ByteBuffer, buffer};
634

635
    #[test]
636
    fn align() {
1✔
637
        let buf = buffer![0u8, 1, 2];
1✔
638
        let aligned = buf.aligned(Alignment::new(32));
1✔
639
        assert_eq!(aligned.alignment(), Alignment::new(32));
1✔
640
        assert_eq!(aligned.as_slice(), &[0, 1, 2]);
1✔
641
    }
1✔
642

643
    #[test]
644
    fn slice() {
1✔
645
        let buf = buffer![0, 1, 2, 3, 4];
1✔
646
        assert_eq!(buf.slice(1..3).as_slice(), &[1, 2]);
1✔
647
        assert_eq!(buf.slice(1..=3).as_slice(), &[1, 2, 3]);
1✔
648
    }
1✔
649

650
    #[test]
651
    fn slice_unaligned() {
1✔
652
        let buf = buffer![0i32, 1, 2, 3, 4].into_byte_buffer();
1✔
653
        // With a regular slice, this would panic. See [`slice_bad_alignment`].
654
        buf.slice_unaligned(1..2);
1✔
655
    }
1✔
656

657
    #[test]
658
    #[should_panic]
659
    fn slice_bad_alignment() {
1✔
660
        let buf = buffer![0i32, 1, 2, 3, 4].into_byte_buffer();
1✔
661
        // We should only be able to slice this buffer on 4-byte (i32) boundaries.
662
        buf.slice(1..2);
1✔
663
    }
1✔
664

665
    #[test]
666
    fn bytes_buf() {
1✔
667
        let mut buf = ByteBuffer::copy_from("helloworld".as_bytes());
1✔
668
        assert_eq!(buf.remaining(), 10);
1✔
669
        assert_eq!(buf.chunk(), b"helloworld");
1✔
670

671
        Buf::advance(&mut buf, 5);
1✔
672
        assert_eq!(buf.remaining(), 5);
1✔
673
        assert_eq!(buf.as_slice(), b"world");
1✔
674
        assert_eq!(buf.chunk(), b"world");
1✔
675
    }
1✔
676
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc