simd_kernels/kernels/bitmask/dispatch.rs
1// Copyright Peter Bower 2025. All Rights Reserved.
2// Licensed under Mozilla Public License (MPL) 2.0.
3
4//! # **Bitmask Dispatch Module** - *Compile-Time SIMD/Scalar Selection for Bitmask Operations*
5//!
6//! Dispatcher that selects between SIMD and scalar implementations
7//! at compile time based on feature flags and target architecture capabilities.
8//!
9//! Prefer this unless you want to access the underlying kernel functions directly.
10
11include!(concat!(env!("OUT_DIR"), "/simd_lanes.rs"));
12
13use crate::operators::{LogicalOperator, UnaryOperator};
14use minarrow::{Bitmask, BitmaskVT};
15
16// --- Binary/Unary Bitmask Operations ---
17
18/// Performs a binary logical operation (AND, OR, XOR) on two bitmask windows with automatic SIMD/scalar dispatch.
19///
20/// Executes the specified logical operation element-wise across two bitmask windows, producing a new bitmask
21/// containing the results. The implementation is automatically selected based on compile-time feature flags.
22///
23/// # Parameters
24/// - `lhs`: Left-hand side bitmask window as `(mask, offset, length)` tuple
25/// - `rhs`: Right-hand side bitmask window as `(mask, offset, length)` tuple
26/// - `op`: Logical operation to perform (AND, OR, XOR)
27///
28/// # Returns
29/// A new `Bitmask` containing the element-wise results of the logical operation.
30///
31/// # Performance Notes
32/// - SIMD path processes multiple u64 words simultaneously for improved throughput
33/// - Scalar path provides universal compatibility with word-level optimisations
34/// - Output bitmask automatically handles trailing bit masking for correctness
35#[inline(always)]
36pub fn bitmask_binop(lhs: BitmaskVT<'_>, rhs: BitmaskVT<'_>, op: LogicalOperator) -> Bitmask {
37 #[cfg(feature = "simd")]
38 {
39 crate::kernels::bitmask::simd::bitmask_binop_simd::<W8>(lhs, rhs, op)
40 }
41 #[cfg(not(feature = "simd"))]
42 {
43 crate::kernels::bitmask::std::bitmask_binop_std(lhs, rhs, op)
44 }
45}
46
47/// Performs a unary operation (`NOT`) on a bitmask window.
48#[inline(always)]
49pub fn bitmask_unop(src: BitmaskVT<'_>, op: UnaryOperator) -> Bitmask {
50 #[cfg(feature = "simd")]
51 {
52 crate::kernels::bitmask::simd::bitmask_unop_simd::<W8>(src, op)
53 }
54 #[cfg(not(feature = "simd"))]
55 {
56 crate::kernels::bitmask::std::bitmask_unop_std(src, op)
57 }
58}
59
60// --- Entry Points (Standard Logical Operations) ---
61
62/// Computes the element-wise bitwise AND of two bitmask windows for intersection operations.
63///
64/// Performs logical AND across corresponding bits in two bitmask windows, commonly used for
65/// combining null masks in nullable array operations. The result bit is set only when both
66/// input bits are set.
67///
68/// # Parameters
69/// - `lhs`: Left-hand side bitmask window as `(mask, offset, length)` tuple
70/// - `rhs`: Right-hand side bitmask window as `(mask, offset, length)` tuple
71///
72/// # Returns
73/// A new `Bitmask` where each bit represents `lhs[i] AND rhs[i]`.
74///
75/// # Usage
76/// ```rust,ignore
77/// // Combine validity masks from two nullable arrays
78/// let combined_validity = and_masks(
79/// (&array_a.null_mask, 0, array_a.len()),
80/// (&array_b.null_mask, 0, array_b.len())
81/// );
82/// // Result contains valid elements only where both arrays are non-null
83/// ```
84#[inline(always)]
85pub fn and_masks(lhs: BitmaskVT<'_>, rhs: BitmaskVT<'_>) -> Bitmask {
86 #[cfg(feature = "simd")]
87 {
88 crate::kernels::bitmask::simd::and_masks_simd::<W8>(lhs, rhs)
89 }
90 #[cfg(not(feature = "simd"))]
91 {
92 crate::kernels::bitmask::std::and_masks(lhs, rhs)
93 }
94}
95
96/// Computes the element-wise bitwise OR of two bitmask windows.
97#[inline(always)]
98pub fn or_masks(lhs: BitmaskVT<'_>, rhs: BitmaskVT<'_>) -> Bitmask {
99 #[cfg(feature = "simd")]
100 {
101 crate::kernels::bitmask::simd::or_masks_simd::<W8>(lhs, rhs)
102 }
103 #[cfg(not(feature = "simd"))]
104 {
105 crate::kernels::bitmask::std::or_masks(lhs, rhs)
106 }
107}
108
109/// Computes the element-wise bitwise XOR of two bitmask windows.
110#[inline(always)]
111pub fn xor_masks(lhs: BitmaskVT<'_>, rhs: BitmaskVT<'_>) -> Bitmask {
112 #[cfg(feature = "simd")]
113 {
114 crate::kernels::bitmask::simd::xor_masks_simd::<W8>(lhs, rhs)
115 }
116 #[cfg(not(feature = "simd"))]
117 {
118 crate::kernels::bitmask::std::xor_masks(lhs, rhs)
119 }
120}
121
122/// Computes the element-wise bitwise NOT of a bitmask window.
123#[inline(always)]
124pub fn not_mask(src: BitmaskVT<'_>) -> Bitmask {
125 #[cfg(feature = "simd")]
126 {
127 crate::kernels::bitmask::simd::not_mask_simd::<W8>(src)
128 }
129 #[cfg(not(feature = "simd"))]
130 {
131 crate::kernels::bitmask::std::not_mask(src)
132 }
133}
134
135// --- Set Logic ---
136
137/// Performs bitwise inclusion: output bit is true if lhs bit is present in rhs bit-set.
138#[inline(always)]
139pub fn in_mask(lhs: BitmaskVT<'_>, rhs: BitmaskVT<'_>) -> Bitmask {
140 #[cfg(feature = "simd")]
141 {
142 crate::kernels::bitmask::simd::in_mask_simd::<W8>(lhs, rhs)
143 }
144 #[cfg(not(feature = "simd"))]
145 {
146 crate::kernels::bitmask::std::in_mask(lhs, rhs)
147 }
148}
149
150/// Performs bitwise exclusion: output bit is true if lhs bit is not present in rhs bit-set.
151#[inline(always)]
152pub fn not_in_mask(lhs: BitmaskVT<'_>, rhs: BitmaskVT<'_>) -> Bitmask {
153 #[cfg(feature = "simd")]
154 {
155 crate::kernels::bitmask::simd::not_in_mask_simd::<W8>(lhs, rhs)
156 }
157 #[cfg(not(feature = "simd"))]
158 {
159 crate::kernels::bitmask::std::not_in_mask(lhs, rhs)
160 }
161}
162
163// --- Equality/Inequality Masks ---
164
165/// Computes an equality mask: output bit is true if bits are equal at each position.
166#[inline(always)]
167pub fn eq_mask(a: BitmaskVT<'_>, b: BitmaskVT<'_>) -> Bitmask {
168 #[cfg(feature = "simd")]
169 {
170 crate::kernels::bitmask::simd::eq_mask_simd::<W8>(a, b)
171 }
172 #[cfg(not(feature = "simd"))]
173 {
174 crate::kernels::bitmask::std::eq_mask(a, b)
175 }
176}
177
178/// Computes an inequality mask: output bit is true if bits differ at each position.
179#[inline(always)]
180pub fn ne_mask(a: BitmaskVT<'_>, b: BitmaskVT<'_>) -> Bitmask {
181 #[cfg(feature = "simd")]
182 {
183 crate::kernels::bitmask::simd::ne_mask_simd::<W8>(a, b)
184 }
185 #[cfg(not(feature = "simd"))]
186 {
187 crate::kernels::bitmask::std::ne_mask(a, b)
188 }
189}
190
191/// Returns true if all bits are equal between two bitmask windows.
192#[inline(always)]
193pub fn all_eq(a: BitmaskVT<'_>, b: BitmaskVT<'_>) -> bool {
194 #[cfg(feature = "simd")]
195 {
196 crate::kernels::bitmask::simd::all_eq_mask_simd::<W8>(a, b)
197 }
198 #[cfg(not(feature = "simd"))]
199 {
200 crate::kernels::bitmask::std::all_eq_mask(a, b)
201 }
202}
203
204/// Returns true if all bits differ between two bitmask windows.
205#[inline(always)]
206pub fn all_ne(a: BitmaskVT<'_>, b: BitmaskVT<'_>) -> bool {
207 #[cfg(feature = "simd")]
208 {
209 crate::kernels::bitmask::simd::all_ne_mask_simd::<W8>(a, b)
210 }
211 #[cfg(not(feature = "simd"))]
212 {
213 crate::kernels::bitmask::std::all_ne_mask(a, b)
214 }
215}
216
217// --- Popcount ---
218
219/// Returns the number of set bits (population count) in the bitmask window using fast SIMD reduction.
220///
221/// Counts the number of `1` bits in the specified bitmask window, commonly used to determine
222/// the number of valid (non-null) elements in nullable arrays. The implementation uses
223/// vectorised population count instructions for optimal performance.
224///
225/// # Parameters
226/// - `m`: Bitmask window as `(mask, offset, length)` tuple
227///
228/// # Returns
229/// The count of set bits in the specified window as a `usize`.
230///
231/// # Performance Characteristics
232/// - SIMD path uses vectorised `popcount` instructions with SIMD reduction
233/// - Automatically handles partial words and trailing bit masking
234/// - O(n/64) complexity for word-aligned operations
235///
236/// # Usage
237/// ```rust,ignore
238/// // Determine if computation is worthwhile
239/// let valid_count = popcount_mask((&validity_mask, 0, array.len()));
240/// if valid_count == 0 {
241/// // Skip expensive operations on all-null data
242/// return Array::new_null(array.len());
243/// }
244/// // Proceed with computation on `valid_count` elements
245/// ```
246#[inline(always)]
247pub fn popcount_mask(m: BitmaskVT<'_>) -> usize {
248 #[cfg(feature = "simd")]
249 {
250 crate::kernels::bitmask::simd::popcount_mask_simd::<W8>(m)
251 }
252 #[cfg(not(feature = "simd"))]
253 {
254 crate::kernels::bitmask::std::popcount_mask(m)
255 }
256}
257
258// --- All-True / All-False Checks ---
259
260/// Returns true if all bits in the mask are set (1).
261#[inline(always)]
262pub fn all_true_mask(mask: &Bitmask) -> bool {
263 #[cfg(feature = "simd")]
264 {
265 crate::kernels::bitmask::simd::all_true_mask_simd::<W8>(mask)
266 }
267 #[cfg(not(feature = "simd"))]
268 {
269 crate::kernels::bitmask::std::all_true_mask(mask)
270 }
271}
272
273/// Returns true if all bits in the mask are unset (0).
274#[inline(always)]
275pub fn all_false_mask(mask: &Bitmask) -> bool {
276 #[cfg(feature = "simd")]
277 {
278 crate::kernels::bitmask::simd::all_false_mask_simd::<W8>(mask)
279 }
280 #[cfg(not(feature = "simd"))]
281 {
282 crate::kernels::bitmask::std::all_false_mask(mask)
283 }
284}