rename uN::{gather,scatter}_bits to uN::{extract,deposit}_bits

This commit is contained in:
Juho Kahala 2026-01-26 06:28:42 +02:00
parent a18e6d9d14
commit 29596f87be
4 changed files with 68 additions and 68 deletions

View file

@ -1,12 +1,12 @@
//! Implementations for `uN::gather_bits` and `uN::scatter_bits`
//! Implementations for `uN::extract_bits` and `uN::deposit_bits`
//!
//! For the purposes of this implementation, the operations can be thought
//! of as operating on the input bits as a list, starting from the least
//! significant bit. Gathering is like `Vec::retain` that deletes bits
//! where the mask has a zero. Scattering is like doing the inverse by
//! inserting the zeros that gathering would delete.
//! significant bit. Extraction is like `Vec::retain` that deletes bits
//! where the mask has a zero. Deposition is like doing the inverse by
//! inserting the zeros that extraction would delete.
//!
//! Key observation: Each bit that is gathered/scattered needs to be
//! Key observation: Each extracted or deposited bit needs to be
//! shifted by the count of zeros up to the corresponding mask bit.
//!
//! With that in mind, the general idea is to decompose the operation into
@ -14,7 +14,7 @@
//! of the bits by `n = 1 << stage`. The masks for each stage are computed
//! via prefix counts of zeros in the mask.
//!
//! # Gathering
//! # Extraction
//!
//! Consider the input as a sequence of runs of data (bitstrings A,B,C,...),
//! split by fixed-width groups of zeros ('.'), initially at width `n = 1`.
@ -36,9 +36,9 @@
//! ........abbbcccccddeghh
//! ```
//!
//! # Scattering
//! # Deposition
//!
//! For `scatter_bits`, the stages are reversed. We start with a single run of
//! For `deposit_bits`, the stages are reversed. We start with a single run of
//! data in the low bits. Each stage then splits each run of data in two by
//! shifting part of it left by `n`, which is halved each stage.
//! ```text
@ -100,7 +100,7 @@ macro_rules! uint_impl {
}
#[inline(always)]
pub(in super::super) const fn gather_impl(mut x: $U, sparse: $U) -> $U {
pub(in super::super) const fn extract_impl(mut x: $U, sparse: $U) -> $U {
let masks = prepare(sparse);
x &= sparse;
let mut stage = 0;
@ -131,7 +131,7 @@ macro_rules! uint_impl {
x
}
#[inline(always)]
pub(in super::super) const fn scatter_impl(mut x: $U, sparse: $U) -> $U {
pub(in super::super) const fn deposit_impl(mut x: $U, sparse: $U) -> $U {
let masks = prepare(sparse);
let mut stage = STAGES;
while stage > 0 {

View file

@ -507,15 +507,15 @@ macro_rules! uint_impl {
/// #![feature(uint_gather_scatter_bits)]
#[doc = concat!("let n: ", stringify!($SelfT), " = 0b1011_1100;")]
///
/// assert_eq!(n.gather_bits(0b0010_0100), 0b0000_0011);
/// assert_eq!(n.gather_bits(0xF0), 0b0000_1011);
/// assert_eq!(n.extract_bits(0b0010_0100), 0b0000_0011);
/// assert_eq!(n.extract_bits(0xF0), 0b0000_1011);
/// ```
#[unstable(feature = "uint_gather_scatter_bits", issue = "149069")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn gather_bits(self, mask: Self) -> Self {
crate::num::int_bits::$ActualT::gather_impl(self as $ActualT, mask as $ActualT) as $SelfT
pub const fn extract_bits(self, mask: Self) -> Self {
crate::num::int_bits::$ActualT::extract_impl(self as $ActualT, mask as $ActualT) as $SelfT
}
/// Returns an integer with the least significant bits of `self`
@ -524,15 +524,15 @@ macro_rules! uint_impl {
/// #![feature(uint_gather_scatter_bits)]
#[doc = concat!("let n: ", stringify!($SelfT), " = 0b1010_1101;")]
///
/// assert_eq!(n.scatter_bits(0b0101_0101), 0b0101_0001);
/// assert_eq!(n.scatter_bits(0xF0), 0b1101_0000);
/// assert_eq!(n.deposit_bits(0b0101_0101), 0b0101_0001);
/// assert_eq!(n.deposit_bits(0xF0), 0b1101_0000);
/// ```
#[unstable(feature = "uint_gather_scatter_bits", issue = "149069")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn scatter_bits(self, mask: Self) -> Self {
crate::num::int_bits::$ActualT::scatter_impl(self as $ActualT, mask as $ActualT) as $SelfT
pub const fn deposit_bits(self, mask: Self) -> Self {
crate::num::int_bits::$ActualT::deposit_impl(self as $ActualT, mask as $ActualT) as $SelfT
}
/// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit,

View file

@ -50,8 +50,8 @@ macro_rules! bench_mask_kind {
($mask_kind:ident, $mask:expr) => {
mod $mask_kind {
use super::{Data, ITERATIONS, U};
bench_template!(U::gather_bits, gather_bits, $mask);
bench_template!(U::scatter_bits, scatter_bits, $mask);
bench_template!(U::extract_bits, extract_bits, $mask);
bench_template!(U::deposit_bits, deposit_bits, $mask);
}
};
}

View file

@ -127,50 +127,50 @@ macro_rules! uint_module {
assert_eq_const_safe!($T: _1.swap_bytes(), _1);
}
fn test_gather_bits() {
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0011), 0b_0001);
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0110), 0b_0010);
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_1100), 0b_0001);
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0001_1000), 0b_0000);
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0011_0000), 0b_0010);
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0110_0000), 0b_0001);
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b1100_0000), 0b_0010);
fn test_extract_bits() {
assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_0011), 0b_0001);
assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_0110), 0b_0010);
assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_1100), 0b_0001);
assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0001_1000), 0b_0000);
assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0011_0000), 0b_0010);
assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0110_0000), 0b_0001);
assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b1100_0000), 0b_0010);
assert_eq_const_safe!($T: A.gather_bits(_0), 0);
assert_eq_const_safe!($T: B.gather_bits(_0), 0);
assert_eq_const_safe!($T: C.gather_bits(_0), 0);
assert_eq_const_safe!($T: _0.gather_bits(A), 0);
assert_eq_const_safe!($T: _0.gather_bits(B), 0);
assert_eq_const_safe!($T: _0.gather_bits(C), 0);
assert_eq_const_safe!($T: A.extract_bits(_0), 0);
assert_eq_const_safe!($T: B.extract_bits(_0), 0);
assert_eq_const_safe!($T: C.extract_bits(_0), 0);
assert_eq_const_safe!($T: _0.extract_bits(A), 0);
assert_eq_const_safe!($T: _0.extract_bits(B), 0);
assert_eq_const_safe!($T: _0.extract_bits(C), 0);
assert_eq_const_safe!($T: A.gather_bits(_1), A);
assert_eq_const_safe!($T: B.gather_bits(_1), B);
assert_eq_const_safe!($T: C.gather_bits(_1), C);
assert_eq_const_safe!($T: _1.gather_bits(0b0010_0001), 0b0000_0011);
assert_eq_const_safe!($T: _1.gather_bits(0b0010_1100), 0b0000_0111);
assert_eq_const_safe!($T: _1.gather_bits(0b0111_1001), 0b0001_1111);
assert_eq_const_safe!($T: A.extract_bits(_1), A);
assert_eq_const_safe!($T: B.extract_bits(_1), B);
assert_eq_const_safe!($T: C.extract_bits(_1), C);
assert_eq_const_safe!($T: _1.extract_bits(0b0010_0001), 0b0000_0011);
assert_eq_const_safe!($T: _1.extract_bits(0b0010_1100), 0b0000_0111);
assert_eq_const_safe!($T: _1.extract_bits(0b0111_1001), 0b0001_1111);
}
fn test_scatter_bits() {
assert_eq_const_safe!($T: $T::scatter_bits(0b1111, 0b1001_0110), 0b1001_0110);
assert_eq_const_safe!($T: $T::scatter_bits(0b0001, 0b1001_0110), 0b0000_0010);
assert_eq_const_safe!($T: $T::scatter_bits(0b0010, 0b1001_0110), 0b0000_0100);
assert_eq_const_safe!($T: $T::scatter_bits(0b0100, 0b1001_0110), 0b0001_0000);
assert_eq_const_safe!($T: $T::scatter_bits(0b1000, 0b1001_0110), 0b1000_0000);
fn test_deposit_bits() {
assert_eq_const_safe!($T: $T::deposit_bits(0b1111, 0b1001_0110), 0b1001_0110);
assert_eq_const_safe!($T: $T::deposit_bits(0b0001, 0b1001_0110), 0b0000_0010);
assert_eq_const_safe!($T: $T::deposit_bits(0b0010, 0b1001_0110), 0b0000_0100);
assert_eq_const_safe!($T: $T::deposit_bits(0b0100, 0b1001_0110), 0b0001_0000);
assert_eq_const_safe!($T: $T::deposit_bits(0b1000, 0b1001_0110), 0b1000_0000);
assert_eq_const_safe!($T: A.scatter_bits(_0), 0);
assert_eq_const_safe!($T: B.scatter_bits(_0), 0);
assert_eq_const_safe!($T: C.scatter_bits(_0), 0);
assert_eq_const_safe!($T: _0.scatter_bits(A), 0);
assert_eq_const_safe!($T: _0.scatter_bits(B), 0);
assert_eq_const_safe!($T: _0.scatter_bits(C), 0);
assert_eq_const_safe!($T: A.deposit_bits(_0), 0);
assert_eq_const_safe!($T: B.deposit_bits(_0), 0);
assert_eq_const_safe!($T: C.deposit_bits(_0), 0);
assert_eq_const_safe!($T: _0.deposit_bits(A), 0);
assert_eq_const_safe!($T: _0.deposit_bits(B), 0);
assert_eq_const_safe!($T: _0.deposit_bits(C), 0);
assert_eq_const_safe!($T: A.scatter_bits(_1), A);
assert_eq_const_safe!($T: B.scatter_bits(_1), B);
assert_eq_const_safe!($T: C.scatter_bits(_1), C);
assert_eq_const_safe!($T: _1.scatter_bits(A), A);
assert_eq_const_safe!($T: _1.scatter_bits(B), B);
assert_eq_const_safe!($T: _1.scatter_bits(C), C);
assert_eq_const_safe!($T: A.deposit_bits(_1), A);
assert_eq_const_safe!($T: B.deposit_bits(_1), B);
assert_eq_const_safe!($T: C.deposit_bits(_1), C);
assert_eq_const_safe!($T: _1.deposit_bits(A), A);
assert_eq_const_safe!($T: _1.deposit_bits(B), B);
assert_eq_const_safe!($T: _1.deposit_bits(C), C);
}
fn test_reverse_bits() {
@ -389,7 +389,7 @@ macro_rules! uint_module {
#[cfg(not(miri))] // Miri is too slow
#[test]
fn test_lots_of_gather_scatter() {
fn test_lots_of_extract_deposit() {
// Generate a handful of bit patterns to use as inputs
let xs = {
let mut xs = vec![];
@ -414,7 +414,7 @@ macro_rules! uint_module {
for sparse in sparse_masks {
// Collect the set bits to sequential low bits
let dense = sparse.gather_bits(sparse);
let dense = sparse.extract_bits(sparse);
let count = sparse.count_ones();
assert_eq!(count, dense.count_ones());
assert_eq!(count, dense.trailing_ones());
@ -424,27 +424,27 @@ macro_rules! uint_module {
let mut bit = 1 as $T;
for _ in 0..count {
let lowest_one = t.isolate_lowest_one();
assert_eq!(lowest_one, bit.scatter_bits(sparse));
assert_eq!(bit, lowest_one.gather_bits(sparse));
assert_eq!(lowest_one, bit.deposit_bits(sparse));
assert_eq!(bit, lowest_one.extract_bits(sparse));
t ^= lowest_one;
bit <<= 1;
}
// Other bits are ignored
assert_eq!(0, bit.wrapping_neg().scatter_bits(sparse));
assert_eq!(0, (!sparse).gather_bits(sparse));
assert_eq!(0, bit.wrapping_neg().deposit_bits(sparse));
assert_eq!(0, (!sparse).extract_bits(sparse));
for &x in &xs {
// Gather bits from `x & sparse` to `dense`
let dx = x.gather_bits(sparse);
let dx = x.extract_bits(sparse);
assert_eq!(dx & !dense, 0);
// Scatter bits from `x & dense` to `sparse`
let sx = x.scatter_bits(sparse);
let sx = x.deposit_bits(sparse);
assert_eq!(sx & !sparse, 0);
// The other recovers the input (within the mask)
assert_eq!(dx.scatter_bits(sparse), x & sparse);
assert_eq!(sx.gather_bits(sparse), x & dense);
assert_eq!(dx.deposit_bits(sparse), x & sparse);
assert_eq!(sx.extract_bits(sparse), x & dense);
}
}
}