Skip to content

Commit 1a17030

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

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
@@ -17,6 +17,7 @@ use crate::arrays::PiecewiseSequence;
1717
use crate::arrays::PrimitiveArray;
1818
use crate::arrays::dict::TakeExecute;
1919
use crate::arrays::piecewise_sequence::ConstantOrArray;
20+
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2021
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2122
use crate::dtype::IntegerPType;
2223
use crate::dtype::NativeDecimalType;
@@ -196,11 +197,19 @@ where
196197
);
197198

198199
let mut result = BufferMut::<T>::with_capacity(output_len);
200+
let mut cursor = 0usize;
199201
for &start in starts {
200202
let start = start.as_();
201-
result.extend_from_slice(&values[start..][..length]);
203+
cursor = copy_slice_to_spare(&mut result, cursor, &values[start..][..length], output_len)?;
202204
}
203205

206+
vortex_ensure!(
207+
cursor == output_len,
208+
"PiecewiseSequenceArray expanded length {cursor} does not match declared length {output_len}"
209+
);
210+
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
211+
// output_len` proves all slots were initialized.
212+
unsafe { result.set_len(output_len) };
204213
Ok(result.freeze())
205214
}
206215

@@ -216,17 +225,21 @@ where
216225
T: NativeDecimalType,
217226
{
218227
let mut result = BufferMut::<T>::with_capacity(output_len);
228+
let mut cursor = 0usize;
219229
for (&start, &length) in starts.iter().zip_eq(lengths) {
220230
let start = start.as_();
221231
let length = length.as_();
222-
result.extend_from_slice(&values[start..][..length]);
232+
cursor = copy_slice_to_spare(&mut result, cursor, &values[start..][..length], output_len)?;
223233
}
224234

225235
vortex_ensure!(
226-
result.len() == output_len,
236+
cursor == output_len,
227237
"PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
228-
result.len()
238+
cursor
229239
);
240+
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
241+
// output_len` proves all slots were initialized.
242+
unsafe { result.set_len(output_len) };
230243
Ok(result.freeze())
231244
}
232245

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;
@@ -102,6 +104,40 @@ pub(crate) fn is_constant_multiplier_one(multipliers: &ArrayRef) -> bool {
102104
)
103105
}
104106

107+
pub(crate) fn copy_slice_to_spare<T: Copy>(
108+
buffer: &mut BufferMut<T>,
109+
cursor: usize,
110+
source: &[T],
111+
output_len: usize,
112+
) -> VortexResult<usize> {
113+
let end = cursor
114+
.checked_add(source.len())
115+
.ok_or_else(|| vortex_err!("slice copy output length overflows usize"))?;
116+
vortex_ensure!(
117+
end <= output_len,
118+
"slice copy length {end} exceeds declared output length {output_len}"
119+
);
120+
vortex_ensure!(
121+
buffer.is_empty(),
122+
"slice copy buffer already has {} initialized values",
123+
buffer.len()
124+
);
125+
vortex_ensure!(
126+
output_len <= buffer.capacity(),
127+
"slice copy output length {output_len} exceeds buffer capacity {}",
128+
buffer.capacity()
129+
);
130+
131+
// SAFETY: the checks above prove `cursor..end` is inside the spare capacity of an
132+
// uninitialized buffer allocated for at least `output_len` values.
133+
let dst = unsafe { buffer.spare_capacity_mut().get_unchecked_mut(cursor..end) };
134+
// SAFETY: `dst` has exactly `source.len()` writable slots and does not overlap with `source`.
135+
unsafe {
136+
ptr::copy_nonoverlapping(source.as_ptr(), dst.as_mut_ptr().cast(), source.len());
137+
}
138+
Ok(end)
139+
}
140+
105141
fn constant_unsigned_usize(array: &ArrayRef) -> VortexResult<Option<usize>> {
106142
let Some(scalar) = array.as_constant() else {
107143
return Ok(None);

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::arrays::Primitive;
2424
use crate::arrays::PrimitiveArray;
2525
use crate::arrays::dict::TakeExecute;
2626
use crate::arrays::piecewise_sequence::ConstantOrArray;
27+
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2728
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2829
use crate::builtins::ArrayBuiltins;
2930
use crate::dtype::DType;
@@ -242,17 +243,21 @@ where
242243
L: UnsignedPType,
243244
{
244245
let mut values = BufferMut::<T>::with_capacity(output_len);
246+
let mut cursor = 0usize;
245247
for (&start, &length) in starts.iter().zip_eq(lengths) {
246248
let start = start.as_();
247249
let length = length.as_();
248-
values.extend_from_slice(&source[start..][..length]);
250+
cursor = copy_slice_to_spare(&mut values, cursor, &source[start..][..length], output_len)?;
249251
}
250252

251253
vortex_ensure!(
252-
values.len() == output_len,
254+
cursor == output_len,
253255
"PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
254-
values.len()
256+
cursor
255257
);
258+
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
259+
// output_len` proves all slots were initialized.
260+
unsafe { values.set_len(output_len) };
256261
Ok(values.freeze())
257262
}
258263

@@ -309,11 +314,19 @@ where
309314
);
310315

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

323+
vortex_ensure!(
324+
cursor == output_len,
325+
"PiecewiseSequenceArray expanded length {cursor} does not match declared length {output_len}"
326+
);
327+
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
328+
// output_len` proves all slots were initialized.
329+
unsafe { values.set_len(output_len) };
317330
Ok(values.freeze())
318331
}
319332

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::arrays::VarBin;
2121
use crate::arrays::VarBinArray;
2222
use crate::arrays::dict::TakeExecute;
2323
use crate::arrays::piecewise_sequence::ConstantOrArray;
24+
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2425
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2526
use crate::arrays::primitive::PrimitiveArrayExt;
2627
use crate::arrays::varbin::VarBinArrayExt;
@@ -467,6 +468,7 @@ where
467468
}
468469

469470
let mut new_data = ByteBufferMut::with_capacity(output_bytes);
471+
let mut cursor = 0usize;
470472
for &start in starts {
471473
let start = start.as_();
472474
if length == 0 {
@@ -476,8 +478,20 @@ where
476478
let offset_range = &offsets[start..][..=length];
477479
let byte_start = offset_range[0].as_();
478480
let byte_end = offset_range[length].as_();
479-
new_data.extend_from_slice(&data[byte_start..][..byte_end - byte_start]);
481+
cursor = copy_slice_to_spare(
482+
&mut new_data,
483+
cursor,
484+
&data[byte_start..][..byte_end - byte_start],
485+
output_bytes,
486+
)?;
480487
}
488+
vortex_ensure!(
489+
cursor == output_bytes,
490+
"VarBin byte copy length {cursor} does not match computed byte length {output_bytes}"
491+
);
492+
// SAFETY: `copy_slice_to_spare` checked every write against `output_bytes`, and `cursor ==
493+
// output_bytes` proves all bytes were initialized.
494+
unsafe { new_data.set_len(output_bytes) };
481495

482496
let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
483497
.reinterpret_cast(out_offset_ptype)
@@ -544,6 +558,7 @@ where
544558
);
545559

546560
let mut new_data = ByteBufferMut::with_capacity(output_bytes);
561+
let mut cursor = 0usize;
547562
for (&start, &length) in starts.iter().zip_eq(lengths) {
548563
let start = start.as_();
549564
let length = length.as_();
@@ -554,8 +569,20 @@ where
554569
let offset_range = &offsets[start..][..=length];
555570
let byte_start = offset_range[0].as_();
556571
let byte_end = offset_range[length].as_();
557-
new_data.extend_from_slice(&data[byte_start..][..byte_end - byte_start]);
572+
cursor = copy_slice_to_spare(
573+
&mut new_data,
574+
cursor,
575+
&data[byte_start..][..byte_end - byte_start],
576+
output_bytes,
577+
)?;
558578
}
579+
vortex_ensure!(
580+
cursor == output_bytes,
581+
"VarBin byte copy length {cursor} does not match computed byte length {output_bytes}"
582+
);
583+
// SAFETY: `copy_slice_to_spare` checked every write against `output_bytes`, and `cursor ==
584+
// output_bytes` proves all bytes were initialized.
585+
unsafe { new_data.set_len(output_bytes) };
559586

560587
let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
561588
.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
@@ -23,6 +23,7 @@ use crate::arrays::VarBinView;
2323
use crate::arrays::VarBinViewArray;
2424
use crate::arrays::dict::TakeExecute;
2525
use crate::arrays::piecewise_sequence::ConstantOrArray;
26+
use crate::arrays::piecewise_sequence::copy_slice_to_spare;
2627
use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
2728
use crate::arrays::varbinview::BinaryView;
2829
use crate::buffer::BufferHandle;
@@ -171,11 +172,19 @@ where
171172
);
172173

173174
let mut views = BufferMut::<BinaryView>::with_capacity(output_len);
175+
let mut cursor = 0usize;
174176
for &start in starts {
175177
let start = start.as_();
176-
views.extend_from_slice(&source[start..][..length]);
178+
cursor = copy_slice_to_spare(&mut views, cursor, &source[start..][..length], output_len)?;
177179
}
178180

181+
vortex_ensure!(
182+
cursor == output_len,
183+
"PiecewiseSequenceArray expanded length {cursor} does not match declared length {output_len}"
184+
);
185+
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
186+
// output_len` proves all slots were initialized.
187+
unsafe { views.set_len(output_len) };
179188
Ok(views.freeze())
180189
}
181190

@@ -190,17 +199,21 @@ where
190199
L: UnsignedPType,
191200
{
192201
let mut views = BufferMut::<BinaryView>::with_capacity(output_len);
202+
let mut cursor = 0usize;
193203
for (&start, &length) in starts.iter().zip_eq(lengths) {
194204
let start = start.as_();
195205
let length = length.as_();
196-
views.extend_from_slice(&source[start..][..length]);
206+
cursor = copy_slice_to_spare(&mut views, cursor, &source[start..][..length], output_len)?;
197207
}
198208

199209
vortex_ensure!(
200-
views.len() == output_len,
210+
cursor == output_len,
201211
"PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
202-
views.len()
212+
cursor
203213
);
214+
// SAFETY: `copy_slice_to_spare` checked every write against `output_len`, and `cursor ==
215+
// output_len` proves all slots were initialized.
216+
unsafe { views.set_len(output_len) };
204217
Ok(views.freeze())
205218
}
206219

0 commit comments

Comments
 (0)