bitable 0.1.0

An all-const, compile-time checked, safe bit handling library with zero runtime overhead
Documentation
use const_panic::concat_assert;

// ---------------------------------------------------------------------------
// CPU native bit-size (work-in-progress)
// ---------------------------------------------------------------------------

#[cfg(target_has_atomic = "32")]
pub type UReg = u32;

#[cfg(all(target_has_atomic = "16", not(target_has_atomic = "32")))]
pub type UReg = u16;

#[cfg(all(target_has_atomic = "8", not(target_has_atomic = "16")))]
pub type UReg = u8;

// ---------------------------------------------------------------------------
// Convertion between UReg and supported primitive types
// ---------------------------------------------------------------------------

pub const trait CastFromUReg {
    fn cast_from(value: UReg) -> Self;
}

impl const CastFromUReg for bool {
    fn cast_from(value: UReg) -> Self { value != 0 }
}

impl const CastFromUReg for u8 {
    fn cast_from(value: UReg) -> Self { value as u8 }
}

impl const CastFromUReg for u16 {
    fn cast_from(value: UReg) -> Self { value as u16 }
}

impl const CastFromUReg for u32 {
    fn cast_from(value: UReg) -> Self { value as u32 }
}

// ---------------------------------------------------------------------------
// Core helper functions
// ---------------------------------------------------------------------------

pub const fn flag_mask(offset: u8) -> UReg {
    0b1 << offset
}
pub const fn field_mask(offset: u8, length: u8) -> UReg {
    group_mask(length) << offset
}
const fn group_mask(length: u8) -> UReg {
    // TODO: make this arch-agnostic
    // equivalent to `(0b1 << length) - 1` w/o overflow for `length == 32``
    (0b1_u32.checked_shl(length as u32)).unwrap_or(0).wrapping_sub(1)
}

// ---------------------------------------------------------------------------
// Compile-time assertions
// ---------------------------------------------------------------------------

#[track_caller]
pub const fn assert_flag<Type>(offset: u8) {
    let type_name = core::any::type_name::<Type>();
    let type_bit_size = { core::mem::size_of::<Type>() * 8} as u8;
    concat_assert!(offset < type_bit_size,
        "\nBit Flag offset `", offset, "` overflows target type `", display:type_name,
        "` (max offset: ", type_bit_size - 1, ")");
}
#[track_caller]
pub const fn assert_field<Type>(offset: u8, size: u8) {
    let type_name = core::any::type_name::<Type>();
    let type_bit_size = { core::mem::size_of::<Type>() * 8} as u8;
    concat_assert!(offset < type_bit_size,
        "\nBit Field offset `", offset, "` overflows target type `", display:type_name,
        "` (max offset: ", type_bit_size - 1, ")");
    let last_offset = offset + size - 1;
    concat_assert!(last_offset < type_bit_size,
        "\nBit Field size `", size, "` overflows target type `", display:type_name,
        "` (max size at offset `", offset, "`: ", type_bit_size - offset, ")");
}
#[track_caller]
pub const fn assert_mask<Type>(size: u8) {
    let type_bit_size = { core::mem::size_of::<Type>() * 8} as u8;
    let type_name = core::any::type_name::<Type>();
    concat_assert!(size <= type_bit_size,
        "\nBit Mask size `", size, "` overflows target type `", display:type_name,
        "` (max size: ", type_bit_size, ")");
}
#[track_caller]
pub const fn assert_size(size: u8) {
    concat_assert!(size > 0, "\nBit Field size cannot be `0`");
}