Skip to content

Commit 7e59dfb

Browse files
feat[fixed-size-list]: impl repeat and slice (#5751)
Signed-off-by: Joe Isaacs <[email protected]>
1 parent 5a78d5f commit 7e59dfb

File tree

2 files changed

+183
-5
lines changed

2 files changed

+183
-5
lines changed

vortex-vector/src/fixed_size_list/scalar.rs

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
use vortex_dtype::DType;
55
use vortex_error::vortex_panic;
66
use vortex_mask::Mask;
7+
use vortex_mask::MaskMut;
78

89
use crate::Scalar;
910
use crate::ScalarOps;
1011
use crate::VectorMut;
1112
use crate::VectorMutOps;
1213
use crate::VectorOps;
1314
use crate::fixed_size_list::FixedSizeListVector;
15+
use crate::fixed_size_list::FixedSizeListVectorMut;
1416

1517
/// A scalar value for fixed-size list types.
1618
///
@@ -84,9 +86,56 @@ impl ScalarOps for FixedSizeListScalar {
8486
}
8587
}
8688

87-
fn repeat(&self, _n: usize) -> VectorMut {
88-
// TODO(ngates): add "repeat(n)" to the vector ops trait
89-
todo!()
89+
fn repeat(&self, n: usize) -> VectorMut {
90+
if n == 0 {
91+
// Return an empty vector with the correct structure
92+
let list_size = self.0.list_size();
93+
let scalar_elements = self.0.elements();
94+
let elements = scalar_elements.slice(0..0).into_mut();
95+
let validity = MaskMut::new_true(0);
96+
return unsafe {
97+
VectorMut::FixedSizeList(FixedSizeListVectorMut::new_unchecked(
98+
Box::new(elements),
99+
list_size,
100+
validity,
101+
))
102+
};
103+
}
104+
105+
let list_size = self.0.list_size();
106+
107+
let scalar_elements = self.0.elements();
108+
let mut elements = scalar_elements.as_ref().clone();
109+
// ensure we don't allocate a vector with the wrong size.
110+
elements.clear();
111+
let mut elements = elements.into_mut();
112+
elements.reserve(n * list_size as usize);
113+
114+
if self.is_null() {
115+
elements.append_zeros(n * list_size as usize);
116+
let validity = MaskMut::new_false(n);
117+
return unsafe {
118+
VectorMut::FixedSizeList(FixedSizeListVectorMut::new_unchecked(
119+
Box::new(elements),
120+
list_size,
121+
validity,
122+
))
123+
};
124+
}
125+
126+
// Repeat the elements n-1 more times (we already have 1 copy)
127+
for _ in 0..n {
128+
elements.extend_from_vector(scalar_elements.as_ref());
129+
}
130+
131+
// SAFETY: We've repeated the elements n times, so elements.len() == n * list_size
132+
unsafe {
133+
VectorMut::FixedSizeList(FixedSizeListVectorMut::new_unchecked(
134+
Box::new(elements),
135+
list_size,
136+
MaskMut::new_true(n),
137+
))
138+
}
90139
}
91140
}
92141

@@ -95,3 +144,103 @@ impl From<FixedSizeListScalar> for Scalar {
95144
Scalar::FixedSizeList(val)
96145
}
97146
}
147+
148+
#[cfg(test)]
149+
mod tests {
150+
use std::sync::Arc;
151+
152+
use vortex_dtype::DType;
153+
use vortex_dtype::Nullability;
154+
use vortex_dtype::PType;
155+
use vortex_mask::Mask;
156+
157+
use super::*;
158+
use crate::Vector;
159+
use crate::fixed_size_list::FixedSizeListVector;
160+
use crate::primitive::PVectorMut;
161+
162+
#[test]
163+
fn test_repeat_valid_scalar() {
164+
// Create a FSL with elements [1, 2, 3] (list_size = 3)
165+
let elements: Vector = PVectorMut::<i32>::from_iter([1, 2, 3]).freeze().into();
166+
let validity = Mask::new_true(1);
167+
let fsl = FixedSizeListVector::new(Arc::new(elements), 3, validity);
168+
169+
let scalar = FixedSizeListScalar::new(fsl);
170+
assert!(scalar.is_valid());
171+
172+
// Repeat 4 times
173+
let repeated = scalar.repeat(4).freeze();
174+
assert_eq!(repeated.len(), 4);
175+
176+
// Check validity - all should be valid
177+
assert_eq!(repeated.validity().true_count(), 4);
178+
179+
// Freeze and check the elements
180+
let fsl_vec = repeated.as_fixed_size_list();
181+
assert_eq!(fsl_vec.len(), 4);
182+
assert_eq!(fsl_vec.list_size(), 3);
183+
184+
// Elements should be [1,2,3, 1,2,3, 1,2,3, 1,2,3]
185+
let elements = fsl_vec.elements();
186+
assert_eq!(elements.len(), 12);
187+
}
188+
189+
#[test]
190+
fn test_repeat_null_scalar() {
191+
// Create a null FSL scalar
192+
let dtype = DType::FixedSizeList(
193+
Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable)),
194+
3,
195+
Nullability::Nullable,
196+
);
197+
let scalar = FixedSizeListScalar::null(&dtype);
198+
assert!(!scalar.is_valid());
199+
200+
// Repeat 3 times
201+
let repeated = scalar.repeat(3).freeze();
202+
assert_eq!(repeated.len(), 3);
203+
204+
// Check validity - all should be null
205+
assert_eq!(repeated.validity().true_count(), 0);
206+
}
207+
208+
#[test]
209+
fn test_repeat_zero() {
210+
// Create a valid FSL scalar
211+
let elements: Vector = PVectorMut::<i32>::from_iter([1, 2]).freeze().into();
212+
let validity = Mask::new_true(1);
213+
let fsl = FixedSizeListVector::new(Arc::new(elements), 2, validity);
214+
215+
let scalar = FixedSizeListScalar::new(fsl);
216+
217+
// Repeat 0 times - should return empty vector
218+
let repeated = scalar.repeat(0);
219+
assert_eq!(repeated.len(), 0);
220+
221+
let frozen = repeated.freeze();
222+
let fsl_vec = frozen.as_fixed_size_list();
223+
assert_eq!(fsl_vec.len(), 0);
224+
assert_eq!(fsl_vec.list_size(), 2);
225+
assert_eq!(fsl_vec.elements().len(), 0);
226+
}
227+
228+
#[test]
229+
fn test_repeat_one() {
230+
// Create a FSL with elements [10, 20]
231+
let elements: Vector = PVectorMut::<i32>::from_iter([10, 20]).freeze().into();
232+
let validity = Mask::new_true(1);
233+
let fsl = FixedSizeListVector::new(Arc::new(elements), 2, validity);
234+
235+
let scalar = FixedSizeListScalar::new(fsl);
236+
237+
// Repeat 1 time - should be same as original
238+
let repeated = scalar.repeat(1);
239+
assert_eq!(repeated.len(), 1);
240+
241+
let frozen = repeated.freeze();
242+
let fsl_vec = frozen.as_fixed_size_list();
243+
assert_eq!(fsl_vec.len(), 1);
244+
assert_eq!(fsl_vec.elements().len(), 2);
245+
}
246+
}

vortex-vector/src/fixed_size_list/vector.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::ops::BitAnd;
88
use std::ops::RangeBounds;
99
use std::sync::Arc;
1010

11+
use std::ops::Bound;
1112
use vortex_error::VortexExpect;
1213
use vortex_error::VortexResult;
1314
use vortex_error::vortex_ensure;
@@ -195,8 +196,36 @@ impl VectorOps for FixedSizeListVector {
195196
FixedSizeListScalar::new(self.slice(index..index + 1))
196197
}
197198

198-
fn slice(&self, _range: impl RangeBounds<usize> + Clone + Debug) -> Self {
199-
todo!()
199+
fn slice(&self, range: impl RangeBounds<usize> + Clone + Debug) -> Self {
200+
let start = match range.start_bound() {
201+
Bound::Included(&s) => s,
202+
Bound::Excluded(&s) => s + 1,
203+
Bound::Unbounded => 0,
204+
};
205+
206+
let end = match range.end_bound() {
207+
Bound::Included(&e) => e + 1,
208+
Bound::Excluded(&e) => e,
209+
Bound::Unbounded => self.len,
210+
};
211+
212+
assert!(
213+
start <= end && end <= self.len,
214+
"Range {:?} out of bounds for length {}",
215+
range,
216+
self.len
217+
);
218+
219+
let list_size = self.list_size as usize;
220+
let elem_start = start * list_size;
221+
let elem_end = end * list_size;
222+
223+
let sliced_elements = self.elements.slice(elem_start..elem_end);
224+
let sliced_validity = self.validity.slice(range);
225+
226+
// SAFETY: We're slicing the elements by the corresponding list_size factor,
227+
// so the invariant `elements.len() == validity.len() * list_size` holds.
228+
unsafe { Self::new_unchecked(Arc::new(sliced_elements), self.list_size, sliced_validity) }
200229
}
201230

202231
fn clear(&mut self) {

0 commit comments

Comments
 (0)