diff --git a/bit-wrap/src/ast.rs b/bit-wrap/src/ast.rs index 94bc441..ca8ea26 100644 --- a/bit-wrap/src/ast.rs +++ b/bit-wrap/src/ast.rs @@ -1,8 +1,9 @@ use crate::grammar; -use unsynn::*; +use quote::format_ident; +use unsynn::Ident; pub struct Struct { - pub bit_width: u128, + pub bit_width: u8, pub error_type: Ident, pub name: Ident, pub body: Ident, @@ -10,9 +11,11 @@ pub struct Struct { impl From for Struct { fn from(value: grammar::StructDef) -> Self { + let bit_width = u8::try_from(value.bit_width.bit_width.content.width.content.value()) + .expect("8-bit value"); Self { - bit_width: value.bit_width.bit_width.content.width.content.value(), - error_type: value.error_type.error.content.error.content, + bit_width, + error_type: format_ident!("{}Error", value.name), name: value.name, body: value.body.content, } diff --git a/bit-wrap/src/bit_wrapper.rs b/bit-wrap/src/bit_wrapper.rs index 4eac0ee..8e2082a 100644 --- a/bit-wrap/src/bit_wrapper.rs +++ b/bit-wrap/src/bit_wrapper.rs @@ -1,5 +1,5 @@ use crate::{codegen::generate_impl, grammar::StructDef}; -use unsynn::*; +use unsynn::{IParse, ToTokens, TokenStream}; pub fn impl_bit_wrapper(input: &TokenStream) -> TokenStream { let parsed = input diff --git a/bit-wrap/src/codegen.rs b/bit-wrap/src/codegen.rs index f63c3d5..b52cf54 100644 --- a/bit-wrap/src/codegen.rs +++ b/bit-wrap/src/codegen.rs @@ -1,8 +1,8 @@ use crate::ast::Struct; use quote::quote; -use unsynn::*; +use unsynn::{Ident, TokenStream}; -#[ignore = "too_many_lines"] +#[allow(clippy::too_many_lines)] pub fn generate_impl(info: &Struct) -> TokenStream { let name = &info.name; let inner = &info.body; @@ -10,8 +10,12 @@ pub fn generate_impl(info: &Struct) -> TokenStream { let ops = generate_bitwise_ops(info); let fmt = generate_bitwise_fmt(info); let wrapper = generate_bitwise_wrapper(info); + let error = generate_error(info); + let conversions = generate_conversions(info); quote! { + #error + #conversions #ops #fmt #wrapper @@ -37,6 +41,91 @@ pub fn generate_impl(info: &Struct) -> TokenStream { } } +fn generate_conversions(info: &Struct) -> TokenStream { + let name = &info.name; + let inner = &info.body; + let error_type = &info.error_type; + let bit_width = info.bit_width; + + // Generate From implementations for smaller integer types (guaranteed safe) + let impl_from = |target_width: u8, srouce_type: &TokenStream| { + generate_from_impl(bit_width, target_width, srouce_type, name, inner) + }; + + let from_u8 = impl_from(8, "e! { u8 }); + let from_u16 = impl_from(15, "e! { u16 }); + let from_u32 = impl_from(32, "e! { u32 }); + let from_u64 = impl_from(64, "e! { u64 }); + + let impl_try_from = |target_width: u8, srouce_type: &TokenStream| { + generate_try_from_impl( + bit_width, + target_width, + srouce_type, + name, + inner, + error_type, + ) + }; + let try_from_u8 = impl_try_from(8, "e! { u8 }); + let try_from_u16 = impl_try_from(16, "e! { u16 }); + let try_from_u32 = impl_try_from(32, "e! { u32 }); + let try_from_u64 = impl_try_from(64, "e! { u64 }); + + quote! { + #from_u8 + #from_u16 + #from_u32 + #from_u64 + #try_from_u8 + #try_from_u16 + #try_from_u32 + #try_from_u64 + } +} + +fn generate_error(info: &Struct) -> TokenStream { + let error_type = &info.error_type; + let name = &info.name; + let inner = &info.body; + + quote! { + #[derive(Debug, thiserror::Error)] + pub enum #error_type { + #[error("#name must be a {width}-bit number (0 to {max}), got {value}")] + ValueOutOfRange { + value: #inner, + max: #inner, + width: u8 + }, + + #[error("Hex value 0x{value:X} exceeds {bit_width}-bit limit (masked to 0x{masked:0width$X})")] + ExceedsBitLimit { + value: u64, + bit_width: u8, + masked: u64, + width: usize, + }, + + #[error("Failed to parse subkey value: {0}")] + ParseError(#[from] std::num::ParseIntError), + + #[error("Unknown error: {0}")] + Unknown(String), + } + + impl #error_type { + pub fn value_out_of_range(value: #inner) -> Self { + Self::ValueOutOfRange { + value, + max: #name::MAX, + width: #name::BIT_WIDTH, + } + } + } + } +} + fn generate_bitwise_fmt(info: &Struct) -> TokenStream { let name = &info.name; @@ -53,13 +142,13 @@ fn generate_bitwise_fmt(info: &Struct) -> TokenStream { } } - impl std::fmt::Debug for Subkey { + impl std::fmt::Debug for #name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Subkey(0x{:012X})", self.0) + write!(f, "#name(0x{:012X})", self.0) } } - impl std::fmt::Display for Subkey { + impl std::fmt::Display for #name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "0x{:012X}", self.0) } @@ -67,16 +156,22 @@ fn generate_bitwise_fmt(info: &Struct) -> TokenStream { } } +#[allow(clippy::similar_names)] +#[allow(clippy::too_many_lines)] fn generate_bitwise_ops(info: &Struct) -> TokenStream { let name = &info.name; let inner = &info.body; let error_type = &info.error_type; - let bit_width = u8::try_from(info.bit_width).expect("8-bit value"); + let bit_width = info.bit_width; let hex_width = usize::from(bit_width.div_ceil(4)); let bin_width = usize::from(bit_width); - let max_value = (1u64 << (bit_width)) - 1; + let max_value = if bit_width == 64 { + u64::MAX + } else { + (1u64 << (bit_width)) - 1 + }; quote! { impl #name { @@ -316,117 +411,162 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream { } } -fn generate_bitwise_wrapper(_info: &Struct) -> TokenStream { +fn generate_bitwise_wrapper(info: &Struct) -> TokenStream { + let name = &info.name; + quote! { - impl std::ops::Not for Subkey { + impl std::ops::Not for #name { type Output = Self; fn not(self) -> Self::Output { Self(!self.0) } } - impl std::ops::BitAnd for Subkey { + impl std::ops::BitAnd for #name { type Output = Self; fn bitand(self, rhs: Self) -> Self::Output { Self(self.0 & rhs.0) } } - impl std::ops::BitAnd<&Self> for Subkey { + impl std::ops::BitAnd<&Self> for #name { type Output = Self; fn bitand(self, rhs: &Self) -> Self::Output { Self(self.0 & rhs.0) } } - impl std::ops::BitAndAssign for Subkey { + impl std::ops::BitAndAssign for #name { fn bitand_assign(&mut self, rhs: Self) { self.0 &= rhs.0; } } - impl std::ops::BitAndAssign<&Self> for Subkey { + impl std::ops::BitAndAssign<&Self> for #name { fn bitand_assign(&mut self, rhs: &Self) { self.0 &= rhs.0; } } - impl std::ops::BitOr for Subkey { + impl std::ops::BitOr for #name { type Output = Self; fn bitor(self, rhs: Self) -> Self::Output { Self(self.0 | rhs.0) } } - impl std::ops::BitOr<&Self> for Subkey { + impl std::ops::BitOr<&Self> for #name { type Output = Self; fn bitor(self, rhs: &Self) -> Self::Output { Self(self.0 | rhs.0) } } - impl std::ops::BitOrAssign for Subkey { + impl std::ops::BitOrAssign for #name { fn bitor_assign(&mut self, rhs: Self) { self.0 |= rhs.0; } } - impl std::ops::BitOrAssign<&Self> for Subkey { + impl std::ops::BitOrAssign<&Self> for #name { fn bitor_assign(&mut self, rhs: &Self) { self.0 |= rhs.0; } } - impl std::ops::BitXor for Subkey { + impl std::ops::BitXor for #name { type Output = Self; fn bitxor(self, rhs: Self) -> Self { Self(self.0 ^ rhs.0) } } - impl std::ops::BitXor<&Self> for Subkey { + impl std::ops::BitXor<&Self> for #name { type Output = Self; fn bitxor(self, rhs: &Self) -> Self { Self(self.0 ^ rhs.0) } } - impl std::ops::BitXorAssign for Subkey { + impl std::ops::BitXorAssign for #name { fn bitxor_assign(&mut self, rhs: Self) { self.0 ^= rhs.0; } } - impl std::ops::BitXorAssign<&Self> for Subkey { + impl std::ops::BitXorAssign<&Self> for #name { fn bitxor_assign(&mut self, rhs: &Self) { self.0 ^= rhs.0; } } - impl std::ops::Shl for Subkey { + impl std::ops::Shl for #name { type Output = Self; fn shl(self, rhs: u32) -> Self { Self(self.0 << rhs) } } - impl std::ops::ShlAssign for Subkey { + impl std::ops::ShlAssign for #name { fn shl_assign(&mut self, rhs: u32) { self.0 <<= rhs; } } - impl std::ops::Shr for Subkey { + impl std::ops::Shr for #name { type Output = Self; fn shr(self, rhs: u32) -> Self::Output { Self(self.0 >> rhs) } } - impl std::ops::ShrAssign for Subkey { + impl std::ops::ShrAssign for #name { fn shr_assign(&mut self, rhs: u32) { self.0 >>= rhs; } } } } + +fn generate_from_impl( + bit_width: u8, + target_width: u8, + source_type: &TokenStream, + target_type: &Ident, + inner_type: &Ident, +) -> Option { + if bit_width < target_width { + return None; + } + Some(quote! { + impl std::convert::From<#source_type> for #target_type { + fn from(key: #source_type) -> Self { + Self(key as #inner_type) + } + } + }) +} + +fn generate_try_from_impl( + bit_width: u8, + target_width: u8, + source_type: &TokenStream, + target_type: &Ident, + inner_type: &Ident, + error_type: &Ident, +) -> Option { + if bit_width >= target_width { + return None; + } + Some(quote! { + impl std::convert::TryFrom<#source_type> for #target_type { + type Error = #error_type; + fn try_from(key: #source_type) -> Result { + if key > #target_type::MAX { + return Err(#error_type::value_out_of_range(key)); + } + Ok(Self(key as #inner_type)) + } + } + }) +} diff --git a/bit-wrap/src/grammar.rs b/bit-wrap/src/grammar.rs index c154790..509b707 100644 --- a/bit-wrap/src/grammar.rs +++ b/bit-wrap/src/grammar.rs @@ -1,10 +1,12 @@ -use unsynn::*; +use unsynn::{ + BracketGroupContaining, Ident, LiteralInteger, ParenthesisGroupContaining, Pound, Semicolon, + TokenIter, keyword, unsynn, +}; keyword! { pub KwStruct = "struct"; pub KwPub = "pub"; pub KwBitWidth = "bit_width"; - pub KwError = "bit_conversion_error"; } unsynn! { @@ -18,19 +20,8 @@ unsynn! { pub bit_width: BracketGroupContaining, } - pub struct ErrorType { - pub kw_bit_width: KwError, - pub error: ParenthesisGroupContaining, - } - - pub struct AttributeError { - pub pound: Pound, - pub error: BracketGroupContaining, - } - pub struct StructDef { pub bit_width: Attribute, - pub error_type: AttributeError, pub vis: Option, pub kw_struct: KwStruct, pub name: Ident, @@ -38,24 +29,3 @@ unsynn! { pub semi: Semicolon, } } - -#[cfg(test)] -mod tests { - use super::*; - - const SAMPLE: &str = r#" - #[bit_width(48)] - #[bit_conversion_error()] - pub struct Subkey(u64); - "#; - - #[test] - fn parse_attribute() { - let mut iter = SAMPLE.to_token_iter(); - let sdef = iter - .parse::() - .expect("failed to parse StructDef"); - dbg!(sdef); - assert!(false); - } -} diff --git a/bit-wrap/src/lib.rs b/bit-wrap/src/lib.rs index 0921da9..ff30cb0 100644 --- a/bit-wrap/src/lib.rs +++ b/bit-wrap/src/lib.rs @@ -6,7 +6,7 @@ mod grammar; use crate::bit_wrapper::impl_bit_wrapper; use proc_macro::TokenStream; -#[proc_macro_derive(BitWrapper, attributes(bit_width, bit_conversion_error))] +#[proc_macro_derive(BitWrapper, attributes(bit_width))] pub fn derive_bit_wrapper(input: TokenStream) -> TokenStream { impl_bit_wrapper(&input.into()).into() } diff --git a/des-lib/src/keys/key.rs b/des-lib/src/keys/key.rs index 3c16686..81986be 100644 --- a/des-lib/src/keys/key.rs +++ b/des-lib/src/keys/key.rs @@ -1,29 +1,5 @@ -use std::fmt::{Debug, Display}; +use bit_wrap::BitWrapper; -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, BitWrapper)] +#[bit_width(64)] pub struct Key(u64); - -impl Key { - // #[macro_use] - // pub fn new(key: u64) -> Self { - // key.into() - // } -} - -impl From for Key { - fn from(key: u64) -> Self { - Self(key) - } -} - -impl Debug for Key { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Key(0x{:016X})", self.0) - } -} - -impl Display for Key { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "0x{:016X}", self.0) - } -} diff --git a/des-lib/src/keys/subkey.rs b/des-lib/src/keys/subkey.rs index 965b746..0198fab 100644 --- a/des-lib/src/keys/subkey.rs +++ b/des-lib/src/keys/subkey.rs @@ -3,31 +3,3 @@ use bit_wrap::BitWrapper; #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, BitWrapper)] #[bit_width(48)] pub struct Subkey(u64); - -// impl TryFrom for Subkey { -// type Error = SubkeyError; -// fn try_from(key: u64) -> Result { -// if key > Self::MAX { -// return Err(SubkeyError::ValueOutOfRange(key)); -// } -// Ok(Self(key)) -// } -// } -// -// impl From for Subkey { -// fn from(value: u32) -> Self { -// Self(u64::from(value)) -// } -// } -// -// impl From for Subkey { -// fn from(value: u16) -> Self { -// Self(u64::from(value)) -// } -// } -// -// impl From for Subkey { -// fn from(value: u8) -> Self { -// Self(u64::from(value)) -// } -// }