From bd5eb530b7f2100fe51d061d5485a9622a4d8f54 Mon Sep 17 00:00:00 2001 From: YdrMaster Date: Fri, 30 Aug 2024 18:13:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E7=94=A8=E5=90=8D=E5=AD=97=E6=9D=A5?= =?UTF-8?q?=E5=8C=BA=E5=88=86=E5=A4=8D=E6=9D=82=E7=9A=84=E9=87=8F=E5=8C=96?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: YdrMaster --- Cargo.toml | 4 +- README.md | 2 +- src/lib.rs | 285 +++++++++++++++++++++++++++++++++++--------------- src/macros.rs | 46 ++------ src/types.rs | 54 +++------- 5 files changed, 225 insertions(+), 166 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5bc7015..3b4f064 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 "] repository = "https://github.com/YdrMaster/digit-layout.git" diff --git a/README.md b/README.md index 7eda7ef..ccdb73f 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/src/lib.rs b/src/lib.rs index ce2dad2..cbb11dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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_ { - /// 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 _8: usize = u8::BITS as usize; -const _7: usize = _8 - 1; -const MAX_ALIGN: usize = align_of::(); +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] + } + )); } diff --git a/src/macros.rs b/src/macros.rs index 9457091..6ae34bf 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -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); }; } diff --git a/src/types.rs b/src/types.rs index b9d048a..65cf9eb 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,44 +2,16 @@ #![allow(missing_docs)] -layout!(BOOL u( 1) ); -layout!(I8 i( 8) ); -layout!(I16 i(16) ); -layout!(I32 i(32) ); -layout!(I64 i(64) ); -layout!(U8 u( 8) ); -layout!(U16 u(16) ); -layout!(U32 u(32) ); -layout!(U64 u(64) ); -layout!(F16 e(10)m( 5) ); -layout!(BF16 e( 7)m( 8) ); -layout!(F32 e(23)m( 8) ); -layout!(F64 e(52)m(11) ); - -layout!(F16x2 e(10)m( 5)x(2) ); -layout!(BF16x2 e( 7)m( 8)x(2) ); - -impl_digit!(bool: BOOL, CBOOL); -impl_digit!(i8 : I8 , CI8 ); -impl_digit!(i16 : I16 , CI16 ); -impl_digit!(i32 : I32 , CI32 ); -impl_digit!(i64 : I64 , CI64 ); -impl_digit!(u8 : U8 , CU8 ); -impl_digit!(u16 : U16 , CU16 ); -impl_digit!(u32 : U32 , CU32 ); -impl_digit!(u64 : U64 , CU64 ); -impl_digit!(f32 : F32 , CF32 ); -impl_digit!(f64 : F64 , CF64 ); - -#[cfg(feature = "half")] -mod half_impl { - use half::{bf16, f16}; - - impl_digit!( f16 : super::F16 , CF16 ); - impl_digit!([f16 ; 2] : super::F16x2 , CF16_2 ); - impl_digit!((f16 , f16): super::F16x2 ); - - impl_digit!( bf16 : super::BF16 , CBF16 ); - impl_digit!([bf16; 2] : super::BF16x2, CBF16_2); - impl_digit!((bf16, bf16): super::BF16x2 ); -} +layout!(I8 i( 8) ); +layout!(I16 i(16) ); +layout!(I32 i(32) ); +layout!(I64 i(64) ); +layout!(U8 u( 8) ); +layout!(U16 u(16) ); +layout!(U32 u(32) ); +layout!(U64 u(64) ); +layout!(F16 e( 5)m(10)); +layout!(BF16 e( 8)m( 7)); +layout!(F32 e( 8)m(23)); +layout!(F64 e(11)m(52)); +layout!(Bool "bool" );