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}