Skip to content

Commit 6bd1832

Browse files
committed
feat[fixed-size-list]: impl repeat and slice
Signed-off-by: Joe Isaacs <[email protected]>
1 parent f5ee1a0 commit 6bd1832

File tree

2 files changed

+173
-5
lines changed

2 files changed

+173
-5
lines changed

vortex-vector/src/fixed_size_list/scalar.rs

Lines changed: 142 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,46 @@ 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+
// Get the elements from the scalar's inner length-1 vector and repeat them
108+
// Clone the inner Vector from the Arc
109+
let scalar_elements = self.0.elements();
110+
let mut elements = scalar_elements.as_ref().clone().into_mut();
111+
elements.reserve((n - 1) * list_size as usize);
112+
113+
// Repeat the elements n-1 more times (we already have 1 copy)
114+
for _ in 1..n {
115+
elements.extend_from_vector(scalar_elements.as_ref());
116+
}
117+
118+
// Build validity for n lists
119+
let validity = MaskMut::new(n, self.is_valid());
120+
121+
// SAFETY: We've repeated the elements n times, so elements.len() == n * list_size
122+
unsafe {
123+
VectorMut::FixedSizeList(FixedSizeListVectorMut::new_unchecked(
124+
Box::new(elements),
125+
list_size,
126+
validity,
127+
))
128+
}
90129
}
91130
}
92131

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

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)