Skip to content

Commit e3ecd45

Browse files
authored
Rollup merge of #149097 - okaneco:gather_scatter_bits, r=Mark-Simulacrum
num: Implement `uint_gather_scatter_bits` feature for unsigned integers Feature gate: `#![feature(uint_gather_scatter_bits)]` Tracking issue: #149069 Accepted ACP: rust-lang/libs-team#695 (comment) Implement `gather_bits`, `scatter_bits` functions on unsigned integers Add tests to coretests This implementation is a small improvement over the plain naive form (see the [solution sketch](rust-lang/libs-team#695)). We only check the set bits in the mask instead of iterating over every bit.
2 parents 45c136b + 7f89192 commit e3ecd45

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

library/core/src/num/uint_macros.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,76 @@ macro_rules! uint_impl {
479479
intrinsics::bswap(self as $ActualT) as Self
480480
}
481481

482+
/// Returns an integer with the bit locations specified by `mask` packed
483+
/// contiguously into the least significant bits of the result.
484+
/// ```
485+
/// #![feature(uint_gather_scatter_bits)]
486+
#[doc = concat!("let n: ", stringify!($SelfT), " = 0b1011_1100;")]
487+
///
488+
/// assert_eq!(n.gather_bits(0b0010_0100), 0b0000_0011);
489+
/// assert_eq!(n.gather_bits(0xF0), 0b0000_1011);
490+
/// ```
491+
#[unstable(feature = "uint_gather_scatter_bits", issue = "149069")]
492+
#[must_use = "this returns the result of the operation, \
493+
without modifying the original"]
494+
#[inline]
495+
pub const fn gather_bits(self, mut mask: Self) -> Self {
496+
let mut bit_position = 1;
497+
let mut result = 0;
498+
499+
// Iterate through the mask bits, unsetting the lowest bit after
500+
// each iteration. We fill the bits in the result starting from the
501+
// least significant bit.
502+
while mask != 0 {
503+
// Find the next lowest set bit in the mask
504+
let next_mask_bit = mask.isolate_lowest_one();
505+
506+
// Retrieve the masked bit and if present, set it in the result
507+
let src_bit = (self & next_mask_bit) != 0;
508+
result |= if src_bit { bit_position } else { 0 };
509+
510+
// Unset lowest set bit in the mask, prepare next position to set
511+
mask ^= next_mask_bit;
512+
bit_position <<= 1;
513+
}
514+
515+
result
516+
}
517+
518+
/// Returns an integer with the least significant bits of `self`
519+
/// distributed to the bit locations specified by `mask`.
520+
/// ```
521+
/// #![feature(uint_gather_scatter_bits)]
522+
#[doc = concat!("let n: ", stringify!($SelfT), " = 0b1010_1101;")]
523+
///
524+
/// assert_eq!(n.scatter_bits(0b0101_0101), 0b0101_0001);
525+
/// assert_eq!(n.scatter_bits(0xF0), 0b1101_0000);
526+
/// ```
527+
#[unstable(feature = "uint_gather_scatter_bits", issue = "149069")]
528+
#[must_use = "this returns the result of the operation, \
529+
without modifying the original"]
530+
#[inline]
531+
pub const fn scatter_bits(mut self, mut mask: Self) -> Self {
532+
let mut result = 0;
533+
534+
// Iterate through the mask bits, unsetting the lowest bit after
535+
// each iteration and right-shifting `self` by one to get the next
536+
// bit into the least significant bit position.
537+
while mask != 0 {
538+
// Find the next bit position to potentially set
539+
let next_mask_bit = mask.isolate_lowest_one();
540+
541+
// If bit is set, deposit it at the masked bit position
542+
result |= if (self & 1) != 0 { next_mask_bit } else { 0 };
543+
544+
// Unset lowest set bit in the mask, shift in next `self` bit
545+
mask ^= next_mask_bit;
546+
self >>= 1;
547+
}
548+
549+
result
550+
}
551+
482552
/// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit,
483553
/// second least-significant bit becomes second most-significant bit, etc.
484554
///

library/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
#![feature(try_find)]
115115
#![feature(try_trait_v2)]
116116
#![feature(uint_bit_width)]
117+
#![feature(uint_gather_scatter_bits)]
117118
#![feature(unsize)]
118119
#![feature(unwrap_infallible)]
119120
// tidy-alphabetical-end

library/coretests/tests/num/uint_macros.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,52 @@ macro_rules! uint_module {
127127
assert_eq_const_safe!($T: _1.swap_bytes(), _1);
128128
}
129129

130+
fn test_gather_bits() {
131+
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0011), 0b_0001);
132+
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0110), 0b_0010);
133+
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_1100), 0b_0001);
134+
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0001_1000), 0b_0000);
135+
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0011_0000), 0b_0010);
136+
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0110_0000), 0b_0001);
137+
assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b1100_0000), 0b_0010);
138+
139+
assert_eq_const_safe!($T: A.gather_bits(_0), 0);
140+
assert_eq_const_safe!($T: B.gather_bits(_0), 0);
141+
assert_eq_const_safe!($T: C.gather_bits(_0), 0);
142+
assert_eq_const_safe!($T: _0.gather_bits(A), 0);
143+
assert_eq_const_safe!($T: _0.gather_bits(B), 0);
144+
assert_eq_const_safe!($T: _0.gather_bits(C), 0);
145+
146+
assert_eq_const_safe!($T: A.gather_bits(_1), A);
147+
assert_eq_const_safe!($T: B.gather_bits(_1), B);
148+
assert_eq_const_safe!($T: C.gather_bits(_1), C);
149+
assert_eq_const_safe!($T: _1.gather_bits(0b0010_0001), 0b0000_0011);
150+
assert_eq_const_safe!($T: _1.gather_bits(0b0010_1100), 0b0000_0111);
151+
assert_eq_const_safe!($T: _1.gather_bits(0b0111_1001), 0b0001_1111);
152+
}
153+
154+
fn test_scatter_bits() {
155+
assert_eq_const_safe!($T: $T::scatter_bits(0b1111, 0b1001_0110), 0b1001_0110);
156+
assert_eq_const_safe!($T: $T::scatter_bits(0b0001, 0b1001_0110), 0b0000_0010);
157+
assert_eq_const_safe!($T: $T::scatter_bits(0b0010, 0b1001_0110), 0b0000_0100);
158+
assert_eq_const_safe!($T: $T::scatter_bits(0b0100, 0b1001_0110), 0b0001_0000);
159+
assert_eq_const_safe!($T: $T::scatter_bits(0b1000, 0b1001_0110), 0b1000_0000);
160+
161+
assert_eq_const_safe!($T: A.scatter_bits(_0), 0);
162+
assert_eq_const_safe!($T: B.scatter_bits(_0), 0);
163+
assert_eq_const_safe!($T: C.scatter_bits(_0), 0);
164+
assert_eq_const_safe!($T: _0.scatter_bits(A), 0);
165+
assert_eq_const_safe!($T: _0.scatter_bits(B), 0);
166+
assert_eq_const_safe!($T: _0.scatter_bits(C), 0);
167+
168+
assert_eq_const_safe!($T: A.scatter_bits(_1), A);
169+
assert_eq_const_safe!($T: B.scatter_bits(_1), B);
170+
assert_eq_const_safe!($T: C.scatter_bits(_1), C);
171+
assert_eq_const_safe!($T: _1.scatter_bits(A), A);
172+
assert_eq_const_safe!($T: _1.scatter_bits(B), B);
173+
assert_eq_const_safe!($T: _1.scatter_bits(C), C);
174+
}
175+
130176
fn test_reverse_bits() {
131177
assert_eq_const_safe!($T: A.reverse_bits().reverse_bits(), A);
132178
assert_eq_const_safe!($T: B.reverse_bits().reverse_bits(), B);

0 commit comments

Comments
 (0)