Skip to content

Commit 1af09ed

Browse files
committed
Advance PiecewiseSequence copy destinations
Signed-off-by: Daniel King <dan@spiraldb.com>
1 parent 94138de commit 1af09ed

5 files changed

Lines changed: 105 additions & 124 deletions

File tree

vortex-array/src/arrays/decimal/compute/take.rs

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use crate::arrays::DecimalArray;
1717
use crate::arrays::PiecewiseSequence;
1818
use crate::arrays::PrimitiveArray;
1919
use crate::arrays::dict::TakeExecute;
20+
use crate::arrays::piecewise_sequence::SpareBufferWriter;
2021
use crate::arrays::piecewise_sequence::constant_unsigned_usize;
21-
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2222
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2323
use crate::dtype::IntegerPType;
2424
use crate::dtype::NativeDecimalType;
@@ -200,19 +200,14 @@ where
200200
);
201201

202202
let mut result = BufferMut::<T>::with_capacity(output_len);
203-
let mut cursor = 0usize;
203+
let mut writer = SpareBufferWriter::new(&mut result, output_len)?;
204204
for &start in starts {
205205
let start = start.as_();
206-
cursor = copy_slice_to_spare(&mut result, cursor, &values[start..][..length], output_len)?;
206+
// SAFETY: `computed_len == output_len` proves that all fixed-length slices fit in the
207+
// output buffer.
208+
unsafe { writer.copy_slice_unchecked(&values[start..][..length]) };
207209
}
208-
209-
vortex_ensure!(
210-
cursor == output_len,
211-
"PiecewiseSequenceArray expanded length {cursor} does not match declared length {output_len}"
212-
);
213-
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
214-
// output_len` proves all slots were initialized.
215-
unsafe { result.set_len(output_len) };
210+
writer.finish()?;
216211
Ok(result.freeze())
217212
}
218213

@@ -228,21 +223,13 @@ where
228223
T: NativeDecimalType,
229224
{
230225
let mut result = BufferMut::<T>::with_capacity(output_len);
231-
let mut cursor = 0usize;
226+
let mut writer = SpareBufferWriter::new(&mut result, output_len)?;
232227
for (&start, &length) in starts.iter().zip_eq(lengths) {
233228
let start = start.as_();
234229
let length = length.as_();
235-
cursor = copy_slice_to_spare(&mut result, cursor, &values[start..][..length], output_len)?;
230+
writer.copy_slice(&values[start..][..length])?;
236231
}
237-
238-
vortex_ensure!(
239-
cursor == output_len,
240-
"PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
241-
cursor
242-
);
243-
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
244-
// output_len` proves all slots were initialized.
245-
unsafe { result.set_len(output_len) };
232+
writer.finish()?;
246233
Ok(result.freeze())
247234
}
248235

vortex-array/src/arrays/piecewise_sequence/mod.rs

Lines changed: 69 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -94,38 +94,78 @@ pub(crate) fn is_constant_one(multipliers: &ArrayRef) -> bool {
9494
)
9595
}
9696

97-
pub(crate) fn copy_slice_to_spare<T: Copy>(
98-
buffer: &mut BufferMut<T>,
99-
cursor: usize,
100-
source: &[T],
97+
pub(crate) struct SpareBufferWriter<'a, T> {
98+
buffer: &'a mut BufferMut<T>,
99+
next: *mut T,
100+
remaining: usize,
101101
output_len: usize,
102-
) -> VortexResult<usize> {
103-
let end = cursor
104-
.checked_add(source.len())
105-
.ok_or_else(|| vortex_err!("slice copy output length overflows usize"))?;
106-
vortex_ensure!(
107-
end <= output_len,
108-
"slice copy length {end} exceeds declared output length {output_len}"
109-
);
110-
vortex_ensure!(
111-
buffer.is_empty(),
112-
"slice copy buffer already has {} initialized values",
113-
buffer.len()
114-
);
115-
vortex_ensure!(
116-
output_len <= buffer.capacity(),
117-
"slice copy output length {output_len} exceeds buffer capacity {}",
118-
buffer.capacity()
119-
);
102+
}
103+
104+
impl<'a, T: Copy> SpareBufferWriter<'a, T> {
105+
pub(crate) fn new(buffer: &'a mut BufferMut<T>, output_len: usize) -> VortexResult<Self> {
106+
vortex_ensure!(
107+
buffer.is_empty(),
108+
"slice copy buffer already has {} initialized values",
109+
buffer.len()
110+
);
111+
vortex_ensure!(
112+
output_len <= buffer.capacity(),
113+
"slice copy output length {output_len} exceeds buffer capacity {}",
114+
buffer.capacity()
115+
);
120116

121-
// SAFETY: the checks above prove `cursor..end` is inside the spare capacity of an
122-
// uninitialized buffer allocated for at least `output_len` values.
123-
let dst = unsafe { buffer.spare_capacity_mut().get_unchecked_mut(cursor..end) };
124-
// SAFETY: `dst` has exactly `source.len()` writable slots and does not overlap with `source`.
125-
unsafe {
126-
ptr::copy_nonoverlapping(source.as_ptr(), dst.as_mut_ptr().cast(), source.len());
117+
let next = buffer.spare_capacity_mut().as_mut_ptr().cast();
118+
Ok(Self {
119+
buffer,
120+
next,
121+
remaining: output_len,
122+
output_len,
123+
})
124+
}
125+
126+
#[inline]
127+
pub(crate) fn copy_slice(&mut self, source: &[T]) -> VortexResult<()> {
128+
vortex_ensure!(
129+
source.len() <= self.remaining,
130+
"slice copy length {} exceeds remaining output length {}",
131+
source.len(),
132+
self.remaining
133+
);
134+
// SAFETY: the check above proves that `source` fits in the remaining output slots.
135+
unsafe { self.copy_slice_unchecked(source) };
136+
Ok(())
137+
}
138+
139+
/// Copies `source` to the next output slots without checking the remaining output length.
140+
///
141+
/// # Safety
142+
///
143+
/// The caller must ensure that `source.len()` does not exceed the remaining output length.
144+
#[inline]
145+
pub(crate) unsafe fn copy_slice_unchecked(&mut self, source: &[T]) {
146+
debug_assert!(source.len() <= self.remaining);
147+
// SAFETY: `next` points to the first unwritten slot, the caller guarantees the source fits
148+
// in the remaining capacity, and the mutable buffer borrow prevents reallocation.
149+
unsafe {
150+
ptr::copy_nonoverlapping(source.as_ptr(), self.next, source.len());
151+
self.next = self.next.add(source.len());
152+
}
153+
self.remaining -= source.len();
154+
}
155+
156+
pub(crate) fn finish(self) -> VortexResult<()> {
157+
vortex_ensure!(
158+
self.remaining == 0,
159+
"slice copy length {} does not match declared output length {}",
160+
self.output_len - self.remaining,
161+
self.output_len
162+
);
163+
// SAFETY: successful calls to the copy methods initialized exactly `output_len` slots.
164+
unsafe {
165+
self.buffer.set_len(self.output_len);
166+
}
167+
Ok(())
127168
}
128-
Ok(end)
129169
}
130170

131171
pub(crate) fn constant_unsigned_usize(array: &ConstantArray) -> usize {

vortex-array/src/arrays/primitive/compute/take/mod.rs

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use crate::arrays::PiecewiseSequence;
2424
use crate::arrays::Primitive;
2525
use crate::arrays::PrimitiveArray;
2626
use crate::arrays::dict::TakeExecute;
27+
use crate::arrays::piecewise_sequence::SpareBufferWriter;
2728
use crate::arrays::piecewise_sequence::constant_unsigned_usize;
28-
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2929
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
3030
use crate::builtins::ArrayBuiltins;
3131
use crate::dtype::DType;
@@ -246,21 +246,13 @@ where
246246
L: UnsignedPType,
247247
{
248248
let mut values = BufferMut::<T>::with_capacity(output_len);
249-
let mut cursor = 0usize;
249+
let mut writer = SpareBufferWriter::new(&mut values, output_len)?;
250250
for (&start, &length) in starts.iter().zip_eq(lengths) {
251251
let start = start.as_();
252252
let length = length.as_();
253-
cursor = copy_slice_to_spare(&mut values, cursor, &source[start..][..length], output_len)?;
253+
writer.copy_slice(&source[start..][..length])?;
254254
}
255-
256-
vortex_ensure!(
257-
cursor == output_len,
258-
"PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
259-
cursor
260-
);
261-
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
262-
// output_len` proves all slots were initialized.
263-
unsafe { values.set_len(output_len) };
255+
writer.finish()?;
264256
Ok(values.freeze())
265257
}
266258

@@ -317,19 +309,14 @@ where
317309
);
318310

319311
let mut values = BufferMut::<T>::with_capacity(output_len);
320-
let mut cursor = 0usize;
312+
let mut writer = SpareBufferWriter::new(&mut values, output_len)?;
321313
for &start in starts {
322314
let start = start.as_();
323-
cursor = copy_slice_to_spare(&mut values, cursor, &source[start..][..length], output_len)?;
315+
// SAFETY: `computed_len == output_len` proves that all fixed-length slices fit in the
316+
// output buffer.
317+
unsafe { writer.copy_slice_unchecked(&source[start..][..length]) };
324318
}
325-
326-
vortex_ensure!(
327-
cursor == output_len,
328-
"PiecewiseSequenceArray expanded length {cursor} does not match declared length {output_len}"
329-
);
330-
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
331-
// output_len` proves all slots were initialized.
332-
unsafe { values.set_len(output_len) };
319+
writer.finish()?;
333320
Ok(values.freeze())
334321
}
335322

vortex-array/src/arrays/varbin/compute/take.rs

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use crate::arrays::PrimitiveArray;
2121
use crate::arrays::VarBin;
2222
use crate::arrays::VarBinArray;
2323
use crate::arrays::dict::TakeExecute;
24+
use crate::arrays::piecewise_sequence::SpareBufferWriter;
2425
use crate::arrays::piecewise_sequence::constant_unsigned_usize;
25-
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2626
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2727
use crate::arrays::primitive::PrimitiveArrayExt;
2828
use crate::arrays::varbin::VarBinArrayExt;
@@ -475,7 +475,7 @@ where
475475
}
476476

477477
let mut new_data = ByteBufferMut::with_capacity(output_bytes);
478-
let mut cursor = 0usize;
478+
let mut writer = SpareBufferWriter::new(&mut new_data, output_bytes)?;
479479
for &start in starts {
480480
let start = start.as_();
481481
if length == 0 {
@@ -485,20 +485,10 @@ where
485485
let offset_range = &offsets[start..][..=length];
486486
let byte_start = offset_range[0].as_();
487487
let byte_end = offset_range[length].as_();
488-
cursor = copy_slice_to_spare(
489-
&mut new_data,
490-
cursor,
491-
&data[byte_start..][..byte_end - byte_start],
492-
output_bytes,
493-
)?;
488+
// SAFETY: the first pass computed `output_bytes` from these same byte ranges.
489+
unsafe { writer.copy_slice_unchecked(&data[byte_start..][..byte_end - byte_start]) };
494490
}
495-
vortex_ensure!(
496-
cursor == output_bytes,
497-
"VarBin byte copy length {cursor} does not match computed byte length {output_bytes}"
498-
);
499-
// SAFETY: `copy_slice_to_spare` checked every write against `output_bytes`, and `cursor ==
500-
// output_bytes` proves all bytes were initialized.
501-
unsafe { new_data.set_len(output_bytes) };
491+
writer.finish()?;
502492

503493
let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
504494
.reinterpret_cast(out_offset_ptype)
@@ -565,7 +555,7 @@ where
565555
);
566556

567557
let mut new_data = ByteBufferMut::with_capacity(output_bytes);
568-
let mut cursor = 0usize;
558+
let mut writer = SpareBufferWriter::new(&mut new_data, output_bytes)?;
569559
for (&start, &length) in starts.iter().zip_eq(lengths) {
570560
let start = start.as_();
571561
let length = length.as_();
@@ -576,20 +566,10 @@ where
576566
let offset_range = &offsets[start..][..=length];
577567
let byte_start = offset_range[0].as_();
578568
let byte_end = offset_range[length].as_();
579-
cursor = copy_slice_to_spare(
580-
&mut new_data,
581-
cursor,
582-
&data[byte_start..][..byte_end - byte_start],
583-
output_bytes,
584-
)?;
569+
// SAFETY: the first pass computed `output_bytes` from these same byte ranges.
570+
unsafe { writer.copy_slice_unchecked(&data[byte_start..][..byte_end - byte_start]) };
585571
}
586-
vortex_ensure!(
587-
cursor == output_bytes,
588-
"VarBin byte copy length {cursor} does not match computed byte length {output_bytes}"
589-
);
590-
// SAFETY: `copy_slice_to_spare` checked every write against `output_bytes`, and `cursor ==
591-
// output_bytes` proves all bytes were initialized.
592-
unsafe { new_data.set_len(output_bytes) };
572+
writer.finish()?;
593573

594574
let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
595575
.reinterpret_cast(out_offset_ptype)

vortex-array/src/arrays/varbinview/compute/take.rs

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ use crate::arrays::PrimitiveArray;
2323
use crate::arrays::VarBinView;
2424
use crate::arrays::VarBinViewArray;
2525
use crate::arrays::dict::TakeExecute;
26+
use crate::arrays::piecewise_sequence::SpareBufferWriter;
2627
use crate::arrays::piecewise_sequence::constant_unsigned_usize;
27-
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2828
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2929
use crate::arrays::varbinview::BinaryView;
3030
use crate::buffer::BufferHandle;
@@ -175,19 +175,14 @@ where
175175
);
176176

177177
let mut views = BufferMut::<BinaryView>::with_capacity(output_len);
178-
let mut cursor = 0usize;
178+
let mut writer = SpareBufferWriter::new(&mut views, output_len)?;
179179
for &start in starts {
180180
let start = start.as_();
181-
cursor = copy_slice_to_spare(&mut views, cursor, &source[start..][..length], output_len)?;
181+
// SAFETY: `computed_len == output_len` proves that all fixed-length slices fit in the
182+
// output buffer.
183+
unsafe { writer.copy_slice_unchecked(&source[start..][..length]) };
182184
}
183-
184-
vortex_ensure!(
185-
cursor == output_len,
186-
"PiecewiseSequenceArray expanded length {cursor} does not match declared length {output_len}"
187-
);
188-
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
189-
// output_len` proves all slots were initialized.
190-
unsafe { views.set_len(output_len) };
185+
writer.finish()?;
191186
Ok(views.freeze())
192187
}
193188

@@ -202,21 +197,13 @@ where
202197
L: UnsignedPType,
203198
{
204199
let mut views = BufferMut::<BinaryView>::with_capacity(output_len);
205-
let mut cursor = 0usize;
200+
let mut writer = SpareBufferWriter::new(&mut views, output_len)?;
206201
for (&start, &length) in starts.iter().zip_eq(lengths) {
207202
let start = start.as_();
208203
let length = length.as_();
209-
cursor = copy_slice_to_spare(&mut views, cursor, &source[start..][..length], output_len)?;
204+
writer.copy_slice(&source[start..][..length])?;
210205
}
211-
212-
vortex_ensure!(
213-
cursor == output_len,
214-
"PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
215-
cursor
216-
);
217-
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
218-
// output_len` proves all slots were initialized.
219-
unsafe { views.set_len(output_len) };
206+
writer.finish()?;
220207
Ok(views.freeze())
221208
}
222209

0 commit comments

Comments
 (0)