#power #exponent #math #storing #operation #range #non-negative

pow2

Represents a non-negative power of 2, by storing its exponent

2 releases

0.1.1 Sep 6, 2023
0.1.0 Apr 27, 2020

#1275 in Algorithms

MIT/Apache

17KB
191 lines

Represents a non-negative power of 2, by storing its exponent.

The Pow2 type is limited in range to 1 .. 2^255 (inclusive). It is simply a wrapper around u8, which stores the exponent.

Pow2 is typically used for aligning integer values, such as I/O buffer sizes, page sizes, etc.

Negative powers of 2 are not supported because they have little or no application in managing memory, quick multiplication, etc.

Pow2 can represent values outside of the range of specific integer types. For example, Pow2::from_exponent(40) cannot be converted to u32. The conversion implementations treat this in a way similar to arithmetic overflow for operations such as x * y; the operation is defined, but may fail at runtime.

It would be possible to define a family of Pow2 variants, one for each primitive integer type. However, the ergonomics of that design might be undesireable; it is left for future study.

The operators defined for Pow2 are defined in terms of the value of the power of 2, not the exponent. For example, Pow2 * Pow2 results in adding the exponents, not in multiplying the exponents.

Examples

const PAGE_SIZE: Pow2 = Pow2::from_exponent(12);
assert_eq!(u32::from(PAGE_SIZE), 0x1000);
let x: u32 = 0x1eee;
assert_eq!(PAGE_SIZE.align_down(x), 0x1000);
assert_eq!(PAGE_SIZE.align_up(x), Some(0x2000));
assert_eq!(PAGE_SIZE.align_up_unchecked(x), 0x2000);

The pow2_const! macro can be used for writing constant powers of 2, using a value rather than an exponent. Due to current limitations of const fns, the pow2_const! macro does not check whether the input is a valid power of 2. If it is not a power of 2, then its behavior is undefined.

use pow2::{Pow2, pow2_const};
const PAGE_SIZE: Pow2 = pow2_const!(0x1000);
assert_eq!(PAGE_SIZE.exponent(), 12);

Signed examples

Pow2 works with signed types as well as unsigned.

use pow2::Pow2;
const ALIGN4: Pow2 = Pow2::from_exponent(2);
assert_eq!(ALIGN4.align_up(4i32), Some(4i32));
assert_eq!(ALIGN4.align_up(3i32), Some(4i32));
assert_eq!(ALIGN4.align_up(2i32), Some(4i32));
assert_eq!(ALIGN4.align_up(1i32), Some(4i32));
assert_eq!(ALIGN4.align_up(-0i32), Some(0i32));
assert_eq!(ALIGN4.align_up(-1i32), Some(0i32));
assert_eq!(ALIGN4.align_up(-2i32), Some(0i32));
assert_eq!(ALIGN4.align_up(-3i32), Some(0i32));
assert_eq!(ALIGN4.align_up(-4i32), Some(-4i32));
assert_eq!(ALIGN4.align_up(-5i32), Some(-4i32));
assert_eq!(ALIGN4.align_up(-6i32), Some(-4i32));
assert_eq!(ALIGN4.align_up(-7i32), Some(-4i32));
assert_eq!(ALIGN4.align_up(-8i32), Some(-8i32));

Unsafe code examples

This library does not contain unsafe code, but it can be used by unsafe code to operate on pointers. This is because Rust allows safe code to operate on the values of unsafe pointers, but not to dereference them.

use pow2::{Pow2, pow2_const};

const U32_ALIGN: Pow2 = Pow2::align_of::<u32>();

let array: [u32; 4] = [ 111, 222, 333, 444 ];
let item_0_address: *const u8 = &array[0] as *const u32 as *const u8;
let item_1_address: *const u8 = &array[1] as *const u32 as *const u8;

assert_eq!(
  U32_ALIGN.align_up((item_0_address as usize + 1) as *const u8).unwrap(),
  item_1_address
);

Dependencies

~0.9–1.5MB
~27K SLoC