11//! These functions compute the integer logarithm of their type, assuming
22//! that someone has already checked that the value is strictly positive.
33
4+ use crate :: num:: NonZero ;
5+
46// 0 < val <= u8::MAX
57#[ inline]
6- pub ( super ) const fn u8 ( val : u8 ) -> u32 {
8+ const fn u8_impl ( val : u8 ) -> u32 {
79 let val = val as u32 ;
810
911 // For better performance, avoid branches by assembling the solution
@@ -45,13 +47,13 @@ const fn less_than_5(val: u32) -> u32 {
4547
4648// 0 < val <= u16::MAX
4749#[ inline]
48- pub ( super ) const fn u16 ( val : u16 ) -> u32 {
50+ const fn u16_impl ( val : u16 ) -> u32 {
4951 less_than_5 ( val as u32 )
5052}
5153
5254// 0 < val <= u32::MAX
5355#[ inline]
54- pub ( super ) const fn u32 ( mut val : u32 ) -> u32 {
56+ const fn u32_impl ( mut val : u32 ) -> u32 {
5557 let mut log = 0 ;
5658 if val >= 100_000 {
5759 val /= 100_000 ;
@@ -62,7 +64,7 @@ pub(super) const fn u32(mut val: u32) -> u32 {
6264
6365// 0 < val <= u64::MAX
6466#[ inline]
65- pub ( super ) const fn u64 ( mut val : u64 ) -> u32 {
67+ const fn u64_impl ( mut val : u64 ) -> u32 {
6668 let mut log = 0 ;
6769 if val >= 10_000_000_000 {
6870 val /= 10_000_000_000 ;
@@ -77,66 +79,87 @@ pub(super) const fn u64(mut val: u64) -> u32 {
7779
7880// 0 < val <= u128::MAX
7981#[ inline]
80- pub ( super ) const fn u128 ( mut val : u128 ) -> u32 {
82+ const fn u128_impl ( mut val : u128 ) -> u32 {
8183 let mut log = 0 ;
8284 if val >= 100_000_000_000_000_000_000_000_000_000_000 {
8385 val /= 100_000_000_000_000_000_000_000_000_000_000 ;
8486 log += 32 ;
85- return log + u32 ( val as u32 ) ;
87+ return log + u32_impl ( val as u32 ) ;
8688 }
8789 if val >= 10_000_000_000_000_000 {
8890 val /= 10_000_000_000_000_000 ;
8991 log += 16 ;
9092 }
91- log + u64 ( val as u64 )
93+ log + u64_impl ( val as u64 )
9294}
9395
94- # [ cfg ( target_pointer_width = "16" ) ]
95- # [ inline ]
96- pub ( super ) const fn usize ( val : usize ) -> u32 {
97- u16 ( val as _ )
98- }
96+ macro_rules! define_unsigned_ilog10 {
97+ ( $ ( $ty : ident => $impl_fn : ident , ) * ) => { $ (
98+ # [ inline ]
99+ pub ( super ) const fn $ty ( val : NonZero <$ty> ) -> u32 {
100+ let result = $impl_fn ( val . get ( ) ) ;
99101
100- #[ cfg( target_pointer_width = "32" ) ]
101- #[ inline]
102- pub ( super ) const fn usize ( val : usize ) -> u32 {
103- u32 ( val as _ )
104- }
102+ // SAFETY: Integer logarithm is monotonic non-decreasing, so the computed `result` cannot
103+ // exceed the value produced for the maximum input.
104+ unsafe { crate :: hint:: assert_unchecked( result <= const { $impl_fn( $ty:: MAX ) } ) } ;
105105
106- #[ cfg( target_pointer_width = "64" ) ]
107- #[ inline]
108- pub ( super ) const fn usize ( val : usize ) -> u32 {
109- u64 ( val as _ )
106+ result
107+ }
108+ ) * } ;
110109}
111110
112- // 0 < val <= i8::MAX
113- #[ inline]
114- pub ( super ) const fn i8 ( val : i8 ) -> u32 {
115- u8 ( val as u8 )
111+ define_unsigned_ilog10 ! {
112+ u8 => u8_impl,
113+ u16 => u16_impl,
114+ u32 => u32_impl,
115+ u64 => u64_impl,
116+ u128 => u128_impl,
116117}
117118
118- // 0 < val <= i16::MAX
119119#[ inline]
120- pub ( super ) const fn i16 ( val : i16 ) -> u32 {
121- u16 ( val as u16 )
122- }
120+ pub ( super ) const fn usize ( val : NonZero < usize > ) -> u32 {
121+ # [ cfg ( target_pointer_width = "16" ) ]
122+ let impl_fn = u16 ;
123123
124- // 0 < val <= i32::MAX
125- #[ inline]
126- pub ( super ) const fn i32 ( val : i32 ) -> u32 {
127- u32 ( val as u32 )
124+ #[ cfg( target_pointer_width = "32" ) ]
125+ let impl_fn = u32;
126+
127+ #[ cfg( target_pointer_width = "64" ) ]
128+ let impl_fn = u64;
129+
130+ // SAFETY: We have selected the correct `impl_fn`, so the converting `val` to the argument is
131+ // safe.
132+ impl_fn ( unsafe { NonZero :: new_unchecked ( val. get ( ) as _ ) } )
128133}
129134
130- // 0 < val <= i64::MAX
131- #[ inline]
132- pub ( super ) const fn i64 ( val : i64 ) -> u32 {
133- u64 ( val as u64 )
135+ macro_rules! define_signed_ilog10 {
136+ ( $( $ty: ident => $impl_fn: ident, ) * ) => { $(
137+ // 0 < val <= $ty::MAX
138+ #[ inline]
139+ pub ( super ) const fn $ty( val: $ty) -> Option <u32 > {
140+ if val > 0 {
141+ let result = $impl_fn( val. cast_unsigned( ) ) ;
142+
143+ // SAFETY: Integer logarithm is monotonic non-decreasing, so the computed `result`
144+ // cannot exceed the value produced for the maximum input.
145+ unsafe {
146+ crate :: hint:: assert_unchecked( result <= const { $impl_fn( $ty:: MAX . cast_unsigned( ) ) } ) ;
147+ }
148+
149+ Some ( result)
150+ } else {
151+ None
152+ }
153+ }
154+ ) * } ;
134155}
135156
136- // 0 < val <= i128::MAX
137- #[ inline]
138- pub ( super ) const fn i128 ( val : i128 ) -> u32 {
139- u128 ( val as u128 )
157+ define_signed_ilog10 ! {
158+ i8 => u8_impl,
159+ i16 => u16_impl,
160+ i32 => u32_impl,
161+ i64 => u64_impl,
162+ i128 => u128_impl,
140163}
141164
142165/// Instantiate this panic logic once, rather than for all the ilog methods
0 commit comments