Skip to content

Commit 7eafc3e

Browse files
authored
Zero copy from any Vec into a Buffer (#4742)
I just want to have nice things Signed-off-by: Adam Gutglick <[email protected]>
1 parent 171bb75 commit 7eafc3e

File tree

1 file changed

+65
-8
lines changed

1 file changed

+65
-8
lines changed

vortex-buffer/src/buffer.rs

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::cmp::Ordering;
66
use std::collections::Bound;
77
use std::fmt::{Debug, Formatter};
88
use std::hash::{Hash, Hasher};
9+
use std::marker::PhantomData;
910
use std::ops::{Deref, RangeBounds};
1011

1112
use bytes::{Buf, Bytes};
@@ -20,7 +21,7 @@ pub struct Buffer<T> {
2021
pub(crate) bytes: Bytes,
2122
pub(crate) length: usize,
2223
pub(crate) alignment: Alignment,
23-
pub(crate) _marker: std::marker::PhantomData<T>,
24+
pub(crate) _marker: PhantomData<T>,
2425
}
2526

2627
impl<T> Clone for Buffer<T> {
@@ -30,7 +31,18 @@ impl<T> Clone for Buffer<T> {
3031
bytes: self.bytes.clone(),
3132
length: self.length,
3233
alignment: self.alignment,
33-
_marker: std::marker::PhantomData,
34+
_marker: PhantomData,
35+
}
36+
}
37+
}
38+
39+
impl<T> Default for Buffer<T> {
40+
fn default() -> Self {
41+
Self {
42+
bytes: Default::default(),
43+
length: 0,
44+
alignment: Alignment::of::<T>(),
45+
_marker: PhantomData,
3446
}
3547
}
3648
}
@@ -42,6 +54,18 @@ impl<T> PartialEq for Buffer<T> {
4254
}
4355
}
4456

57+
impl<T: PartialEq> PartialEq<Vec<T>> for Buffer<T> {
58+
fn eq(&self, other: &Vec<T>) -> bool {
59+
self.as_ref() == other.as_slice()
60+
}
61+
}
62+
63+
impl<T: PartialEq> PartialEq<Buffer<T>> for Vec<T> {
64+
fn eq(&self, other: &Buffer<T>) -> bool {
65+
self.as_slice() == other.as_ref()
66+
}
67+
}
68+
4569
impl<T> Eq for Buffer<T> {}
4670

4771
impl<T> Ord for Buffer<T> {
@@ -502,14 +526,39 @@ impl<T> FromIterator<T> for Buffer<T> {
502526
}
503527
}
504528

505-
/// Only for `Buffer<u8>` can we zero-copy from a `Vec<u8>` since we can use a 1-byte alignment.
506-
impl From<Vec<u8>> for ByteBuffer {
507-
fn from(value: Vec<u8>) -> Self {
508-
Self::from(Bytes::from(value))
529+
// Helper struct to allow us to zero-copy any vec into a buffer
530+
#[repr(transparent)]
531+
struct Wrapper<T>(Vec<T>);
532+
533+
impl<T> AsRef<[u8]> for Wrapper<T> {
534+
fn as_ref(&self) -> &[u8] {
535+
let data = self.0.as_ptr().cast::<u8>();
536+
let len = self.0.len() * size_of::<T>();
537+
unsafe { std::slice::from_raw_parts(data, len) }
538+
}
539+
}
540+
541+
impl<T> From<Vec<T>> for Buffer<T>
542+
where
543+
T: Send + 'static,
544+
{
545+
fn from(value: Vec<T>) -> Self {
546+
let original_len = value.len();
547+
let wrapped_vec = Wrapper(value);
548+
549+
let bytes = Bytes::from_owner(wrapped_vec);
550+
551+
assert_eq!(bytes.as_ptr().align_offset(align_of::<T>()), 0);
552+
553+
Self {
554+
bytes,
555+
length: original_len,
556+
alignment: Alignment::of::<T>(),
557+
_marker: PhantomData,
558+
}
509559
}
510560
}
511561

512-
/// Only for `Buffer<u8>` can we zero-copy from a `Bytes` since we can use a 1-byte alignment.
513562
impl From<Bytes> for ByteBuffer {
514563
fn from(bytes: Bytes) -> Self {
515564
let length = bytes.len();
@@ -596,7 +645,7 @@ impl<T> From<BufferMut<T>> for Buffer<T> {
596645
mod test {
597646
use bytes::Buf;
598647

599-
use crate::{Alignment, ByteBuffer, buffer};
648+
use crate::{Alignment, Buffer, ByteBuffer, buffer};
600649

601650
#[test]
602651
fn align() {
@@ -644,4 +693,12 @@ mod test {
644693
assert_eq!(buf.as_slice(), b"world");
645694
assert_eq!(buf.chunk(), b"world");
646695
}
696+
697+
#[test]
698+
fn from_vec() {
699+
let vec = vec![1, 2, 3, 4, 5];
700+
let buff = Buffer::from(vec.clone());
701+
assert!(buff.is_aligned(Alignment::of::<i32>()));
702+
assert_eq!(vec, buff);
703+
}
647704
}

0 commit comments

Comments
 (0)