Skip to main content

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}