Skip to content

Commit 7034fd3

Browse files
committed
Use cursor copies for PiecewiseSequence slices
Signed-off-by: Daniel King <dan@spiraldb.com>
1 parent e31db3e commit 7034fd3

5 files changed

Lines changed: 116 additions & 14 deletions

File tree

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::arrays::PiecewiseSequence;
1818
use crate::arrays::PrimitiveArray;
1919
use crate::arrays::dict::TakeExecute;
2020
use crate::arrays::piecewise_sequence::constant_unsigned_usize;
21+
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2122
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2223
use crate::dtype::IntegerPType;
2324
use crate::dtype::NativeDecimalType;
@@ -199,11 +200,19 @@ where
199200
);
200201

201202
let mut result = BufferMut::<T>::with_capacity(output_len);
203+
let mut cursor = 0usize;
202204
for &start in starts {
203205
let start = start.as_();
204-
result.extend_from_slice(&values[start..][..length]);
206+
cursor = copy_slice_to_spare(&mut result, cursor, &values[start..][..length], output_len)?;
205207
}
206208

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) };
207216
Ok(result.freeze())
208217
}
209218

@@ -219,17 +228,21 @@ where
219228
T: NativeDecimalType,
220229
{
221230
let mut result = BufferMut::<T>::with_capacity(output_len);
231+
let mut cursor = 0usize;
222232
for (&start, &length) in starts.iter().zip_eq(lengths) {
223233
let start = start.as_();
224234
let length = length.as_();
225-
result.extend_from_slice(&values[start..][..length]);
235+
cursor = copy_slice_to_spare(&mut result, cursor, &values[start..][..length], output_len)?;
226236
}
227237

228238
vortex_ensure!(
229-
result.len() == output_len,
239+
cursor == output_len,
230240
"PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
231-
result.len()
241+
cursor
232242
);
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) };
233246
Ok(result.freeze())
234247
}
235248

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
//! intended for take operations that can gather regular runs without materializing one index per
99
//! element.
1010
11+
use std::ptr;
12+
1113
use itertools::Itertools;
1214
use num_traits::AsPrimitive;
1315
use vortex_buffer::BufferMut;
@@ -92,6 +94,40 @@ pub(crate) fn is_constant_one(multipliers: &ArrayRef) -> bool {
9294
)
9395
}
9496

97+
pub(crate) fn copy_slice_to_spare<T: Copy>(
98+
buffer: &mut BufferMut<T>,
99+
cursor: usize,
100+
source: &[T],
101+
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+
);
120+
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());
127+
}
128+
Ok(end)
129+
}
130+
95131
pub(crate) fn constant_unsigned_usize(array: &ConstantArray) -> usize {
96132
let pvalue = array
97133
.scalar()

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::arrays::Primitive;
2525
use crate::arrays::PrimitiveArray;
2626
use crate::arrays::dict::TakeExecute;
2727
use crate::arrays::piecewise_sequence::constant_unsigned_usize;
28+
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2829
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2930
use crate::builtins::ArrayBuiltins;
3031
use crate::dtype::DType;
@@ -245,17 +246,21 @@ where
245246
L: UnsignedPType,
246247
{
247248
let mut values = BufferMut::<T>::with_capacity(output_len);
249+
let mut cursor = 0usize;
248250
for (&start, &length) in starts.iter().zip_eq(lengths) {
249251
let start = start.as_();
250252
let length = length.as_();
251-
values.extend_from_slice(&source[start..][..length]);
253+
cursor = copy_slice_to_spare(&mut values, cursor, &source[start..][..length], output_len)?;
252254
}
253255

254256
vortex_ensure!(
255-
values.len() == output_len,
257+
cursor == output_len,
256258
"PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
257-
values.len()
259+
cursor
258260
);
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) };
259264
Ok(values.freeze())
260265
}
261266

@@ -312,11 +317,19 @@ where
312317
);
313318

314319
let mut values = BufferMut::<T>::with_capacity(output_len);
320+
let mut cursor = 0usize;
315321
for &start in starts {
316322
let start = start.as_();
317-
values.extend_from_slice(&source[start..][..length]);
323+
cursor = copy_slice_to_spare(&mut values, cursor, &source[start..][..length], output_len)?;
318324
}
319325

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) };
320333
Ok(values.freeze())
321334
}
322335

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::arrays::VarBin;
2222
use crate::arrays::VarBinArray;
2323
use crate::arrays::dict::TakeExecute;
2424
use crate::arrays::piecewise_sequence::constant_unsigned_usize;
25+
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2526
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2627
use crate::arrays::primitive::PrimitiveArrayExt;
2728
use crate::arrays::varbin::VarBinArrayExt;
@@ -474,6 +475,7 @@ where
474475
}
475476

476477
let mut new_data = ByteBufferMut::with_capacity(output_bytes);
478+
let mut cursor = 0usize;
477479
for &start in starts {
478480
let start = start.as_();
479481
if length == 0 {
@@ -483,8 +485,20 @@ where
483485
let offset_range = &offsets[start..][..=length];
484486
let byte_start = offset_range[0].as_();
485487
let byte_end = offset_range[length].as_();
486-
new_data.extend_from_slice(&data[byte_start..][..byte_end - byte_start]);
488+
cursor = copy_slice_to_spare(
489+
&mut new_data,
490+
cursor,
491+
&data[byte_start..][..byte_end - byte_start],
492+
output_bytes,
493+
)?;
487494
}
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) };
488502

489503
let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
490504
.reinterpret_cast(out_offset_ptype)
@@ -551,6 +565,7 @@ where
551565
);
552566

553567
let mut new_data = ByteBufferMut::with_capacity(output_bytes);
568+
let mut cursor = 0usize;
554569
for (&start, &length) in starts.iter().zip_eq(lengths) {
555570
let start = start.as_();
556571
let length = length.as_();
@@ -561,8 +576,20 @@ where
561576
let offset_range = &offsets[start..][..=length];
562577
let byte_start = offset_range[0].as_();
563578
let byte_end = offset_range[length].as_();
564-
new_data.extend_from_slice(&data[byte_start..][..byte_end - byte_start]);
579+
cursor = copy_slice_to_spare(
580+
&mut new_data,
581+
cursor,
582+
&data[byte_start..][..byte_end - byte_start],
583+
output_bytes,
584+
)?;
565585
}
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) };
566593

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

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::arrays::VarBinView;
2424
use crate::arrays::VarBinViewArray;
2525
use crate::arrays::dict::TakeExecute;
2626
use crate::arrays::piecewise_sequence::constant_unsigned_usize;
27+
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2728
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2829
use crate::arrays::varbinview::BinaryView;
2930
use crate::buffer::BufferHandle;
@@ -174,11 +175,19 @@ where
174175
);
175176

176177
let mut views = BufferMut::<BinaryView>::with_capacity(output_len);
178+
let mut cursor = 0usize;
177179
for &start in starts {
178180
let start = start.as_();
179-
views.extend_from_slice(&source[start..][..length]);
181+
cursor = copy_slice_to_spare(&mut views, cursor, &source[start..][..length], output_len)?;
180182
}
181183

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) };
182191
Ok(views.freeze())
183192
}
184193

@@ -193,17 +202,21 @@ where
193202
L: UnsignedPType,
194203
{
195204
let mut views = BufferMut::<BinaryView>::with_capacity(output_len);
205+
let mut cursor = 0usize;
196206
for (&start, &length) in starts.iter().zip_eq(lengths) {
197207
let start = start.as_();
198208
let length = length.as_();
199-
views.extend_from_slice(&source[start..][..length]);
209+
cursor = copy_slice_to_spare(&mut views, cursor, &source[start..][..length], output_len)?;
200210
}
201211

202212
vortex_ensure!(
203-
views.len() == output_len,
213+
cursor == output_len,
204214
"PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
205-
views.len()
215+
cursor
206216
);
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) };
207220
Ok(views.freeze())
208221
}
209222

0 commit comments

Comments
 (0)