• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 16350502539

17 Jul 2025 04:22PM UTC coverage: 80.836% (-0.7%) from 81.557%
16350502539

Pull #3876

github

web-flow
Merge d8ff9e2c1 into d53d06603
Pull Request #3876: feat[layout]: replace register_splits with a layout splits stream

645 of 692 new or added lines in 17 files covered. (93.21%)

372 existing lines in 117 files now uncovered.

42316 of 52348 relevant lines covered (80.84%)

141734.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.92
/encodings/sequence/src/array.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use num_traits::cast::FromPrimitive;
5
use vortex_array::arrays::PrimitiveArray;
6
use vortex_array::stats::{ArrayStats, StatsSetRef};
7
use vortex_array::vtable::{
8
    ArrayVTable, CanonicalVTable, NotSupported, OperationsVTable, VTable, ValidityVTable,
9
    VisitorVTable,
10
};
11
use vortex_array::{
12
    ArrayBufferVisitor, ArrayChildVisitor, ArrayRef, Canonical, EncodingId, EncodingRef, vtable,
13
};
14
use vortex_buffer::BufferMut;
15
use vortex_dtype::{
16
    DType, NativePType, Nullability, PType, match_each_integer_ptype, match_each_native_ptype,
17
};
18
use vortex_error::{VortexExpect, VortexResult, vortex_bail, vortex_err};
19
use vortex_mask::Mask;
20
use vortex_scalar::{PValue, Scalar, ScalarValue};
21

22
vtable!(Sequence);
23

24
#[derive(Clone, Debug)]
25
/// An array representing the equation `A[i] = base + i * multiplier`.
26
pub struct SequenceArray {
27
    base: PValue,
28
    multiplier: PValue,
29
    dtype: DType,
30
    length: usize,
31
    stats_set: ArrayStats,
32
}
33

34
impl SequenceArray {
35
    pub fn typed_new<T: NativePType + Into<PValue>>(
17,526✔
36
        base: T,
17,526✔
37
        multiplier: T,
17,526✔
38
        nullability: Nullability,
17,526✔
39
        length: usize,
17,526✔
40
    ) -> VortexResult<Self> {
17,526✔
41
        Self::new(
17,526✔
42
            base.into(),
17,526✔
43
            multiplier.into(),
17,526✔
44
            T::PTYPE,
45
            nullability,
17,526✔
46
            length,
17,526✔
47
        )
48
    }
17,526✔
49

50
    /// Constructs a sequence array using two integer values (with the same ptype).
51
    pub fn new(
17,570✔
52
        base: PValue,
17,570✔
53
        multiplier: PValue,
17,570✔
54
        ptype: PType,
17,570✔
55
        nullability: Nullability,
17,570✔
56
        length: usize,
17,570✔
57
    ) -> VortexResult<Self> {
17,570✔
58
        if !ptype.is_int() {
17,570✔
59
            vortex_bail!("only integer ptype are supported in SequenceArray currently")
×
60
        }
17,570✔
61

62
        Self::try_last(base, multiplier, ptype, length).map_err(|e| {
17,570✔
63
            e.with_context(format!(
2✔
64
                "final value not expressible, base = {base:?}, multiplier = {multiplier:?}, len = {length} ",
2✔
65
            ))
66
        })?;
2✔
67

68
        Ok(Self::unchecked_new(
17,568✔
69
            base,
17,568✔
70
            multiplier,
17,568✔
71
            ptype,
17,568✔
72
            nullability,
17,568✔
73
            length,
17,568✔
74
        ))
17,568✔
75
    }
17,570✔
76

77
    pub(crate) fn unchecked_new(
18,075✔
78
        base: PValue,
18,075✔
79
        multiplier: PValue,
18,075✔
80
        ptype: PType,
18,075✔
81
        nullability: Nullability,
18,075✔
82
        length: usize,
18,075✔
83
    ) -> Self {
18,075✔
84
        let dtype = DType::Primitive(ptype, nullability);
18,075✔
85
        Self {
18,075✔
86
            base,
18,075✔
87
            multiplier,
18,075✔
88
            dtype,
18,075✔
89
            length,
18,075✔
90
            // TODO(joe): add stats, on construct or on use?
18,075✔
91
            stats_set: Default::default(),
18,075✔
92
        }
18,075✔
93
    }
18,075✔
94

95
    pub fn ptype(&self) -> PType {
822✔
96
        self.dtype.as_ptype()
822✔
97
    }
822✔
98

99
    pub fn base(&self) -> PValue {
1,135✔
100
        self.base
1,135✔
101
    }
1,135✔
102

103
    pub fn multiplier(&self) -> PValue {
1,134✔
104
        self.multiplier
1,134✔
105
    }
1,134✔
106

107
    pub(crate) fn try_last(
28,291✔
108
        base: PValue,
28,291✔
109
        multiplier: PValue,
28,291✔
110
        ptype: PType,
28,291✔
111
        length: usize,
28,291✔
112
    ) -> VortexResult<PValue> {
28,291✔
113
        match_each_integer_ptype!(ptype, |P| {
28,291✔
114
            let len_t = <P>::from_usize(length - 1)
1,776✔
115
                .ok_or_else(|| vortex_err!("cannot convert length {} into {}", length, ptype))?;
1,776✔
116

117
            let base = base.as_primitive::<P>()?;
1,704✔
118
            let multiplier = multiplier.as_primitive::<P>()?;
1,704✔
119

120
            let last = len_t
1,704✔
121
                .checked_mul(multiplier)
1,704✔
122
                .and_then(|offset| offset.checked_add(base))
28,107✔
123
                .ok_or_else(|| vortex_err!("last value computation overflows"))?;
1,704✔
124
            Ok(PValue::from(last))
1,704✔
125
        })
126
    }
28,291✔
127

128
    fn index_value(&self, idx: usize) -> VortexResult<PValue> {
188✔
129
        if idx > self.length {
188✔
UNCOV
130
            vortex_bail!("out of bounds")
×
131
        }
188✔
132
        match_each_native_ptype!(self.ptype(), |P| {
188✔
133
            let base = self.base.as_primitive::<P>()?;
2✔
134
            let multiplier = self.multiplier.as_primitive::<P>()?;
2✔
135
            let value = base + (multiplier * <P>::from_usize(idx).vortex_expect("must fit"));
2✔
136

137
            Ok(PValue::from(value))
2✔
138
        })
139
    }
188✔
140

141
    /// Returns the validated final value of a sequence array
142
    pub fn last(&self) -> PValue {
1✔
143
        Self::try_last(self.base, self.multiplier, self.ptype(), self.length)
1✔
144
            .vortex_expect("validated array")
1✔
145
    }
1✔
146
}
147

148
impl VTable for SequenceVTable {
149
    type Array = SequenceArray;
150
    type Encoding = SequenceEncoding;
151

152
    type ArrayVTable = Self;
153
    type CanonicalVTable = Self;
154
    type OperationsVTable = Self;
155
    type ValidityVTable = Self;
156
    type VisitorVTable = Self;
157
    type ComputeVTable = NotSupported;
158
    type EncodeVTable = Self;
159
    type SerdeVTable = Self;
160

161
    fn id(_encoding: &Self::Encoding) -> EncodingId {
12,745✔
162
        EncodingId::new_ref("vortex.sequence")
12,745✔
163
    }
12,745✔
164

165
    fn encoding(_array: &Self::Array) -> EncodingRef {
6,236✔
166
        EncodingRef::new_ref(SequenceEncoding.as_ref())
6,236✔
167
    }
6,236✔
168
}
169

170
impl ArrayVTable<SequenceVTable> for SequenceVTable {
171
    fn len(array: &SequenceArray) -> usize {
8,783✔
172
        array.length
8,783✔
173
    }
8,783✔
174

175
    fn dtype(array: &SequenceArray) -> &DType {
8,151✔
176
        &array.dtype
8,151✔
177
    }
8,151✔
178

179
    fn stats(array: &SequenceArray) -> StatsSetRef<'_> {
3,433✔
180
        array.stats_set.to_ref(array.as_ref())
3,433✔
181
    }
3,433✔
182
}
183

184
impl CanonicalVTable<SequenceVTable> for SequenceVTable {
185
    fn canonicalize(array: &SequenceArray) -> VortexResult<Canonical> {
600✔
186
        let prim = match_each_native_ptype!(array.ptype(), |P| {
600✔
187
            let base = array.base().as_primitive::<P>()?;
52✔
188
            let multiplier = array.multiplier().as_primitive::<P>()?;
52✔
189
            let values = BufferMut::from_iter(
52✔
190
                (0..array.len())
52✔
191
                    .map(|i| base + <P>::from_usize(i).vortex_expect("must fit") * multiplier),
2,653,080✔
192
            );
193
            PrimitiveArray::new(values, array.dtype.nullability().into())
52✔
194
        });
195

196
        Ok(Canonical::Primitive(prim))
600✔
197
    }
600✔
198
}
199

200
impl OperationsVTable<SequenceVTable> for SequenceVTable {
201
    fn slice(array: &SequenceArray, start: usize, stop: usize) -> VortexResult<ArrayRef> {
33✔
202
        Ok(SequenceArray::unchecked_new(
33✔
203
            array.index_value(start)?,
33✔
204
            array.multiplier,
33✔
205
            array.ptype(),
33✔
206
            array.dtype().nullability(),
33✔
207
            stop - start,
33✔
208
        )
209
        .to_array())
33✔
210
    }
33✔
211

212
    fn scalar_at(array: &SequenceArray, index: usize) -> VortexResult<Scalar> {
155✔
213
        // Ok(Scalar::from(array.index_value(index)))
214
        Ok(Scalar::new(
155✔
215
            array.dtype().clone(),
155✔
216
            ScalarValue::from(array.index_value(index)?),
155✔
217
        ))
218
    }
155✔
219
}
220

221
impl ValidityVTable<SequenceVTable> for SequenceVTable {
222
    fn is_valid(_array: &SequenceArray, _index: usize) -> VortexResult<bool> {
155✔
223
        Ok(true)
155✔
224
    }
155✔
225

226
    fn all_valid(_array: &SequenceArray) -> VortexResult<bool> {
722✔
227
        Ok(true)
722✔
228
    }
722✔
229

230
    fn all_invalid(_array: &SequenceArray) -> VortexResult<bool> {
816✔
231
        Ok(false)
816✔
232
    }
816✔
233

234
    fn validity_mask(array: &SequenceArray) -> VortexResult<Mask> {
101✔
235
        Ok(Mask::AllTrue(array.len()))
101✔
236
    }
101✔
237
}
238

239
impl VisitorVTable<SequenceVTable> for SequenceVTable {
240
    fn visit_buffers(_array: &SequenceArray, _visitor: &mut dyn ArrayBufferVisitor) {
1,941✔
241
        // TODO(joe): expose scalar values
242
    }
1,941✔
243

244
    fn visit_children(_array: &SequenceArray, _visitor: &mut dyn ArrayChildVisitor) {}
2,166✔
245
}
246

247
#[derive(Clone, Debug)]
248
pub struct SequenceEncoding;
249

250
#[cfg(test)]
251
mod tests {
252
    use vortex_array::arrays::PrimitiveArray;
253
    use vortex_dtype::Nullability;
254
    use vortex_scalar::{Scalar, ScalarValue};
255

256
    use crate::array::SequenceArray;
257

258
    #[test]
259
    fn test_sequence_canonical() {
1✔
260
        let arr = SequenceArray::typed_new(2i64, 3, Nullability::NonNullable, 4).unwrap();
1✔
261

262
        let canon = PrimitiveArray::from_iter((0..4).map(|i| 2i64 + i * 3));
4✔
263

264
        assert_eq!(
1✔
265
            arr.to_canonical()
1✔
266
                .unwrap()
1✔
267
                .into_primitive()
1✔
268
                .unwrap()
1✔
269
                .as_slice::<i64>(),
1✔
270
            canon.as_slice::<i64>()
1✔
271
        )
272
    }
1✔
273

274
    #[test]
275
    fn test_sequence_slice_canonical() {
1✔
276
        let arr = SequenceArray::typed_new(2i64, 3, Nullability::NonNullable, 4)
1✔
277
            .unwrap()
1✔
278
            .slice(2, 3)
1✔
279
            .unwrap();
1✔
280

281
        let canon = PrimitiveArray::from_iter((2..3).map(|i| 2i64 + i * 3));
1✔
282

283
        assert_eq!(
1✔
284
            arr.to_canonical()
1✔
285
                .unwrap()
1✔
286
                .into_primitive()
1✔
287
                .unwrap()
1✔
288
                .as_slice::<i64>(),
1✔
289
            canon.as_slice::<i64>()
1✔
290
        )
291
    }
1✔
292

293
    #[test]
294
    fn test_sequence_scalar_at() {
1✔
295
        let scalar = SequenceArray::typed_new(2i64, 3, Nullability::NonNullable, 4)
1✔
296
            .unwrap()
1✔
297
            .scalar_at(2)
1✔
298
            .unwrap();
1✔
299

300
        assert_eq!(
1✔
301
            scalar,
302
            Scalar::new(scalar.dtype().clone(), ScalarValue::from(8i64))
1✔
303
        )
304
    }
1✔
305

306
    #[test]
307
    fn test_sequence_min_max() {
1✔
308
        assert!(SequenceArray::typed_new(-127i8, -1i8, Nullability::NonNullable, 2).is_ok());
1✔
309
        assert!(SequenceArray::typed_new(126i8, -1i8, Nullability::NonNullable, 2).is_ok());
1✔
310
    }
1✔
311

312
    #[test]
313
    fn test_sequence_too_big() {
1✔
314
        assert!(SequenceArray::typed_new(127i8, 1i8, Nullability::NonNullable, 2).is_err());
1✔
315
        assert!(SequenceArray::typed_new(-128i8, -1i8, Nullability::NonNullable, 2).is_err());
1✔
316
    }
1✔
317
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc