#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PointerSize {
Bit8,
Bit16,
Bit32,
Bit64,
Bit128,
}
impl PointerSize {
#[must_use]
pub fn from_is_64bit(is_64bit: bool) -> Self {
if is_64bit {
Self::Bit64
} else {
Self::Bit32
}
}
#[must_use]
pub fn bytes(self) -> usize {
match self {
Self::Bit8 => 1,
Self::Bit16 => 2,
Self::Bit32 => 4,
Self::Bit64 => 8,
Self::Bit128 => 16,
}
}
#[must_use]
pub fn bits(self) -> u32 {
match self {
Self::Bit8 => 8,
Self::Bit16 => 16,
Self::Bit32 => 32,
Self::Bit64 => 64,
Self::Bit128 => 128,
}
}
#[must_use]
pub fn mask_signed(self, value: i64) -> i64 {
match self {
Self::Bit8 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as i8;
i64::from(truncated)
}
Self::Bit16 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as i16;
i64::from(truncated)
}
Self::Bit32 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as i32;
i64::from(truncated)
}
Self::Bit64 => value,
Self::Bit128 => value,
}
}
#[must_use]
pub fn mask_unsigned(self, value: u64) -> u64 {
match self {
Self::Bit8 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as u8;
u64::from(truncated)
}
Self::Bit16 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as u16;
u64::from(truncated)
}
Self::Bit32 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as u32;
u64::from(truncated)
}
Self::Bit64 => value,
Self::Bit128 => value,
}
}
#[must_use]
pub fn mask_signed_128(self, value: i128) -> i128 {
match self {
Self::Bit8 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as i8;
i128::from(truncated)
}
Self::Bit16 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as i16;
i128::from(truncated)
}
Self::Bit32 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as i32;
i128::from(truncated)
}
Self::Bit64 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as i64;
i128::from(truncated)
}
Self::Bit128 => value,
}
}
#[must_use]
pub fn mask_unsigned_128(self, value: u128) -> u128 {
match self {
Self::Bit8 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as u8;
u128::from(truncated)
}
Self::Bit16 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as u16;
u128::from(truncated)
}
Self::Bit32 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as u32;
u128::from(truncated)
}
Self::Bit64 => {
#[allow(clippy::cast_possible_truncation)]
let truncated = value as u64;
u128::from(truncated)
}
Self::Bit128 => value,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_is_64bit_maps_bitness_to_pointer_size() {
assert_eq!(PointerSize::from_is_64bit(false), PointerSize::Bit32);
assert_eq!(PointerSize::from_is_64bit(true), PointerSize::Bit64);
}
#[test]
fn bytes_and_bits_match_arch_width() {
assert_eq!(PointerSize::Bit8.bytes(), 1);
assert_eq!(PointerSize::Bit8.bits(), 8);
assert_eq!(PointerSize::Bit16.bytes(), 2);
assert_eq!(PointerSize::Bit16.bits(), 16);
assert_eq!(PointerSize::Bit32.bytes(), 4);
assert_eq!(PointerSize::Bit32.bits(), 32);
assert_eq!(PointerSize::Bit64.bytes(), 8);
assert_eq!(PointerSize::Bit64.bits(), 64);
assert_eq!(PointerSize::Bit128.bytes(), 16);
assert_eq!(PointerSize::Bit128.bits(), 128);
}
#[test]
fn signed_mask_truncates_and_sign_extends_values() {
assert_eq!(PointerSize::Bit8.mask_signed(0xFF), -1_i64);
assert_eq!(PointerSize::Bit8.mask_signed(0x7F), 127_i64);
assert_eq!(PointerSize::Bit16.mask_signed(0xFFFF), -1_i64);
assert_eq!(PointerSize::Bit16.mask_signed(0x7FFF), 32767_i64);
assert_eq!(
PointerSize::Bit32.mask_signed(0x0000_0000_8000_0000),
i64::from(i32::MIN)
);
assert_eq!(
PointerSize::Bit32.mask_signed(0x0000_0000_7fff_ffff),
i64::from(i32::MAX)
);
assert_eq!(PointerSize::Bit64.mask_signed(i64::MIN), i64::MIN);
assert_eq!(PointerSize::Bit128.mask_signed(-42), -42);
}
#[test]
fn unsigned_mask_truncates_values() {
assert_eq!(PointerSize::Bit8.mask_unsigned(0x1FF), 0xFF_u64);
assert_eq!(PointerSize::Bit16.mask_unsigned(0x1FFFF), 0xFFFF_u64);
assert_eq!(
PointerSize::Bit32.mask_unsigned(0x1_ffff_ffff),
u64::from(u32::MAX)
);
assert_eq!(PointerSize::Bit32.mask_unsigned(0x1_0000_0000), 0);
assert_eq!(PointerSize::Bit64.mask_unsigned(u64::MAX), u64::MAX);
assert_eq!(PointerSize::Bit128.mask_unsigned(42), 42);
}
#[test]
fn signed_mask_128_truncates_and_sign_extends_values() {
assert_eq!(PointerSize::Bit8.mask_signed_128(0xFF), -1_i128);
assert_eq!(PointerSize::Bit16.mask_signed_128(0xFFFF), -1_i128);
assert_eq!(PointerSize::Bit32.mask_signed_128(0xFFFF_FFFF), -1_i128);
assert_eq!(
PointerSize::Bit64.mask_signed_128(0xFFFF_FFFF_FFFF_FFFF),
-1_i128
);
assert_eq!(PointerSize::Bit128.mask_signed_128(i128::MAX), i128::MAX);
}
#[test]
fn unsigned_mask_128_truncates_values() {
assert_eq!(PointerSize::Bit8.mask_unsigned_128(0x1FF), 0xFF_u128);
assert_eq!(PointerSize::Bit16.mask_unsigned_128(0x1FFFF), 0xFFFF_u128);
assert_eq!(
PointerSize::Bit32.mask_unsigned_128(0x1_FFFF_FFFF),
u32::MAX as u128
);
assert_eq!(
PointerSize::Bit64.mask_unsigned_128(u128::from(u64::MAX) + 1),
0
);
assert_eq!(PointerSize::Bit128.mask_unsigned_128(u128::MAX), u128::MAX);
}
}