use std::fmt::{self, Display, Formatter, Write};
use std::hash::Hash;
use amplify::Wrapper;
use crate::STRICT_TYPES_LIB;
pub mod constants {
use super::Primitive;
pub const U8: Primitive = Primitive::unsigned(1);
pub const U16: Primitive = Primitive::unsigned(2);
pub const U24: Primitive = Primitive::unsigned(3);
pub const U32: Primitive = Primitive::unsigned(4);
pub const U48: Primitive = Primitive::unsigned(6);
pub const U64: Primitive = Primitive::unsigned(8);
pub const U128: Primitive = Primitive::unsigned(16);
pub const U160: Primitive = Primitive::unsigned(20);
pub const U256: Primitive = Primitive::unsigned(32);
pub const U512: Primitive = Primitive::unsigned(64);
pub const U1024: Primitive = Primitive::unsigned(128);
pub const I8: Primitive = Primitive::signed(1);
pub const I16: Primitive = Primitive::signed(2);
pub const I24: Primitive = Primitive::signed(3);
pub const I32: Primitive = Primitive::signed(4);
pub const I48: Primitive = Primitive::signed(6);
pub const I64: Primitive = Primitive::signed(8);
pub const I128: Primitive = Primitive::signed(16);
pub const I256: Primitive = Primitive::signed(32);
pub const I512: Primitive = Primitive::signed(64);
pub const I1024: Primitive = Primitive::signed(128);
pub const N8: Primitive = Primitive::non_zero(1);
pub const N16: Primitive = Primitive::non_zero(2);
pub const N24: Primitive = Primitive::non_zero(3);
pub const N32: Primitive = Primitive::non_zero(4);
pub const N48: Primitive = Primitive::non_zero(6);
pub const N64: Primitive = Primitive::non_zero(8);
pub const N128: Primitive = Primitive::non_zero(16);
pub const F16: Primitive = Primitive::float(2);
pub const F32: Primitive = Primitive::float(4);
pub const F64: Primitive = Primitive::float(8);
pub const F80: Primitive = Primitive::float(10);
pub const F128: Primitive = Primitive::float(16);
pub const F256: Primitive = Primitive::float(32);
pub const UNIT: Primitive = Primitive(0x00);
pub const BYTE: Primitive = Primitive(0x40);
pub const RESERVED: Primitive = Primitive(0x80);
pub const F16B: Primitive = Primitive(0xC0);
pub const FLOAT_RESERVED_1: Primitive = Primitive(0xC1);
pub const FLOAT_RESERVED_2: Primitive = Primitive(0xC3);
pub const FLOAT_RESERVED_3: Primitive = Primitive(0xC5);
pub const FLOAT_RESERVED_4: Primitive = Primitive(0xC6);
pub const FLOAT_RESERVED_5: Primitive = Primitive(0xC7);
pub const FLOAT_RESERVED_6: Primitive = Primitive(0xC9);
pub const FLOAT_RESERVED_7: Primitive = Primitive(0xCB);
pub const FLOAT_RESERVED_8: Primitive = Primitive(0xCC);
pub const FLOAT_RESERVED_9: Primitive = Primitive(0xCD);
pub const FLOAT_RESERVED_10: Primitive = Primitive(0xCE);
pub const FLOAT_RESERVED_11: Primitive = Primitive(0xCF);
pub const FLOAT_RESERVED_12: Primitive = Primitive(0xD1);
pub const FLOAT_RESERVED_13: Primitive = Primitive(0xD2);
pub const FLOAT_RESERVED_14: Primitive = Primitive(0xD3);
pub const FLOAT_RESERVED_15: Primitive = Primitive(0xD4);
pub const FLOAT_RESERVED_16: Primitive = Primitive(0xD5);
pub const FLOAT_RESERVED_17: Primitive = Primitive(0xD6);
pub const FLOAT_RESERVED_18: Primitive = Primitive(0xD7);
pub const FLOAT_RESERVED_19: Primitive = Primitive(0xD8);
pub const FLOAT_RESERVED_20: Primitive = Primitive(0xD9);
pub const FLOAT_RESERVED_21: Primitive = Primitive(0xDA);
pub const FLOAT_RESERVED_22: Primitive = Primitive(0xDB);
pub const FLOAT_RESERVED_23: Primitive = Primitive(0xDC);
pub const FLOAT_RESERVED_24: Primitive = Primitive(0xDE);
pub const FLOAT_RESERVED_25: Primitive = Primitive(0xDF);
pub const FLOAT_RESERVED_26: Primitive = Primitive(0xE1);
pub const FLOAT_RESERVED_27: Primitive = Primitive(0xE2);
pub const FLOAT_RESERVED_28: Primitive = Primitive(0xE3);
pub const FLOAT_RESERVED_29: Primitive = Primitive(0xE4);
pub const FLOAT_RESERVED_30: Primitive = Primitive(0xE5);
pub const FLOAT_RESERVED_31: Primitive = Primitive(0xE6);
pub const FLOAT_RESERVED_32: Primitive = Primitive(0xE7);
pub const FLOAT_RESERVED_33: Primitive = Primitive(0xE8);
pub const FLOAT_RESERVED_34: Primitive = Primitive(0xE9);
pub const FLOAT_RESERVED_35: Primitive = Primitive(0xEA);
pub const FLOAT_RESERVED_36: Primitive = Primitive(0xEB);
pub const FLOAT_RESERVED_37: Primitive = Primitive(0xEC);
pub const FLOAT_RESERVED_38: Primitive = Primitive(0xEE);
pub const FLOAT_RESERVED_39: Primitive = Primitive(0xEF);
pub const FLOAT_RESERVED_40: Primitive = Primitive(0xF0);
pub const FLOAT_RESERVED_41: Primitive = Primitive(0xF1);
pub const FLOAT_RESERVED_42: Primitive = Primitive(0xF2);
pub const FLOAT_RESERVED_43: Primitive = Primitive(0xF3);
pub const FLOAT_RESERVED_44: Primitive = Primitive(0xF4);
pub const FLOAT_RESERVED_45: Primitive = Primitive(0xF5);
pub const FLOAT_RESERVED_46: Primitive = Primitive(0xF6);
pub const FLOAT_RESERVED_47: Primitive = Primitive(0xF7);
pub const FLOAT_RESERVED_48: Primitive = Primitive(0xF8);
pub const FLOAT_RESERVED_49: Primitive = Primitive(0xF9);
pub const FLOAT_RESERVED_50: Primitive = Primitive(0xFA);
pub const FLOAT_RESERVED_51: Primitive = Primitive(0xFB);
pub const FLOAT_RESERVED_52: Primitive = Primitive(0xFC);
pub const FLOAT_RESERVED_53: Primitive = Primitive(0xFE);
pub const FLOAT_RESERVED_54: Primitive = Primitive(0xFF);
}
use self::constants::*;
#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
pub struct Primitive(u8);
impl Primitive {
pub const fn unsigned(bytes: u16) -> Self {
Primitive(
NumInfo {
ty: NumCls::Unsigned,
size: NumSize::from_bytes(bytes),
}
.into_code(),
)
}
pub const fn signed(bytes: u16) -> Self {
Primitive(
NumInfo {
ty: NumCls::Signed,
size: NumSize::from_bytes(bytes),
}
.into_code(),
)
}
pub const fn non_zero(bytes: u16) -> Self {
Primitive(
NumInfo {
ty: NumCls::NonZero,
size: NumSize::from_bytes(bytes),
}
.into_code(),
)
}
pub const fn float(bytes: u16) -> Self {
Primitive(
NumInfo {
ty: NumCls::Float,
size: NumSize::from_bytes(bytes),
}
.into_code(),
)
}
pub const fn from_code(code: u8) -> Self { Primitive(code) }
pub const fn into_code(self) -> u8 { self.0 }
pub const fn info(self) -> NumInfo { NumInfo::from_code(self.0) }
pub const fn byte_size(self) -> u16 { self.info().byte_size() }
}
impl Display for Primitive {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
UNIT => return f.write_str("()"),
BYTE => return f.write_str("Byte"),
F16B => return f.write_str("F16b"),
RESERVED => unreachable!("reserved primitive value"),
_ => {}
}
let info = self.info();
f.write_char(match info.ty {
NumCls::Unsigned => 'U',
NumCls::Signed => 'I',
NumCls::NonZero => 'N',
NumCls::Float => 'F',
})?;
write!(f, "{}", info.byte_size() * 8)
}
}
impl_strict_newtype!(Primitive, STRICT_TYPES_LIB);
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct NumInfo {
pub ty: NumCls,
pub size: NumSize,
}
impl NumInfo {
pub const fn from_code(id: u8) -> Self {
NumInfo {
ty: NumCls::from_code(id),
size: NumSize::from_code(id),
}
}
pub const fn into_code(self) -> u8 { self.ty.into_code() | self.size.into_code() }
pub const fn byte_size(self) -> u16 { self.size.byte_size() }
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct NumSize(NumSizeInner);
impl NumSize {
pub(super) const fn from_bytes(bytes: u16) -> Self {
NumSize(if bytes < 0x20 {
NumSizeInner::Bytes(bytes as u8)
} else if bytes % 16 != 0 {
unreachable!()
} else {
NumSizeInner::Factored((bytes / 16 - 2) as u8)
})
}
pub(super) const fn from_code(id: u8) -> Self {
let code = id & 0x1F;
NumSize(match (id & 0x20) / 0x20 {
0 => NumSizeInner::Bytes(code),
1 => NumSizeInner::Factored(code),
_ => unreachable!(),
})
}
pub(super) const fn into_code(self) -> u8 {
match self.0 {
NumSizeInner::Bytes(bytes) => bytes,
NumSizeInner::Factored(factor) => factor | 0x20,
}
}
pub const fn byte_size(self) -> u16 {
match self.0 {
NumSizeInner::Bytes(bytes) => bytes as u16,
NumSizeInner::Factored(factor) => 2 * (factor as u16 + 1),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum NumSizeInner {
Bytes(u8),
Factored(u8),
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum NumCls {
Unsigned = 0x00,
Signed = 0x40,
NonZero = 0x80,
Float = 0xC0,
}
impl NumCls {
pub const fn from_code(id: u8) -> Self {
match id & 0xC0 {
x if x == NumCls::Unsigned as u8 => NumCls::Unsigned,
x if x == NumCls::Signed as u8 => NumCls::Signed,
x if x == NumCls::NonZero as u8 => NumCls::NonZero,
x if x == NumCls::Float as u8 => NumCls::Float,
_ => unreachable!(),
}
}
pub const fn into_code(self) -> u8 { self as u8 }
}
#[cfg(test)]
mod test {
use super::constants::U8;
#[test]
fn u8() {
let prim = U8;
assert_eq!(prim.byte_size(), 1);
}
}