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