1use std::mem::needs_drop;
5
6use vortex_error::VortexError;
7use vortex_error::VortexResult;
8use vortex_error::vortex_err;
9
10use crate::ArrayRef;
11use crate::ExecutionCtx;
12use crate::array::ArrayView;
13use crate::array::VTable;
14use crate::array::probe::RepeatedArrayProbe;
15use crate::array::probe::RepeatedState;
16use crate::array::probe::repeated::child_probe;
17use crate::array::probe::repeated::is_valid_scalar;
18use crate::arrays::Primitive;
19use crate::scalar::Scalar;
20use crate::validity::Validity;
21use crate::vtable::OperationsVTable;
22
23pub enum ArrayProbe<'a> {
30 Once(&'a ArrayRef),
32 Repeated(&'a mut RepeatedArrayProbe),
34}
35
36const _: () = assert!(!needs_drop::<ArrayProbe<'_>>());
39const _: () = assert!(!needs_drop::<ProbeState<'_, Primitive>>());
40
41impl ArrayProbe<'_> {
42 #[inline]
44 pub fn array(&self) -> &ArrayRef {
45 match self {
46 Self::Once(array) => array,
47 Self::Repeated(probe) => probe.array(),
48 }
49 }
50
51 #[inline]
53 pub fn execute_scalar(&mut self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<Scalar> {
54 match self {
55 Self::Once(array) => execute_scalar_once(array, index, ctx),
56 Self::Repeated(probe) => probe.execute_scalar(index, ctx),
57 }
58 }
59
60 #[inline]
62 pub fn execute_is_valid(&mut self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
63 match self {
64 Self::Once(array) => execute_is_valid_once(array, index, ctx),
65 Self::Repeated(probe) => probe.execute_is_valid(index, ctx),
66 }
67 }
68
69 #[inline]
71 pub fn execute_is_invalid(
72 &mut self,
73 index: usize,
74 ctx: &mut ExecutionCtx,
75 ) -> VortexResult<bool> {
76 Ok(!self.execute_is_valid(index, ctx)?)
77 }
78}
79
80#[inline]
82fn execute_scalar_once(
83 array: &ArrayRef,
84 index: usize,
85 ctx: &mut ExecutionCtx,
86) -> VortexResult<Scalar> {
87 check_bounds(array, index)?;
88 check_dtype(
89 array,
90 array.dyn_array().probe_scalar_once(array, index, ctx),
91 )
92}
93
94#[inline]
96fn execute_is_valid_once(
97 array: &ArrayRef,
98 index: usize,
99 ctx: &mut ExecutionCtx,
100) -> VortexResult<bool> {
101 check_bounds(array, index)?;
102 is_valid_once(array, index, ctx)
103}
104
105#[inline]
106fn is_valid_once(array: &ArrayRef, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
107 if !array.dtype().is_nullable() {
108 return Ok(true);
109 }
110 match array.validity()? {
111 Validity::NonNullable | Validity::AllValid => Ok(true),
112 Validity::AllInvalid => Ok(false),
113 Validity::Array(validity) => is_valid_scalar(validity.execute_scalar(index, ctx)?, index),
114 }
115}
116
117#[inline]
122pub(super) fn check_dtype(array: &ArrayRef, result: VortexResult<Scalar>) -> VortexResult<Scalar> {
123 result.inspect(|scalar| {
124 debug_assert_eq!(scalar.dtype(), array.dtype(), "Scalar dtype mismatch");
125 })
126}
127
128#[inline]
129pub(super) fn check_bounds(array: &ArrayRef, index: usize) -> VortexResult<()> {
130 if index >= array.len() {
131 return Err(out_of_bounds(index, array.len()));
132 }
133 Ok(())
134}
135
136#[cold]
139#[inline(never)]
140fn out_of_bounds(index: usize, len: usize) -> VortexError {
141 vortex_err!(OutOfBounds: index, 0, len)
142}
143
144pub type EncodingProbeState<V> =
146 <<V as VTable>::OperationsVTable as OperationsVTable<V>>::ProbeState;
147
148pub struct ProbeState<'a, V: VTable> {
160 array: ArrayView<'a, V>,
161 retained: Option<&'a mut RepeatedState<EncodingProbeState<V>>>,
162}
163
164impl<'a, V: VTable> ProbeState<'a, V> {
165 #[inline]
170 pub fn once(array: ArrayView<'a, V>) -> Self {
171 Self {
172 array,
173 retained: None,
174 }
175 }
176
177 #[inline]
179 pub(crate) fn repeated(
180 array: ArrayView<'a, V>,
181 retained: &'a mut RepeatedState<EncodingProbeState<V>>,
182 ) -> Self {
183 Self {
184 array,
185 retained: Some(retained),
186 }
187 }
188
189 #[inline]
191 pub fn array(&self) -> ArrayView<'a, V> {
192 self.array
193 }
194
195 #[inline]
198 pub fn is_valid(&mut self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
199 let array = self.array.array();
200 match &mut self.retained {
201 None => is_valid_once(array, index, ctx),
202 Some(retained) => retained.is_valid(array, index, ctx),
203 }
204 }
205
206 #[inline]
211 pub fn retained(&mut self) -> Option<&mut EncodingProbeState<V>> {
212 self.retained.as_deref_mut().map(RepeatedState::state_mut)
213 }
214
215 #[inline]
218 pub fn split(&mut self) -> (Option<&mut EncodingProbeState<V>>, ProbeChildren<'_, 'a, V>) {
219 let array = self.array;
220 match &mut self.retained {
221 None => (None, ProbeChildren { array, slots: None }),
222 Some(repeated) => {
223 let (state, slots) = repeated.split_mut();
224 let children = ProbeChildren {
225 array,
226 slots: Some(slots),
227 };
228 (Some(state), children)
229 }
230 }
231 }
232
233 #[inline]
238 pub fn slot(&mut self, slot: usize) -> VortexResult<Option<ArrayProbe<'_>>> {
239 let parent = self.array.array();
240 match &mut self.retained {
241 None => Ok(child_of(parent, slot)?.map(ArrayProbe::Once)),
242 Some(repeated) => {
243 Ok(child_probe(repeated.split_mut().1, parent, slot)?.map(ArrayProbe::Repeated))
244 }
245 }
246 }
247}
248
249pub struct ProbeChildren<'s, 'a, V: VTable> {
252 array: ArrayView<'a, V>,
253 slots: Option<&'s mut Vec<Option<RepeatedArrayProbe>>>,
255}
256
257impl<V: VTable> ProbeChildren<'_, '_, V> {
258 #[inline]
264 pub fn slot(&mut self, slot: usize) -> VortexResult<Option<ArrayProbe<'_>>> {
265 let parent = self.array.array();
266 match &mut self.slots {
267 None => Ok(child_of(parent, slot)?.map(ArrayProbe::Once)),
268 Some(slots) => Ok(child_probe(slots, parent, slot)?.map(ArrayProbe::Repeated)),
269 }
270 }
271}
272
273#[inline]
275pub(super) fn child_of(parent: &ArrayRef, slot: usize) -> VortexResult<Option<&ArrayRef>> {
276 Ok(parent
277 .slots()
278 .get(slot)
279 .ok_or_else(|| vortex_err!("Probe slot {slot} is out of bounds"))?
280 .as_ref())
281}
282#[cfg(test)]
283mod tests {
284 use vortex_error::VortexResult;
285 use vortex_error::vortex_err;
286
287 use super::*;
288 use crate::VortexSessionExecute;
289 use crate::array::IntoArray;
290 use crate::arrays::BoolArray;
291 use crate::arrays::DictArray;
292 use crate::arrays::PrimitiveArray;
293 use crate::arrays::Struct;
294 use crate::arrays::StructArray;
295
296 fn nullable_ints() -> ArrayRef {
297 PrimitiveArray::from_option_iter([Some(10i32), None, Some(30)]).into_array()
298 }
299
300 fn check_reads(mut probe: ArrayProbe<'_>, ctx: &mut ExecutionCtx) -> VortexResult<()> {
301 assert!(probe.execute_scalar(3, ctx).is_err());
302 assert!(probe.execute_is_valid(3, ctx).is_err());
303 assert!(probe.execute_scalar(1, ctx)?.is_null());
304 assert!(!probe.execute_is_valid(1, ctx)?);
305 assert!(probe.execute_is_invalid(1, ctx)?);
306 assert_eq!(probe.execute_scalar(2, ctx)?, Scalar::from(Some(30i32)));
307 assert_eq!(probe.execute_scalar(0, ctx)?, Scalar::from(Some(10i32)));
308 Ok(())
309 }
310
311 #[test]
312 fn once_probe_checks_bounds_and_nulls() -> VortexResult<()> {
313 let mut ctx = crate::array_session().create_execution_ctx();
314 let array = nullable_ints();
315 check_reads(array.probe(), &mut ctx)
316 }
317
318 #[test]
321 fn default_probe_scalar_resolves_nulls() -> VortexResult<()> {
322 let mut ctx = crate::array_session().create_execution_ctx();
323 let array = BoolArray::from_iter([Some(true), None, Some(false)]).into_array();
324 check_bool_reads(array.probe(), &mut ctx)?;
325 check_bool_reads(array.repeated_probe().as_probe(), &mut ctx)
326 }
327
328 fn check_bool_reads(mut probe: ArrayProbe<'_>, ctx: &mut ExecutionCtx) -> VortexResult<()> {
329 assert_eq!(probe.execute_scalar(0, ctx)?, Scalar::from(Some(true)));
330 assert!(probe.execute_scalar(1, ctx)?.is_null());
331 assert_eq!(probe.execute_scalar(2, ctx)?, Scalar::from(Some(false)));
332 Ok(())
333 }
334
335 #[test]
338 fn wrapper_passes_child_nulls_through() -> VortexResult<()> {
339 let mut ctx = crate::array_session().create_execution_ctx();
340 let codes = PrimitiveArray::from_iter([0u32, 1, 0, 2]).into_array();
341 let values = PrimitiveArray::from_option_iter([Some(7i64), None, Some(9)]).into_array();
342 let array = DictArray::try_new(codes, values)?.into_array();
343 let expected = [Some(7i64), None, Some(7), Some(9)];
344 let mut once = array.probe();
345 let mut repeated = array.repeated_probe();
346 for (index, value) in expected.into_iter().enumerate() {
347 assert_eq!(once.execute_scalar(index, &mut ctx)?, Scalar::from(value));
348 assert_eq!(
349 repeated.execute_scalar(index, &mut ctx)?,
350 Scalar::from(value)
351 );
352 }
353 Ok(())
354 }
355
356 #[test]
359 fn dict_with_nullable_codes_and_values() -> VortexResult<()> {
360 let mut ctx = crate::array_session().create_execution_ctx();
361 let codes = PrimitiveArray::from_option_iter([Some(0u32), None, Some(1), Some(2), None])
362 .into_array();
363 let values = PrimitiveArray::from_option_iter([Some(7i64), None, Some(9)]).into_array();
364 let array = DictArray::try_new(codes, values)?.into_array();
365 let expected = [Some(7i64), None, None, Some(9), None];
366 for (array, expected) in [
367 (array.clone(), &expected[..]),
368 (array.slice(1..4)?, &expected[1..4]),
369 ] {
370 let mut once = array.probe();
371 let mut repeated = array.repeated_probe();
372 for (index, value) in expected.iter().enumerate().rev() {
373 assert_eq!(once.execute_is_valid(index, &mut ctx)?, value.is_some());
374 assert_eq!(repeated.execute_is_valid(index, &mut ctx)?, value.is_some());
375 assert_eq!(once.execute_scalar(index, &mut ctx)?, Scalar::from(*value));
376 assert_eq!(
377 repeated.execute_scalar(index, &mut ctx)?,
378 Scalar::from(*value)
379 );
380 }
381 }
382 Ok(())
383 }
384
385 #[test]
386 fn state_validity_follows_read_policy() -> VortexResult<()> {
387 let mut ctx = crate::array_session().create_execution_ctx();
388 let array = nullable_ints();
389 let typed = array
390 .as_opt::<Primitive>()
391 .ok_or_else(|| vortex_err!("expected a primitive"))?;
392
393 let mut once = ProbeState::once(typed);
394 assert!(once.is_valid(0, &mut ctx)?);
395 assert!(!once.is_valid(1, &mut ctx)?);
396
397 let mut repeated = RepeatedState::<()>::default();
398 let mut state = ProbeState::repeated(typed, &mut repeated);
399 assert!(!state.is_valid(1, &mut ctx)?);
400 assert!(state.is_valid(2, &mut ctx)?);
401
402 let all_valid = PrimitiveArray::from_option_iter([Some(1i32)]).into_array();
403 let typed = all_valid
404 .as_opt::<Primitive>()
405 .ok_or_else(|| vortex_err!("expected a primitive"))?;
406 assert!(ProbeState::once(typed).is_valid(0, &mut ctx)?);
407 Ok(())
408 }
409
410 #[test]
411 fn once_state_reads_children_without_retaining() -> VortexResult<()> {
412 let mut ctx = crate::array_session().create_execution_ctx();
413 let array = struct_of_two_fields()?;
414 let typed = array
415 .as_opt::<Struct>()
416 .ok_or_else(|| vortex_err!("expected a struct"))?;
417 let mut state = ProbeState::once(typed);
418
419 assert!(state.slot(5).is_err());
420 assert!(state.slot(0)?.is_none());
422 {
423 let mut field = state.slot(2)?.ok_or_else(|| vortex_err!("missing field"))?;
424 assert!(matches!(field, ArrayProbe::Once(_)));
425 assert_eq!(field.array().len(), 2);
426 assert_eq!(field.execute_scalar(1, &mut ctx)?, Scalar::from(4i64));
427 assert!(field.execute_is_valid(0, &mut ctx)?);
428 }
429 assert!(state.retained().is_none());
430 let (retained, mut children) = state.split();
431 assert!(retained.is_none());
432 let mut field = children
433 .slot(1)?
434 .ok_or_else(|| vortex_err!("missing field"))?;
435 assert_eq!(field.execute_scalar(1, &mut ctx)?, Scalar::from(2i32));
436 Ok(())
437 }
438
439 fn struct_of_two_fields() -> VortexResult<ArrayRef> {
440 Ok(StructArray::from_fields(&[
441 ("a", PrimitiveArray::from_iter([1i32, 2]).into_array()),
442 ("b", PrimitiveArray::from_iter([3i64, 4]).into_array()),
443 ])?
444 .into_array())
445 }
446}