Skip to main content

arbitrary_int/
lib.rs

1//! # arbitrary-int - Arbitrary number types for Rust
2//!
3//! This crate implements arbitrary numbers for Rust. Once included, you can use types like `u5` or `u120`.
4//!
5//! ## Why yet another arbitrary integer crate?
6//!
7//! There are quite a few similar crates to this one (the most famous being [ux](https://crates.io/crates/ux)). After trying out a
8//! few of them I just realized that they are all very heavy: They create a ton of classes and take seconds to compile.
9//!
10//! This crate is designed to be very short, using const generics. Instead of introducing ~123 new structs, this crates only
11//! introduces 5 (one for `u8`, `u16`, `u32`, `u64`, `u128`) and uses const generics for the specific bit depth.
12//! It does introduce 123 new type aliases (`u1`, `u2`, etc.), but these don't stress the compiler nearly as much.
13//!
14//! Additionally, most of its functions are const, so that they can be used in const contexts.
15//!
16//! ## How to use
17//!
18//! Unlike primitive data types like `u32`, there is no intrinsic syntax (Rust does not allow that). An instance is created as
19//! follows:
20//!
21//! ```rust
22//! use arbitrary_int::u9;
23//!
24//! let value9 = u9::new(30);
25//! ```
26//!
27//! This will create a value with 9 bits. If the value passed into `new()` doesn't fit, a panic! will be raised. This means
28//! that a function that accepts a `u9` as an argument can be certain that its contents are never larger than an `u9`.
29//!
30//! Standard operators are all overloaded, so it is possible to perform calculations using this type. Note that addition
31//! and subtraction (at least in debug mode) performs bounds check. If this is undesired, see chapter num-traits below.
32//!
33//! Internally, `u9` will hold its data in an `u16`. It is possible to get this value:
34//!
35//! ```rust
36//! use arbitrary_int::u9;
37//!
38//! let value9 = u9::new(30).value();
39//! ```
40//!
41//! ## Underlying data type
42//!
43//! This crate defines types `u1`, `u2`, .., `u126`, `u127` (skipping the normal `u8`, `u16`, `u32`, `u64`, `u128`). Each of those types holds
44//! its actual data in the next larger data type (e.g. a `u14` internally has an `u16`, a `u120` internally has an `u128`). However,
45//! `uXX` are just type aliases; it is also possible to use the actual underlying generic struct:
46//!
47//! ```rust
48//! use arbitrary_int::UInt;
49//!
50//! let a = UInt::<u8, 5>::new(0b10101);
51//! let b = UInt::<u32, 5>::new(0b10101);
52//! ```
53//!
54//! In this example, `a` will have 5 bits and be represented by a `u8`. This is identical to `u5`. `b` however is represented by a
55//! `u32`, so it is a different type from `u5`.
56//!
57//! ## Extract
58//!
59//! A common source for arbitrary integers is by extracting them from bitfields. For example, if data contained 32 bits and
60//! we want to extract bits `4..=9`, we could perform the following:
61//!
62//! ```rust
63//! use arbitrary_int::u6;
64//!
65//! let data = 5_u32;
66//! let a = u6::new(((data >> 4) & 0b111111) as u8);
67//! ```
68//!
69//! This is a pretty common operation, but it's easy to get it wrong: The number of 1s and `u6` have to match. Also, `new()`
70//! will internally perform a bounds-check, which can panic. Thirdly, a type-cast is often needed.
71//! To make this easier, various extract methods exist that handle shifting and masking, for example:
72//!
73//! ```rust
74//! use arbitrary_int::{u6, u12};
75//!
76//! let data = 0b1010100000_u32;
77//! let a = u6::extract_u32(data, 4);
78//! assert_eq!(a.value(), 0b101010);
79//! let data2 = (0x800 as u128) << 63;
80//! let b = u12::extract_u128(data2, 63);
81//! assert_eq!(b.value(), 0x800);
82//! ```
83//!
84//! ## num-traits
85//!
86//! By default, arbitrary-int doesn't require any other traits. It has optional support for num-traits however. It
87//! implements `WrappingAdd`, `WrappingSub`, which (unlike the regular addition and subtraction) don't perform bounds checks.
88// By unconditionally declaring this crate as `no_std` we opt out of the standard library's prelude,
89// which implicitly brings items like `Vec` and `String` into scope. Since we'd need to import those
90// manually in case the `alloc` crate is used but the standard library isn't, we might as well keep
91// things consistent and always manually import them.
92#![no_std]
93#![cfg_attr(feature = "step_trait", feature(step_trait))]
94
95// This makes it possible to use `std::` when the `std` feature is enabled, even though we're `no_std`.
96#[cfg(feature = "std")]
97extern crate std;
98
99// The `alloc` crate is always usable when the standard library (i.e. the `std` feature) is enabled.
100// The standard library re-exports collections from the `alloc` crate, but since this crate supports
101// `alloc` without `std` its best to use `alloc` directly: that works both with and without `std`.
102#[cfg(any(feature = "borsh", feature = "std"))]
103extern crate alloc;
104
105use core::fmt;
106
107mod common;
108mod signed;
109pub mod traits;
110mod unsigned;
111mod v1_number_compat;
112
113pub use signed::*;
114pub use unsigned::*;
115pub use v1_number_compat::*;
116
117/// The preferred way to import arbitrary-int into a project: `use arbitrary_int::prelude::*`
118pub mod prelude {
119    pub use crate::signed::*;
120    pub use crate::traits::*;
121    pub use crate::unsigned::*;
122    pub use crate::TryNewError;
123}
124
125#[cfg(feature = "arbitrary")]
126mod arbitrary;
127
128#[cfg(feature = "quickcheck")]
129mod quickcheck;
130
131#[derive(Debug, Clone, Eq, PartialEq)]
132pub struct TryNewError;
133
134impl fmt::Display for TryNewError {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        write!(f, "Value too large to fit within this integer type")
137    }
138}