Skip to content

Commit

Permalink
feat: 修改定义以支持用名字来区分复杂的量化类型
Browse files Browse the repository at this point in the history
Signed-off-by: YdrMaster <[email protected]>
  • Loading branch information
YdrMaster committed Aug 30, 2024
1 parent 32f1fb8 commit bd5eb53
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 166 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "digit-layout"
description = "Define memory layout of digital types as `[sign|exponent|mantissa; N]`"
version = "0.0.0"
description = "This crate provides a unified data type definition across various libraries, efficiently encodes types in a compact layout, thus avoiding the redundancy of enumerating definitions for data types."
version = "0.1.0"
edition = "2021"
authors = ["YdrMaster <[email protected]>"]
repository = "https://github.com/YdrMaster/digit-layout.git"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
![GitHub contributors](https://img.shields.io/github/contributors/YdrMaster/digit-layout)
![GitHub commit activity](https://img.shields.io/github/commit-activity/m/YdrMaster/digit-layout)

This crate provides types, traits and macros to define the memory layout of digital types as `[sign|exponent|mantissa; N]`.
This crate provides a unified data type definition across various libraries, efficiently encodes types in a compact layout, thus avoiding the redundancy of enumerating definitions for data types.
285 changes: 200 additions & 85 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,114 +6,229 @@
mod macros;
pub mod types;

use core::{
alloc::Layout,
mem::{align_of, transmute},
};

/// A layout of a digit data type in memory.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(C)]
pub struct DigitLayout {
signed_nbyte: u8,
packed: u8,
exponent: u8,
mantissa: u8,
}
#[repr(transparent)]
pub struct DigitLayout(u32);

/// A trait for types that can be represented as a digit data type.
pub trait AsDigit {
/// The layout of the digit data type.
const LAYOUT: DigitLayout;
/// The content of a digit layout.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum LayoutContent {
/// An unsigned integer type.
Unsigned {
/// The width of the integer in bits.
width: u32,
},
/// A real number type.
Real {
/// The width of the exponent in bits.
exponent: u32,
/// The width of the mantissa in bits.
mantissa: u32,
},
/// A named type.
Named {
/// The name of the type.
name: [u8; 8],
},
}

/// Allow a const [`DigitLayout`] value associated with a type.
pub trait TypeOf_<const N: u32> {
/// The type associated with the layout.
type Type;
#[repr(u32)]
enum DigitLayoutType {
Unsigned = 0xe0_00_00_00, // 0b111...
Real = 0xc0_00_00_00, // 0b110...
Named = 0, // 0b...
}

/// A type template for the [`TypeOf_`] trait to implement.
pub struct TypeOf<const N: u32>;

const _8: usize = u8::BITS as usize;
const _7: usize = _8 - 1;
const MAX_ALIGN: usize = align_of::<usize>();
const HEAD: u32 = DigitLayoutType::Unsigned as _;

impl DigitLayout {
/// Creates a new [`DigitLayout`] value.
/// Create a new digit layout for an unsigned integer type.
#[inline]
pub const fn new(packed: usize, signed: bool, exponent: usize, mantissa: usize) -> Self {
assert!(packed <= u8::MAX as usize);
assert!(exponent <= u8::MAX as usize);
assert!(mantissa <= u8::MAX as usize);
let signed = if signed { 1 } else { 0 };

let total_bits = packed * (signed + exponent + mantissa);
let nbyte = ((total_bits + _7) / _8).next_power_of_two();
assert!(nbyte < (1 << _7));

Self {
packed: packed as _,
signed_nbyte: ((signed << _7) | nbyte) as _,
exponent: exponent as _,
mantissa: mantissa as _,
}
pub const fn unsigned(width: u32) -> Self {
assert!(width & HEAD == 0);
Self::new(DigitLayoutType::Unsigned, width)
}

/// Converts the layout to a `u32` code.
/// Create a new digit layout for a real number type.
#[inline]
pub const fn to_u32(self) -> u32 {
unsafe { transmute(self) }
pub const fn real(exponent: u32, mantissa: u32) -> Self {
let body = (exponent << 16) | mantissa;
assert!(body & HEAD == 0);
Self::new(DigitLayoutType::Real, body)
}

/// Gets the packed count of the digit data type.
#[inline]
pub const fn packed(self) -> usize {
self.packed as _
/// Create a new digit layout for a named type.
pub const fn named(name: &str) -> Self {
let mut exp = 1;
let mut bytes = name.as_bytes();
let mut body = 0;
while let [b, tail @ ..] = bytes {
bytes = tail;

const MARK: u32 = 0x60_00_00_00; // 0b011...
let b = match b {
b'0'..=b'9' => *b - b'0',
b'a'..=b'z' => *b - b'a' + 10,
b'A'..=b'Z' => *b - b'A' + 10,
b'_' | b'.' => continue,
_ => panic!("Invalid character in digit name"),
};
body += (b as u32 + 1) * exp;
assert!(body & MARK == 0);
assert!(exp & MARK == 0);
exp *= 37; // 37 = 10 + 26 + 1
}
Self::new(DigitLayoutType::Named, body)
}

/// Gets the signedness of the digit data type.
#[inline]
pub const fn signed(self) -> bool {
self.signed_nbyte >> _7 == 1
#[inline(always)]
const fn new(ty: DigitLayoutType, body: u32) -> Self {
Self((ty as u32) | body)
}

/// Gets the exponent bits of the digit data type.
/// Decode the content of the digit layout.
#[inline]
pub const fn exponent(self) -> usize {
self.exponent as _
pub const fn decode(self) -> LayoutContent {
const UNSIGNED: u32 = DigitLayoutType::Unsigned as _;
const SIGNED: u32 = DigitLayoutType::Real as _;

let head = self.0 & HEAD;
match head {
UNSIGNED => LayoutContent::Unsigned {
width: self.0 & !HEAD,
},
SIGNED => LayoutContent::Real {
exponent: ((self.0 & !HEAD) >> 16) & 0xff,
mantissa: self.0 & 0xffff,
},
_ => {
let mut name = [0; 8];
let mut body = self.0;
let mut i = 0;
while body > 0 {
let b = (body % 37) as u8 - 1;
name[i] = b + if b < 10 { b'0' } else { b'a' - 10 };
body /= 37;
i += 1;
}
LayoutContent::Named { name }
}
}
}
}

/// Gets the mantissa bits of the digit data type.
#[inline]
pub const fn mantissa(self) -> usize {
self.mantissa as _
}
#[test]
fn test_unsigned() {
assert!(matches!(
types::U8.decode(),
LayoutContent::Unsigned { width: 8 }
));

assert!(matches!(
types::U16.decode(),
LayoutContent::Unsigned { width: 16 }
));

assert!(matches!(
types::U32.decode(),
LayoutContent::Unsigned { width: 32 }
));

assert!(matches!(
types::U64.decode(),
LayoutContent::Unsigned { width: 64 }
));
}

/// Gets the padding bits of the digit data type.
#[inline]
pub const fn padding(self) -> usize {
self.nbits() - self.packed() * (self.signed() as usize + self.exponent() + self.mantissa())
}
#[test]
fn test_real() {
assert!(matches!(
types::I8.decode(),
LayoutContent::Real {
exponent: 0,
mantissa: 8,
}
));

/// Gets the total bits of the digit data type.
#[inline]
pub const fn nbits(self) -> usize {
self.nbytes() * _8
}
assert!(matches!(
types::I16.decode(),
LayoutContent::Real {
exponent: 0,
mantissa: 16,
}
));

/// Gets the number of bytes of the digit data type.
#[inline]
pub const fn nbytes(self) -> usize {
(self.signed_nbyte & ((1 << _7) - 1)) as _
}
assert!(matches!(
types::I32.decode(),
LayoutContent::Real {
exponent: 0,
mantissa: 32,
}
));

/// Gets the layout of the digit data type.
#[inline]
pub const fn layout(self) -> Layout {
let size = self.nbytes();
let align = if size < MAX_ALIGN { size } else { MAX_ALIGN };
unsafe { Layout::from_size_align_unchecked(size, align) }
}
assert!(matches!(
types::I64.decode(),
LayoutContent::Real {
exponent: 0,
mantissa: 64,
}
));

assert!(matches!(
types::F16.decode(),
LayoutContent::Real {
exponent: 5,
mantissa: 10,
}
));

assert!(matches!(
types::BF16.decode(),
LayoutContent::Real {
exponent: 8,
mantissa: 7,
}
));

assert!(matches!(
types::F32.decode(),
LayoutContent::Real {
exponent: 8,
mantissa: 23,
}
));

assert!(matches!(
types::F64.decode(),
LayoutContent::Real {
exponent: 11,
mantissa: 52,
}
));
}

#[test]
fn test_named() {
assert!(matches!(
types::Bool.decode(),
LayoutContent::Named {
name: [b'b', b'o', b'o', b'l', 0, 0, 0, 0]
}
));

let q8_0 = DigitLayout::named("Q8_0");
assert!(matches!(
q8_0.decode(),
LayoutContent::Named {
name: [b'q', b'8', b'0', 0, 0, 0, 0, 0]
}
));

let zzzzzz = DigitLayout::named("zzzzzz");
assert!(matches!(
zzzzzz.decode(),
LayoutContent::Named {
name: [b'z', b'z', b'z', b'z', b'z', b'z', 0, 0]
}
));
}
46 changes: 9 additions & 37 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,20 @@
/// 定义一个 [`DigitLayout`](crate::DigitLayout) 实例。
#[macro_export]
macro_rules! layout {
($name:ident i($bits:expr)x($packed:expr)) => {
($name:ident u($bits:expr)) => {
#[allow(non_upper_case_globals)]
pub const $name: $crate::DigitLayout =
$crate::DigitLayout::new($packed, true, 0, $bits - 1);
pub const $name: $crate::DigitLayout = $crate::DigitLayout::unsigned($bits);
};
($name:ident u($bits:expr)x($packed:expr)) => {
($name:ident i($bits:expr)) => {
#[allow(non_upper_case_globals)]
pub const $name: $crate::DigitLayout =
$crate::DigitLayout::new($packed, false, 0, $bits);
pub const $name: $crate::DigitLayout = $crate::DigitLayout::real(0, $bits);
};
($name:ident e($exp:expr)m($mant:expr)x($packed:expr)) => {
($name:ident e($exponent:expr)m($mantissa:expr)) => {
#[allow(non_upper_case_globals)]
pub const $name: $crate::DigitLayout =
$crate::DigitLayout::new($packed, true, $exp, $mant);
};

($name:ident i($bits:expr)) => {
layout!($name i($bits)x(1));
};
($name:ident u($bits:expr)) => {
layout!($name u($bits)x(1));
pub const $name: $crate::DigitLayout = $crate::DigitLayout::real($exponent, $mantissa);
};
($name:ident e($exp:expr)m($mant:expr)) => {
layout!($name e($exp)m($mant)x(1));
};
}

/// 为类型实现与 [`DigitLayout`](crate::DigitLayout) 相关的 trait。
#[macro_export]
macro_rules! impl_digit {
($ty:ty : $digit:expr) => {
impl $crate::AsDigit for $ty {
const LAYOUT: $crate::DigitLayout = $digit;
}
};

($ty:ty : $digit:expr, $const:ident) => {
impl_digit!($ty : $digit);

const $const: u32 = $digit.to_u32();
impl $crate::TypeOf_<$const> for $crate::TypeOf<$const> {
type Type = $ty;
}
($name:ident $text:literal) => {
#[allow(non_upper_case_globals)]
pub const $name: $crate::DigitLayout = $crate::DigitLayout::named($text);
};
}
Loading

0 comments on commit bd5eb53

Please sign in to comment.