Skip to content

Commit c140bc1

Browse files
committed
Implement feature int_lowest_highest_one for integer and NonZero types
Implement the accepted ACP for methods that find the index of the least significant (lowest) and most significant (highest) set bit in an integer for signed, unsigned, and NonZero types. Also add unit tests for all these types.
1 parent 3672a55 commit c140bc1

File tree

7 files changed

+333
-0
lines changed

7 files changed

+333
-0
lines changed

library/core/src/num/int_macros.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,48 @@ macro_rules! int_impl {
209209
self & self.wrapping_neg()
210210
}
211211

212+
/// Returns the index of the highest bit set to one in `self`, or `None`
213+
/// if `self` is `0`.
214+
///
215+
/// # Examples
216+
///
217+
/// ```
218+
/// #![feature(int_lowest_highest_one)]
219+
///
220+
#[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".highest_one(), None);")]
221+
#[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".highest_one(), Some(0));")]
222+
#[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".highest_one(), Some(4));")]
223+
#[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".highest_one(), Some(4));")]
224+
/// ```
225+
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
226+
#[must_use = "this returns the result of the operation, \
227+
without modifying the original"]
228+
#[inline(always)]
229+
pub const fn highest_one(self) -> Option<u32> {
230+
(self as $UnsignedT).highest_one()
231+
}
232+
233+
/// Returns the index of the lowest bit set to one in `self`, or `None`
234+
/// if `self` is `0`.
235+
///
236+
/// # Examples
237+
///
238+
/// ```
239+
/// #![feature(int_lowest_highest_one)]
240+
///
241+
#[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".lowest_one(), None);")]
242+
#[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".lowest_one(), Some(0));")]
243+
#[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".lowest_one(), Some(4));")]
244+
#[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".lowest_one(), Some(0));")]
245+
/// ```
246+
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
247+
#[must_use = "this returns the result of the operation, \
248+
without modifying the original"]
249+
#[inline(always)]
250+
pub const fn lowest_one(self) -> Option<u32> {
251+
(self as $UnsignedT).lowest_one()
252+
}
253+
212254
/// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size.
213255
///
214256
/// This produces the same result as an `as` cast, but ensures that the bit-width remains

library/core/src/num/nonzero.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,56 @@ macro_rules! nonzero_integer {
681681
unsafe { NonZero::new_unchecked(n) }
682682
}
683683

684+
/// Returns the index of the highest bit set to one in `self`, or `None`
685+
/// if `self` is `0`.
686+
///
687+
/// # Examples
688+
///
689+
/// ```
690+
/// #![feature(int_lowest_highest_one)]
691+
///
692+
/// # use core::num::NonZero;
693+
/// # fn main() { test().unwrap(); }
694+
/// # fn test() -> Option<()> {
695+
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1)?.highest_one(), 0);")]
696+
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x10)?.highest_one(), 4);")]
697+
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1f)?.highest_one(), 4);")]
698+
/// # Some(())
699+
/// # }
700+
/// ```
701+
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
702+
#[must_use = "this returns the result of the operation, \
703+
without modifying the original"]
704+
#[inline(always)]
705+
pub const fn highest_one(self) -> u32 {
706+
Self::BITS - 1 - self.leading_zeros()
707+
}
708+
709+
/// Returns the index of the lowest bit set to one in `self`, or `None`
710+
/// if `self` is `0`.
711+
///
712+
/// # Examples
713+
///
714+
/// ```
715+
/// #![feature(int_lowest_highest_one)]
716+
///
717+
/// # use core::num::NonZero;
718+
/// # fn main() { test().unwrap(); }
719+
/// # fn test() -> Option<()> {
720+
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1)?.lowest_one(), 0);")]
721+
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x10)?.lowest_one(), 4);")]
722+
#[doc = concat!("assert_eq!(NonZero::<", stringify!($Int), ">::new(0x1f)?.lowest_one(), 0);")]
723+
/// # Some(())
724+
/// # }
725+
/// ```
726+
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
727+
#[must_use = "this returns the result of the operation, \
728+
without modifying the original"]
729+
#[inline(always)]
730+
pub const fn lowest_one(self) -> u32 {
731+
self.trailing_zeros()
732+
}
733+
684734
/// Returns the number of ones in the binary representation of `self`.
685735
///
686736
/// # Examples

library/core/src/num/uint_macros.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,54 @@ macro_rules! uint_impl {
261261
self & self.wrapping_neg()
262262
}
263263

264+
/// Returns the index of the highest bit set to one in `self`, or `None`
265+
/// if `self` is `0`.
266+
///
267+
/// # Examples
268+
///
269+
/// ```
270+
/// #![feature(int_lowest_highest_one)]
271+
///
272+
#[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".highest_one(), None);")]
273+
#[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".highest_one(), Some(0));")]
274+
#[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".highest_one(), Some(4));")]
275+
#[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".highest_one(), Some(4));")]
276+
/// ```
277+
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
278+
#[must_use = "this returns the result of the operation, \
279+
without modifying the original"]
280+
#[inline(always)]
281+
pub const fn highest_one(self) -> Option<u32> {
282+
match NonZero::new(self) {
283+
Some(v) => Some(v.highest_one()),
284+
None => None,
285+
}
286+
}
287+
288+
/// Returns the index of the lowest bit set to one in `self`, or `None`
289+
/// if `self` is `0`.
290+
///
291+
/// # Examples
292+
///
293+
/// ```
294+
/// #![feature(int_lowest_highest_one)]
295+
///
296+
#[doc = concat!("assert_eq!(0x0_", stringify!($SelfT), ".lowest_one(), None);")]
297+
#[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".lowest_one(), Some(0));")]
298+
#[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".lowest_one(), Some(4));")]
299+
#[doc = concat!("assert_eq!(0x1f_", stringify!($SelfT), ".lowest_one(), Some(0));")]
300+
/// ```
301+
#[unstable(feature = "int_lowest_highest_one", issue = "145203")]
302+
#[must_use = "this returns the result of the operation, \
303+
without modifying the original"]
304+
#[inline(always)]
305+
pub const fn lowest_one(self) -> Option<u32> {
306+
match NonZero::new(self) {
307+
Some(v) => Some(v.lowest_one()),
308+
None => None,
309+
}
310+
}
311+
264312
/// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size.
265313
///
266314
/// This produces the same result as an `as` cast, but ensures that the bit-width remains

library/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#![feature(generic_assert_internals)]
5555
#![feature(hasher_prefixfree_extras)]
5656
#![feature(hashmap_internals)]
57+
#![feature(int_lowest_highest_one)]
5758
#![feature(int_roundings)]
5859
#![feature(ip)]
5960
#![feature(ip_from)]

library/coretests/tests/nonzero.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,3 +462,121 @@ fn test_nonzero_fmt() {
462462

463463
assert_eq!(i, nz);
464464
}
465+
466+
#[test]
467+
fn test_nonzero_highest_one() {
468+
macro_rules! nonzero_int_impl {
469+
($($T:ty),+) => {
470+
$(
471+
{
472+
const ONE: $T = 1;
473+
const MINUS_ONE: $T = -1;
474+
475+
for i in 0..<$T>::BITS {
476+
// Set single bit.
477+
assert_eq!(NonZero::<$T>::new(ONE << i).unwrap().highest_one(), i);
478+
if i > <$T>::BITS {
479+
// Set lowest bits.
480+
assert_eq!(
481+
NonZero::<$T>::new(<$T>::MAX >> i).unwrap().highest_one(),
482+
<$T>::BITS - i - 2,
483+
);
484+
}
485+
// Set highest bits.
486+
assert_eq!(
487+
NonZero::<$T>::new(MINUS_ONE << i).unwrap().highest_one(),
488+
<$T>::BITS - 1,
489+
);
490+
}
491+
}
492+
)+
493+
};
494+
}
495+
496+
macro_rules! nonzero_uint_impl {
497+
($($T:ty),+) => {
498+
$(
499+
{
500+
const ONE: $T = 1;
501+
502+
for i in 0..<$T>::BITS {
503+
// Set single bit.
504+
assert_eq!(NonZero::<$T>::new(ONE << i).unwrap().highest_one(), i);
505+
// Set lowest bits.
506+
assert_eq!(
507+
NonZero::<$T>::new(<$T>::MAX >> i).unwrap().highest_one(),
508+
<$T>::BITS - i - 1,
509+
);
510+
// Set highest bits.
511+
assert_eq!(
512+
NonZero::<$T>::new(<$T>::MAX << i).unwrap().highest_one(),
513+
<$T>::BITS - 1,
514+
);
515+
}
516+
}
517+
)+
518+
};
519+
}
520+
521+
nonzero_int_impl!(i8, i16, i32, i64, i128, isize);
522+
nonzero_uint_impl!(u8, u16, u32, u64, u128, usize);
523+
}
524+
525+
#[test]
526+
fn test_nonzero_lowest_one() {
527+
macro_rules! nonzero_int_impl {
528+
($($T:ty),+) => {
529+
$(
530+
{
531+
const ONE: $T = 1;
532+
const MINUS_ONE: $T = -1;
533+
534+
for i in 0..<$T>::BITS {
535+
// Set single bit.
536+
assert_eq!(NonZero::<$T>::new(ONE << i).unwrap().lowest_one(), i);
537+
if i > <$T>::BITS {
538+
// Set lowest bits.
539+
assert_eq!(
540+
NonZero::<$T>::new(<$T>::MAX >> i).unwrap().lowest_one(),
541+
0,
542+
);
543+
}
544+
// Set highest bits.
545+
assert_eq!(
546+
NonZero::<$T>::new(MINUS_ONE << i).unwrap().lowest_one(),
547+
i,
548+
);
549+
}
550+
}
551+
)+
552+
};
553+
}
554+
555+
macro_rules! nonzero_uint_impl {
556+
($($T:ty),+) => {
557+
$(
558+
{
559+
const ONE: $T = 1;
560+
561+
for i in 0..<$T>::BITS {
562+
// Set single bit.
563+
assert_eq!(NonZero::<$T>::new(ONE << i).unwrap().lowest_one(), i);
564+
// Set lowest bits.
565+
assert_eq!(
566+
NonZero::<$T>::new(<$T>::MAX >> i).unwrap().lowest_one(),
567+
0,
568+
);
569+
// Set highest bits.
570+
assert_eq!(
571+
NonZero::<$T>::new(<$T>::MAX << i).unwrap().lowest_one(),
572+
i,
573+
);
574+
}
575+
}
576+
)+
577+
};
578+
}
579+
580+
nonzero_int_impl!(i8, i16, i32, i64, i128, isize);
581+
nonzero_uint_impl!(u8, u16, u32, u64, u128, usize);
582+
}

library/coretests/tests/num/int_macros.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,46 @@ macro_rules! int_module {
227227
}
228228
}
229229

230+
#[test]
231+
fn test_highest_one() {
232+
const ZERO: $T = 0;
233+
const ONE: $T = 1;
234+
const MINUS_ONE: $T = -1;
235+
236+
assert_eq!(ZERO.highest_one(), None);
237+
238+
for i in 0..<$T>::BITS {
239+
// Set single bit.
240+
assert_eq!((ONE << i).highest_one(), Some(i));
241+
if i != <$T>::BITS - 1 {
242+
// Set lowest bits.
243+
assert_eq!((<$T>::MAX >> i).highest_one(), Some(<$T>::BITS - i - 2));
244+
}
245+
// Set highest bits.
246+
assert_eq!((MINUS_ONE << i).highest_one(), Some(<$T>::BITS - 1));
247+
}
248+
}
249+
250+
#[test]
251+
fn test_lowest_one() {
252+
const ZERO: $T = 0;
253+
const ONE: $T = 1;
254+
const MINUS_ONE: $T = -1;
255+
256+
assert_eq!(ZERO.lowest_one(), None);
257+
258+
for i in 0..<$T>::BITS {
259+
// Set single bit.
260+
assert_eq!((ONE << i).lowest_one(), Some(i));
261+
if i != <$T>::BITS - 1 {
262+
// Set lowest bits.
263+
assert_eq!((<$T>::MAX >> i).lowest_one(), Some(0));
264+
}
265+
// Set highest bits.
266+
assert_eq!((MINUS_ONE << i).lowest_one(), Some(i));
267+
}
268+
}
269+
230270
#[test]
231271
fn test_from_str() {
232272
fn from_str<T: std::str::FromStr>(t: &str) -> Option<T> {

library/coretests/tests/num/uint_macros.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,40 @@ macro_rules! uint_module {
184184
}
185185
}
186186

187+
#[test]
188+
fn test_highest_one() {
189+
const ZERO: $T = 0;
190+
const ONE: $T = 1;
191+
192+
assert_eq!(ZERO.highest_one(), None);
193+
194+
for i in 0..<$T>::BITS {
195+
// Set single bit.
196+
assert_eq!((ONE << i).highest_one(), Some(i));
197+
// Set lowest bits.
198+
assert_eq!((<$T>::MAX >> i).highest_one(), Some(<$T>::BITS - i - 1));
199+
// Set highest bits.
200+
assert_eq!((<$T>::MAX << i).highest_one(), Some(<$T>::BITS - 1));
201+
}
202+
}
203+
204+
#[test]
205+
fn test_lowest_one() {
206+
const ZERO: $T = 0;
207+
const ONE: $T = 1;
208+
209+
assert_eq!(ZERO.lowest_one(), None);
210+
211+
for i in 0..<$T>::BITS {
212+
// Set single bit.
213+
assert_eq!((ONE << i).lowest_one(), Some(i));
214+
// Set lowest bits.
215+
assert_eq!((<$T>::MAX >> i).lowest_one(), Some(0));
216+
// Set highest bits.
217+
assert_eq!((<$T>::MAX << i).lowest_one(), Some(i));
218+
}
219+
}
220+
187221
fn from_str<T: core::str::FromStr>(t: &str) -> Option<T> {
188222
core::str::FromStr::from_str(t).ok()
189223
}

0 commit comments

Comments
 (0)