Skip to content

Commit f1d1c3a

Browse files
committed
Avoid zero-filling IPC reads with typed buffer handling
1 parent 1ffd202 commit f1d1c3a

2 files changed

Lines changed: 355 additions & 46 deletions

File tree

arrow-buffer/src/buffer/immutable.rs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
use std::alloc::Layout;
18+
use std::alloc::{Layout, handle_alloc_error};
1919
use std::fmt::Debug;
20+
use std::ops::{Deref, DerefMut};
2021
use std::ptr::NonNull;
2122
use std::sync::Arc;
2223

2324
use crate::BufferBuilder;
2425
use crate::alloc::{Allocation, Deallocation};
26+
use crate::buffer::dangling_ptr;
2527
use crate::util::bit_chunk_iterator::{BitChunks, UnalignedBitChunk};
2628
use crate::{bit_util, bytes::Bytes, native::ArrowNativeType};
2729

@@ -84,6 +86,89 @@ pub struct Buffer {
8486
length: usize,
8587
}
8688

89+
/// An aligned byte buffer that can be filled through `Read::read_exact` and
90+
/// converted into [`Buffer`] without copying.
91+
///
92+
/// This is useful for readers that need Arrow buffer alignment without
93+
/// first zero-initializing the allocation.
94+
pub struct AlignedVec {
95+
ptr: NonNull<u8>,
96+
len: usize,
97+
layout: Layout,
98+
}
99+
100+
impl AlignedVec {
101+
/// Allocates `len` bytes with the requested alignment.
102+
pub fn new(len: usize, align: usize) -> Self {
103+
let layout =
104+
Layout::from_size_align(len, align).expect("failed to create layout for AlignedVec");
105+
106+
let ptr = match layout.size() {
107+
0 => dangling_ptr(),
108+
_ => {
109+
// Safety: `layout` has non-zero size and was constructed above.
110+
let raw_ptr = unsafe { std::alloc::alloc(layout) };
111+
NonNull::new(raw_ptr).unwrap_or_else(|| handle_alloc_error(layout))
112+
}
113+
};
114+
115+
Self { ptr, len, layout }
116+
}
117+
}
118+
119+
// Allows callers such as `Read::read_exact` to view the allocated region as
120+
// bytes after it has been filled.
121+
impl Deref for AlignedVec {
122+
type Target = [u8];
123+
124+
fn deref(&self) -> &[u8] {
125+
// Safety: `ptr` points to `len` bytes owned by this AlignedVec.
126+
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
127+
}
128+
}
129+
130+
// Allows callers such as `Read::read_exact` to write directly into the aligned
131+
// allocation before it is converted into an Arrow buffer.
132+
impl DerefMut for AlignedVec {
133+
fn deref_mut(&mut self) -> &mut [u8] {
134+
// Safety: `ptr` points to `len` bytes owned by this AlignedVec.
135+
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
136+
}
137+
}
138+
139+
// Transfers ownership of the aligned allocation into Buffer without copying.
140+
impl From<AlignedVec> for Buffer {
141+
fn from(value: AlignedVec) -> Self {
142+
// Safety: `value.ptr` was allocated with `value.layout`, and the
143+
// resulting Bytes will deallocate it with the same layout.
144+
let bytes =
145+
unsafe { Bytes::new(value.ptr, value.len, Deallocation::Standard(value.layout)) };
146+
std::mem::forget(value);
147+
Buffer::from(bytes)
148+
}
149+
}
150+
151+
// Converts through Buffer so the aligned allocation is still owned through the
152+
// normal Arrow buffer representation.
153+
impl From<AlignedVec> for MutableBuffer {
154+
fn from(value: AlignedVec) -> Self {
155+
let buffer = Buffer::from(value);
156+
buffer
157+
.into_mutable()
158+
.expect("AlignedVec should be uniquely owned")
159+
}
160+
}
161+
// Frees the allocation if AlignedVec is dropped before ownership is transferred
162+
// into Buffer.
163+
impl Drop for AlignedVec {
164+
fn drop(&mut self) {
165+
if self.layout.size() != 0 {
166+
// Safety: `ptr` was allocated with this exact layout in `new`.
167+
unsafe { std::alloc::dealloc(self.ptr.as_ptr(), self.layout) }
168+
}
169+
}
170+
}
171+
87172
impl Default for Buffer {
88173
#[inline]
89174
fn default() -> Self {

0 commit comments

Comments
 (0)