#unsigned-integer #uint #int

no-std aint

Arbitrary width integers between 1 and 128

1 unstable release

0.1.0 Dec 25, 2023

#1169 in Math

MIT/Apache

1MB
20K SLoC

Aint

Aint is a crate implementing integers of non-standard bit widths between 1 and 127. These integer types are represented by the next largest built-in Rust integer, but are bounded to the range and behaviors of the advertised bit width. That is, T::MIN and T::MAX, are what would be expected for the integer T with N bits, and similarly, wrapping, saturating, and overflow behaviors match what would be expected for a hypothetical built-in integer T with N bits.

Example

fn add(a: i13, b: i13) -> i13 {
    a + b
}

let x = i13!(100);
let y = add(x, i13!(-42));
assert_eq!(y, i13!(58));

See the documentation for details.


lib.rs:

Aint is a crate implementing integers of non-standard bit widths between 1 and 127. These integer types are represented by the next largest built-in Rust integer, but are bounded to the range and behaviors of the advertised bit width. That is, T::MIN and T::MAX, are what would be expected for the integer T with N bits, and similarly, wrapping, saturating, and overflow behaviors match what would be expected for a hypothetical built-in integer T with N bits.

Example

fn add(a: i13, b: i13) -> i13 {
    a + b
}

let x = i13!(100);
let y = add(x, i13!(-42));
assert_eq!(y, i13!(58));

The core implementation type is Aint<R, const WIDTH: u32>, which accepts a representation type, R, and a bit width, WIDTH. Aint is intended to primarily be an implementation detail, not to be used directly. Instead, it is preferred to use the type aliases provided by this crate.

If Aint is used directly, it will only permit generic parameters that match one of the existing type aliases provided by this crate. For example, the type i24 is an alias of Aint<i32, 24>. In theory, Aint<i64, 24> would be equivalent in functionality, but will result in a compile time error if instantiated. This prevents the existence multiple Aints that are functionally the same but are incompatible types to the compiler.

Byte Wide Types

Integer types that have an exact byte width provide additional methods and functionality that other integer types do not. These are types whose width is a multiple of 8, such as i24 (3 bytes), u56 (7 bytes), and u120 (15 bytes). Such types provide the byte and endianness manipulation APIs that built-in Rust integers provide, such as T::from_be_bytes, T::swap_bytes, and T::to_le. Other integer types do not implement these methods since their meaning becomes ambiguous or perhaps even nonsensical.

Conversions

All integer types provided by this crate implement traits to convert to and from all built-in Rust integer types. A conversion will be implemented as From when the conversion is infallible, and TryFrom when the conversion can fail. For example, converting from i4 to i8 is implemented as impl From<i4> for i8, but the reverse conversion is implemented as impl TryFrom<i8> for i4.

Unfortunately, this crate does not provide the same conversions between each of the pairs of types provided, such as i4 to i13. Existing blanket implmentations on the standard conversion traits prevent a generic implementation of these traits that would cover all the types provided by this crate, and implementing conversions for each pair of types individually is infeasible due to extreme compiliation times, since each pair would result on the order of 1282 trait implementations.

This crate also provides two alternate conversion traits that perform infallible, but potentially lossy, conversions: WrappingFrom/WrappingInto and SaturatingFrom/SaturatingInto. These are implemented for all combinations of built-in integers and integers provided by this crate. See each trait's documentation for more details.

Concatenation and Splitting

This crate also provides two traits for concatenating and spliting integers bitwise: BitConcat and BitSplit. Any pair of two integer types can be concatenated as long as their combined bit width is at most 128, and any integer can be split bitwise into any two integers whose combined bit width equals the bit width of the original integer.

let x: u16 = 0b0101010100101_101;
let (x1, x2): (i13, u3) = x.bit_split();
let y = u16::bit_concat(x1, x2);

assert_eq!(x, y);
assert_eq!(x1, i13!(0b0101010100101));
assert_eq!(x2, u3!(0b101));

Macros

Each integer type provided by this crate comes with macro for constructing these types from literals. Built-in integers can have typed literals, such as 42u8 or -7i64. The macros provided by this crate perform a similar function. u13!(100) is essentially a u13 literal with the value 100. The values provided to these macros are also checked at compile time to ensure that they are valid for the type. For example, u4!(100), would result in a compile time error, because u4 can only represent values from 0 to 15, inclusive.

Features

This crate has two non-default features, serde and num, which enable compaitbility with the serde and num crates.

Enabling the serde feature will provide implementations of serde::Serialize and serde::Deserialize for each integer type in this crate.

Enabling the num feature will provide implementations of all appropriate traits from the num_traits and num_integer crates under the num family of crates.

[dependencies]
aint = { version = "0.1", features = [ "serde", "num" ] }

no_std

This crate is also #![no_std]. Unlike some other crates, this crate does not have a std (or no_std) feature - it simply never uses or needs libstd. Additionally, this crate does not use alloc either. libcore is the only required dependency, and the crates pulled in by the serde and num features are also no_std compatible.

Dependencies

~220KB