11// SPDX-License-Identifier: Apache-2.0
22// SPDX-FileCopyrightText: Copyright the Vortex contributors
33
4- //! Native comparison of primitive arrays via bit-packing lane kernels.
4+ //! Primitive comparison execution through [`RowFn`].
5+ //!
6+ //! [`PrimitiveCompare`] delegates decoding, constant handling, validity, and packed Boolean output
7+ //! to the row executor. Its row kernel contains only the comparison selected by [`CompareOperator`].
58
6- use vortex_buffer:: BitBuffer ;
79use vortex_error:: VortexResult ;
810use vortex_error:: vortex_bail;
911
1012use crate :: ArrayRef ;
1113use crate :: ExecutionCtx ;
12- use crate :: IntoArray ;
13- use crate :: arrays:: BoolArray ;
14- use crate :: arrays:: ConstantArray ;
1514use crate :: dtype:: DType ;
1615use crate :: dtype:: NativePType ;
17- use crate :: dtype:: Nullability ;
1816use crate :: dtype:: PType ;
1917use crate :: match_each_native_ptype;
20- use crate :: scalar:: Scalar ;
21- use crate :: scalar_fn:: fns:: binary:: compare:: collect_bits;
22- use crate :: scalar_fn:: fns:: binary:: compare:: collect_zip_bits;
23- use crate :: scalar_fn:: fns:: binary:: compare:: compare_validity;
24- use crate :: scalar_fn:: fns:: binary:: primitive_operand:: PrimitiveOperand ;
18+ use crate :: scalar_fn:: ScalarFnId ;
19+ use crate :: scalar_fn:: ScalarFnVTable ;
20+ use crate :: scalar_fn:: VecExecutionArgs ;
21+ use crate :: scalar_fn:: fns:: binary:: Binary ;
2522use crate :: scalar_fn:: fns:: operators:: CompareOperator ;
23+ use crate :: scalar_fn:: unstable:: row:: RowFn ;
24+ use crate :: scalar_fn:: unstable:: row:: RowVisitor ;
25+ use crate :: scalar_fn:: unstable:: row:: execute_rows;
2626
2727/// Compare two primitive arrays of the same [`PType`].
2828///
@@ -32,99 +32,71 @@ pub(super) fn compare_primitive(
3232 lhs : & ArrayRef ,
3333 rhs : & ArrayRef ,
3434 op : CompareOperator ,
35- nullability : Nullability ,
3635 ctx : & mut ExecutionCtx ,
3736) -> VortexResult < ArrayRef > {
38- let ptype = PType :: try_from ( lhs. dtype ( ) ) ?;
39- match_each_native_ptype ! ( ptype, |T | {
40- compare_primitive_typed:: <T >( lhs, rhs, op, nullability, ctx)
41- } )
37+ let args = VecExecutionArgs :: new ( vec ! [ lhs. clone( ) , rhs. clone( ) ] , lhs. len ( ) ) ;
38+
39+ execute_rows ( & PrimitiveCompare , & op, & args, ctx)
4240}
4341
44- fn compare_primitive_typed < T : NativePType > (
45- lhs : & ArrayRef ,
46- rhs : & ArrayRef ,
47- op : CompareOperator ,
48- nullability : Nullability ,
49- ctx : & mut ExecutionCtx ,
50- ) -> VortexResult < ArrayRef > {
51- let len = lhs. len ( ) ;
52- let lhs = PrimitiveOperand :: < T > :: try_new ( lhs, ctx) ?;
53- let rhs = PrimitiveOperand :: < T > :: try_new ( rhs, ctx) ?;
54- if lhs. len ( ) != rhs. len ( ) {
55- vortex_bail ! (
56- "compare operator requires equal lengths, got {} and {}" ,
57- lhs. len( ) ,
58- rhs. len( )
59- ) ;
60- }
42+ /// Internal row execution for primitive comparison operators.
43+ #[ derive( Clone ) ]
44+ struct PrimitiveCompare ;
6145
62- let validity = compare_validity ( lhs. validity ( ) , rhs. validity ( ) , nullability) ?;
46+ impl RowFn for PrimitiveCompare {
47+ type Options = CompareOperator ;
6348
64- let bits = match ( & lhs, & rhs) {
65- (
66- PrimitiveOperand :: Array { values : lhs, .. } ,
67- PrimitiveOperand :: Array { values : rhs, .. } ,
68- ) => compare_slices ( lhs, rhs, op) ,
69- (
70- PrimitiveOperand :: Array { values : lhs, .. } ,
71- PrimitiveOperand :: Constant { value : rhs, .. } ,
72- ) => compare_slice_constant ( lhs, * rhs, op) ,
73- (
74- PrimitiveOperand :: Constant { value : lhs, .. } ,
75- PrimitiveOperand :: Array { values : rhs, .. } ,
76- ) => compare_slice_constant ( rhs, * lhs, op. swap ( ) ) ,
77- (
78- PrimitiveOperand :: Constant { value : lhs, .. } ,
79- PrimitiveOperand :: Constant { value : rhs, .. } ,
80- ) => {
81- // Unreachable through `execute_compare` (constant-constant is folded there), but
82- // cheap to answer anyway.
83- BitBuffer :: full ( apply_op ( * lhs, * rhs, op) , len)
84- }
85- ( PrimitiveOperand :: Null ( _) , _) | ( _, PrimitiveOperand :: Null ( _) ) => {
86- return Ok (
87- ConstantArray :: new ( Scalar :: null ( DType :: Bool ( Nullability :: Nullable ) ) , len)
88- . into_array ( ) ,
89- ) ;
90- }
91- } ;
49+ const ARG_NAMES : & ' static [ & ' static str ] = & [ "lhs" , "rhs" ] ;
9250
93- Ok ( BoolArray :: try_new ( bits, validity) ?. into_array ( ) )
94- }
51+ const INFALLIBLE : bool = true ;
9552
96- #[ inline( always) ]
97- fn apply_op < T : NativePType > ( lhs : T , rhs : T , op : CompareOperator ) -> bool {
98- match op {
99- CompareOperator :: Eq => lhs. is_eq ( rhs) ,
100- CompareOperator :: NotEq => !lhs. is_eq ( rhs) ,
101- CompareOperator :: Gt => lhs. is_gt ( rhs) ,
102- CompareOperator :: Gte => lhs. is_ge ( rhs) ,
103- CompareOperator :: Lt => lhs. is_lt ( rhs) ,
104- CompareOperator :: Lte => lhs. is_le ( rhs) ,
53+ fn id ( & self ) -> ScalarFnId {
54+ // `PrimitiveCompare` is a private implementation detail of `Binary`: it is never registered
55+ // or serialized independently. Reusing the public ID keeps execution errors attributed to
56+ // `Binary`. If this type becomes registrable, it needs its own ID and persistence contract.
57+ ScalarFnVTable :: id ( & Binary )
10558 }
106- }
10759
108- fn compare_slices < T : NativePType > ( lhs : & [ T ] , rhs : & [ T ] , op : CompareOperator ) -> BitBuffer {
109- // Dispatch the operator outside the lane loop so each instantiation vectorizes a single
110- // branch-free predicate.
111- match op {
112- CompareOperator :: Eq => collect_zip_bits ( lhs, rhs, |a : T , b : T | a. is_eq ( b) ) ,
113- CompareOperator :: NotEq => collect_zip_bits ( lhs, rhs, |a : T , b : T | !a. is_eq ( b) ) ,
114- CompareOperator :: Gt => collect_zip_bits ( lhs, rhs, T :: is_gt) ,
115- CompareOperator :: Gte => collect_zip_bits ( lhs, rhs, T :: is_ge) ,
116- CompareOperator :: Lt => collect_zip_bits ( lhs, rhs, T :: is_lt) ,
117- CompareOperator :: Lte => collect_zip_bits ( lhs, rhs, T :: is_le) ,
60+ fn dispatch < V : RowVisitor > (
61+ & self ,
62+ op : & Self :: Options ,
63+ args : & [ DType ] ,
64+ visitor : V ,
65+ ) -> VortexResult < V :: VisitResult > {
66+ let [ lhs_dtype, _] = args else {
67+ vortex_bail ! (
68+ "a primitive comparison requires two operands, got {}" ,
69+ args. len( ) ,
70+ ) ;
71+ } ;
72+ let ptype = PType :: try_from ( lhs_dtype) ?;
73+
74+ match_each_native_ptype ! ( ptype, |T | { visit_compare:: <T , V >( * op, visitor) } )
11875 }
11976}
12077
121- fn compare_slice_constant < T : NativePType > ( lhs : & [ T ] , rhs : T , op : CompareOperator ) -> BitBuffer {
78+ fn visit_compare < T , V > ( op : CompareOperator , visitor : V ) -> VortexResult < V :: VisitResult >
79+ where
80+ T : NativePType ,
81+ V : RowVisitor ,
82+ {
12283 match op {
123- CompareOperator :: Eq => collect_bits ( lhs , | a : T | a . is_eq ( rhs ) ) ,
124- CompareOperator :: NotEq => collect_bits ( lhs , |a : T | !a . is_eq ( rhs) ) ,
125- CompareOperator :: Gt => collect_bits ( lhs , | a : T | a . is_gt ( rhs ) ) ,
126- CompareOperator :: Gte => collect_bits ( lhs , | a : T | a . is_ge ( rhs ) ) ,
127- CompareOperator :: Lt => collect_bits ( lhs , | a : T | a . is_lt ( rhs ) ) ,
128- CompareOperator :: Lte => collect_bits ( lhs , | a : T | a . is_le ( rhs ) ) ,
84+ CompareOperator :: Eq => visit_compare_with :: < T , V > ( visitor , T :: is_eq) ,
85+ CompareOperator :: NotEq => visit_compare_with :: < T , V > ( visitor , |lhs , rhs | !lhs . is_eq ( rhs) ) ,
86+ CompareOperator :: Gt => visit_compare_with :: < T , V > ( visitor , T :: is_gt) ,
87+ CompareOperator :: Gte => visit_compare_with :: < T , V > ( visitor , T :: is_ge) ,
88+ CompareOperator :: Lt => visit_compare_with :: < T , V > ( visitor , T :: is_lt) ,
89+ CompareOperator :: Lte => visit_compare_with :: < T , V > ( visitor , T :: is_le) ,
12990 }
13091}
92+
93+ fn visit_compare_with < T , V > (
94+ visitor : V ,
95+ compare : impl Fn ( T , T ) -> bool ,
96+ ) -> VortexResult < V :: VisitResult >
97+ where
98+ T : NativePType ,
99+ V : RowVisitor ,
100+ {
101+ visitor. visit_bool :: < ( T , T ) , true > ( move |( lhs, rhs) | compare ( lhs, rhs) )
102+ }
0 commit comments