bit_int/
bit_uint.rs

1// SPDX-FileCopyrightText: 2024 Shun Sakai
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! An arbitrary fixed bit-width unsigned integer.
6
7mod cmp;
8mod consts;
9mod convert;
10mod fmt;
11mod ops;
12
13use num_traits::{PrimInt, Unsigned};
14
15/// `BitUint` is a type that represents a `N`-bit unsigned integer.
16///
17/// `N` is the number of bits in the value. The largest size of `N` is equal to
18/// the size of the underlying type in bits.
19///
20/// # Examples
21///
22/// ```
23/// # use bit_int::BitUint;
24/// #
25/// type Uint = BitUint<u8, 7>;
26///
27/// let n = Uint::new(127).unwrap();
28/// assert_eq!(n, Uint::MAX);
29///
30/// assert_eq!(n.checked_add(1), None);
31/// assert_eq!(n.get().checked_add(1), Some(128));
32/// ```
33///
34/// In this case, `N` must be less than or equal to [`u32::BITS`]:
35///
36/// ```compile_fail
37/// # use bit_int::BitUint;
38/// #
39/// let _ = BitUint::<u32, 33>::new(42);
40/// ```
41///
42/// `N` must be greater than `0`:
43///
44/// ```compile_fail
45/// # use bit_int::BitUint;
46/// #
47/// let _ = BitUint::<u64, 0>::new(0);
48/// ```
49#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
50#[repr(transparent)]
51pub struct BitUint<T: Unsigned + PrimInt, const N: u32>(T);
52
53macro_rules! impl_bit_uint {
54    ($T:ty, $alias:ident) => {
55        impl<const N: u32> BitUint<$T, N> {
56            /// Creates a new `BitUint` with the given unsigned integer value.
57            ///
58            /// Returns [`None`] if the value is not a valid `N`-bit unsigned integer.
59            ///
60            /// # Examples
61            ///
62            /// ```
63            /// # use bit_int::BitUint;
64            /// #
65            #[doc = concat!("let n = BitUint::<", stringify!($T), ", 6>::new(42);")]
66            /// assert_eq!(n.map(BitUint::get), Some(42));
67            #[doc = ""]
68            #[doc = concat!("let m = BitUint::<", stringify!($T), ", 5>::new(42);")]
69            /// assert_eq!(m, None);
70            /// ```
71            #[must_use]
72            #[inline]
73            pub const fn new(n: $T) -> Option<Self> {
74                // `n` must be greater than or equal to 0.
75                debug_assert!(n >= Self::MIN.get());
76
77                if n <= Self::MAX.get() {
78                    // SAFETY: `n` is checked to be a valid `N`-bit unsigned integer.
79                    let n = unsafe { Self::new_unchecked(n) };
80                    Some(n)
81                } else {
82                    None
83                }
84            }
85        }
86
87        /// A specialized [`BitUint`] type whose the underlying type is restricted to
88        #[doc = concat!("[`", stringify!($T), "`].")]
89        ///
90        #[doc = concat!("The largest size of `N` is equal to [`", stringify!($T), "::BITS`].")]
91        pub type $alias<const N: u32> = BitUint<$T, N>;
92    };
93}
94impl_bit_uint!(u8, BitU8);
95impl_bit_uint!(u16, BitU16);
96impl_bit_uint!(u32, BitU32);
97impl_bit_uint!(u64, BitU64);
98impl_bit_uint!(u128, BitU128);
99impl_bit_uint!(usize, BitUsize);
100
101impl<T: Unsigned + PrimInt, const N: u32> BitUint<T, N> {
102    /// Creates a new `BitUint` with the given unsigned integer value.
103    ///
104    /// This method does not check whether the value is a valid `N`-bit unsigned
105    /// integer. This results in undefined behaviour if the value is not a valid
106    /// `N`-bit unsigned integer.
107    ///
108    /// # Safety
109    ///
110    /// The value must be a valid `N`-bit unsigned integer.
111    #[must_use]
112    #[inline]
113    pub const unsafe fn new_unchecked(n: T) -> Self {
114        Self(n)
115    }
116
117    /// Returns the contained value as the underlying type.
118    #[must_use]
119    #[inline]
120    pub const fn get(self) -> T {
121        self.0
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use core::{any, mem};
128
129    use super::*;
130
131    #[test]
132    fn alias() {
133        assert_eq!(
134            any::type_name::<BitU8::<7>>(),
135            any::type_name::<BitUint::<u8, 7>>()
136        );
137        assert_eq!(
138            any::type_name::<BitU16::<15>>(),
139            any::type_name::<BitUint::<u16, 15>>()
140        );
141        assert_eq!(
142            any::type_name::<BitU32::<31>>(),
143            any::type_name::<BitUint::<u32, 31>>()
144        );
145        assert_eq!(
146            any::type_name::<BitU64::<63>>(),
147            any::type_name::<BitUint::<u64, 63>>()
148        );
149        assert_eq!(
150            any::type_name::<BitU128::<127>>(),
151            any::type_name::<BitUint::<u128, 127>>()
152        );
153        assert_eq!(
154            any::type_name::<BitUsize::<31>>(),
155            any::type_name::<BitUint::<usize, 31>>()
156        );
157    }
158
159    #[test]
160    fn layout() {
161        assert_eq!(mem::size_of::<BitU32::<31>>(), mem::size_of::<u32>());
162        assert_eq!(mem::align_of::<BitU32::<31>>(), mem::align_of::<u32>());
163    }
164
165    #[test]
166    fn clone() {
167        assert_eq!(BitU32::<31>::MIN.clone(), BitU32::<31>::MIN);
168    }
169
170    #[test]
171    fn copy() {
172        let a = BitU32::<31>::MIN;
173        let b = a;
174        assert_eq!(a, b);
175    }
176
177    #[test]
178    fn default() {
179        assert_eq!(
180            BitU32::<31>::default(),
181            BitU32::<31>::new(u32::default()).unwrap()
182        );
183    }
184
185    #[test]
186    fn new() {
187        assert_eq!(
188            BitU8::<7>::new(u8::MAX >> 1).map(BitU8::get),
189            Some(u8::MAX >> 1)
190        );
191        assert_eq!(
192            BitU16::<15>::new(u16::MAX >> 1).map(BitU16::get),
193            Some(u16::MAX >> 1)
194        );
195        assert_eq!(
196            BitU32::<31>::new(u32::MAX >> 1).map(BitU32::get),
197            Some(u32::MAX >> 1)
198        );
199        assert_eq!(
200            BitU64::<63>::new(u64::MAX >> 1).map(BitU64::get),
201            Some(u64::MAX >> 1)
202        );
203        assert_eq!(
204            BitU128::<127>::new(u128::MAX >> 1).map(BitU128::get),
205            Some(u128::MAX >> 1)
206        );
207        assert_eq!(
208            BitUsize::<{ usize::BITS - 1 }>::new(usize::MAX >> 1).map(BitUsize::get),
209            Some(usize::MAX >> 1)
210        );
211    }
212
213    #[test]
214    fn new_with_invalid_value() {
215        assert!(BitU8::<7>::new((u8::MAX >> 1) + 1).is_none());
216        assert!(BitU16::<15>::new((u16::MAX >> 1) + 1).is_none());
217        assert!(BitU32::<31>::new((u32::MAX >> 1) + 1).is_none());
218        assert!(BitU64::<63>::new((u64::MAX >> 1) + 1).is_none());
219        assert!(BitU128::<127>::new((u128::MAX >> 1) + 1).is_none());
220        assert!(BitUsize::<{ usize::BITS - 1 }>::new((usize::MAX >> 1) + 1).is_none());
221    }
222
223    #[test]
224    fn new_when_one_bit_value() {
225        assert_eq!(BitU32::<1>::new(0).map(BitU32::get), Some(0));
226        assert_eq!(BitU32::<1>::new(1).map(BitU32::get), Some(1));
227        assert!(BitU32::<1>::new(2).is_none());
228    }
229
230    #[test]
231    fn new_when_max_bits_value() {
232        assert_eq!(
233            BitU32::<{ u32::BITS }>::new(u32::MIN).map(BitU32::get),
234            Some(u32::MIN)
235        );
236        assert_eq!(
237            BitU32::<{ u32::BITS }>::new(u32::MAX).map(BitU32::get),
238            Some(u32::MAX)
239        );
240    }
241
242    #[test]
243    const fn new_is_const_fn() {
244        const _: Option<BitU32<31>> = BitU32::<31>::new(u32::MAX >> 1);
245    }
246
247    #[test]
248    fn new_unchecked() {
249        assert_eq!(
250            unsafe { BitU8::<7>::new_unchecked(u8::MAX >> 1) }.get(),
251            u8::MAX >> 1
252        );
253        assert_eq!(
254            unsafe { BitU16::<15>::new_unchecked(u16::MAX >> 1) }.get(),
255            u16::MAX >> 1
256        );
257        assert_eq!(
258            unsafe { BitU32::<31>::new_unchecked(u32::MAX >> 1) }.get(),
259            u32::MAX >> 1
260        );
261        assert_eq!(
262            unsafe { BitU64::<63>::new_unchecked(u64::MAX >> 1) }.get(),
263            u64::MAX >> 1
264        );
265        assert_eq!(
266            unsafe { BitU128::<127>::new_unchecked(u128::MAX >> 1) }.get(),
267            u128::MAX >> 1
268        );
269        assert_eq!(
270            unsafe { BitUsize::<{ usize::BITS - 1 }>::new_unchecked(usize::MAX >> 1) }.get(),
271            usize::MAX >> 1
272        );
273    }
274
275    #[test]
276    const fn new_unchecked_is_const_fn() {
277        const _: BitU32<31> = unsafe { BitU32::<31>::new_unchecked(u32::MAX >> 1) };
278    }
279
280    #[test]
281    fn get() {
282        assert_eq!(BitU8::<7>::MAX.get(), u8::MAX >> 1);
283        assert_eq!(BitU16::<15>::MAX.get(), u16::MAX >> 1);
284        assert_eq!(BitU32::<31>::MAX.get(), u32::MAX >> 1);
285        assert_eq!(BitU64::<63>::MAX.get(), u64::MAX >> 1);
286        assert_eq!(BitU128::<127>::MAX.get(), u128::MAX >> 1);
287        assert_eq!(BitUsize::<{ usize::BITS - 1 }>::MAX.get(), usize::MAX >> 1);
288    }
289
290    #[test]
291    const fn get_is_const_fn() {
292        const _: u32 = BitU32::<31>::MIN.get();
293    }
294}