diff --git a/objc2-encode/src/lib.rs b/objc2-encode/src/lib.rs index 96792291e..e6367b655 100644 --- a/objc2-encode/src/lib.rs +++ b/objc2-encode/src/lib.rs @@ -107,5 +107,9 @@ mod encode; mod encoding; mod parse; +// Will be used at some point when generic constants are available +#[allow(dead_code)] +mod static_str; + pub use self::encode::{Encode, EncodeArguments, RefEncode}; pub use self::encoding::Encoding; diff --git a/objc2-encode/src/static_str.rs b/objc2-encode/src/static_str.rs new file mode 100644 index 000000000..3dc3bc3be --- /dev/null +++ b/objc2-encode/src/static_str.rs @@ -0,0 +1,263 @@ +use super::Encoding; + +pub(crate) const fn static_int_str_len(mut n: u128) -> usize { + let mut i = 0; + if n == 0 { + return 1; + } + while n > 0 { + n = n / 10; + i += 1; + } + i +} + +pub(crate) const fn static_int_str_array(mut n: u128) -> [u8; RES] { + let mut res: [u8; RES] = [0; RES]; + let mut i = 0; + if n == 0 { + res[0] = '0' as u8; + return res; + } + while n > 0 { + res[i] = '0' as u8 + (n % 10) as u8; + n = n / 10; + i += 1; + } + + let mut rev: [u8; RES] = [0; RES]; + let mut rev_i = 0; + while 0 < i { + i -= 1; + rev[rev_i] = res[i]; + n = n / 10; + rev_i += 1; + } + rev +} + +pub(crate) const fn static_encoding_str_len(encoding: Encoding<'_>) -> usize { + use Encoding::*; + + match encoding { + Char | Short | Int | Long | LongLong | UChar | UShort | UInt | ULong | ULongLong + | Float | Double | LongDouble | Bool | Void | String | Object | Class | Sel + | Unknown => 1, + Block | FloatComplex | DoubleComplex | LongDoubleComplex => 2, + BitField(b, _type) => 1 + static_int_str_len(b as u128), + Pointer(&t) => 1 + static_encoding_str_len(t), + Array(len, &item) => { + 1 + static_int_str_len(len as u128) + static_encoding_str_len(item) + 1 + } + Struct(name, items) | Union(name, items) => { + let mut res = 1 + name.len() + 1; + let mut i = 0; + while i < items.len() { + res += static_encoding_str_len(items[i]); + i += 1; + } + res + 1 + } + } +} + +pub(crate) const fn static_encoding_str_array(encoding: Encoding<'_>) -> [u8; LEN] { + use Encoding::*; + + let mut res: [u8; LEN] = [0; LEN]; + + match encoding { + Char => res[0] = 'c' as u8, + Short => res[0] = 's' as u8, + Int => res[0] = 'i' as u8, + Long => res[0] = 'l' as u8, + LongLong => res[0] = 'q' as u8, + UChar => res[0] = 'C' as u8, + UShort => res[0] = 'S' as u8, + UInt => res[0] = 'I' as u8, + ULong => res[0] = 'L' as u8, + ULongLong => res[0] = 'Q' as u8, + Float => res[0] = 'f' as u8, + Double => res[0] = 'd' as u8, + LongDouble => res[0] = 'D' as u8, + FloatComplex => { + res[0] = 'j' as u8; + res[1] = 'f' as u8; + } + DoubleComplex => { + res[0] = 'j' as u8; + res[1] = 'd' as u8; + } + LongDoubleComplex => { + res[0] = 'j' as u8; + res[1] = 'D' as u8; + } + Bool => res[0] = 'B' as u8, + Void => res[0] = 'v' as u8, + Block => { + res[0] = '@' as u8; + res[1] = '?' as u8; + } + String => res[0] = '*' as u8, + Object => res[0] = '@' as u8, + Class => res[0] = '#' as u8, + Sel => res[0] = ':' as u8, + Unknown => res[0] = '?' as u8, + BitField(b, &_type) => { + let mut res_i = 0; + + res[res_i] = 'b' as u8; + res_i += 1; + + let mut i = 0; + // We use 3 even though it creates an oversized array + let arr = static_int_str_array::<3>(b as u128); + while i < static_int_str_len(b as u128) { + res[res_i] = arr[i]; + res_i += 1; + i += 1; + } + } + Pointer(&t) => { + let mut res_i = 0; + + res[res_i] = '^' as u8; + res_i += 1; + + let mut i = 0; + // We use LEN even though it creates an oversized array + let arr = static_encoding_str_array::(t); + while i < static_encoding_str_len(t) { + res[res_i] = arr[i]; + res_i += 1; + i += 1; + } + } + Array(len, &item) => { + let mut res_i = 0; + + res[res_i] = '[' as u8; + res_i += 1; + + let mut i = 0; + // We use 20 even though it creates an oversized array + let arr = static_int_str_array::<20>(len as u128); + while i < static_int_str_len(len as u128) { + res[res_i] = arr[i]; + res_i += 1; + i += 1; + } + + let mut i = 0; + // We use LEN even though it creates an oversized array + let arr = static_encoding_str_array::(item); + while i < static_encoding_str_len(item) { + res[res_i] = arr[i]; + res_i += 1; + i += 1; + } + + res[res_i] = ']' as u8; + } + Struct(name, items) | Union(name, items) => { + let mut res_i = 0; + + match encoding { + Struct(_, _) => res[res_i] = '{' as u8, + Union(_, _) => res[res_i] = '(' as u8, + _ => {} + }; + res_i += 1; + + let mut name_i = 0; + let name = name.as_bytes(); + while name_i < name.len() { + res[res_i] = name[name_i]; + res_i += 1; + name_i += 1; + } + + res[res_i] = '=' as u8; + res_i += 1; + + let mut items_i = 0; + while items_i < items.len() { + // We use LEN even though it creates an oversized array + let field_res = static_encoding_str_array::(items[items_i]); + + let mut item_res_i = 0; + while item_res_i < static_encoding_str_len(items[items_i]) { + res[res_i] = field_res[item_res_i]; + res_i += 1; + item_res_i += 1; + } + items_i += 1; + } + + match encoding { + Struct(_, _) => res[res_i] = '}' as u8, + Union(_, _) => res[res_i] = ')' as u8, + _ => {} + }; + } + }; + res +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! const_int_str { + ($n:expr) => {{ + const X: [u8; static_int_str_len($n as u128)] = static_int_str_array($n as u128); + unsafe { core::mem::transmute::<&[u8], &str>(&X) } + }}; + } + + #[test] + fn test_const_int_str() { + const STR_0: &'static str = const_int_str!(0); + const STR_4: &'static str = const_int_str!(4); + const STR_42: &'static str = const_int_str!(42); + const STR_100: &'static str = const_int_str!(100); + const STR_999: &'static str = const_int_str!(999); + const STR_1236018655: &'static str = const_int_str!(1236018655); + + assert_eq!(STR_0, "0"); + assert_eq!(STR_4, "4"); + assert_eq!(STR_42, "42"); + assert_eq!(STR_100, "100"); + assert_eq!(STR_999, "999"); + assert_eq!(STR_1236018655, "1236018655"); + } + + macro_rules! const_encoding { + ($e:expr) => {{ + const E: $crate::Encoding<'static> = $e; + const X: [u8; static_encoding_str_len(E)] = static_encoding_str_array(E); + unsafe { core::mem::transmute::<&'static [u8], &'static str>(&X) } + }}; + } + + #[test] + fn test_const_encoding() { + const CHAR: &'static str = const_encoding!(Encoding::Char); + assert_eq!(CHAR, "c"); + const BLOCK: &'static str = const_encoding!(Encoding::Block); + assert_eq!(BLOCK, "@?"); + const STRUCT: &'static str = + const_encoding!(Encoding::Struct("abc", &[Encoding::Int, Encoding::Double])); + assert_eq!(STRUCT, "{abc=id}"); + const VARIOUS: &'static str = const_encoding!(Encoding::Struct( + "abc", + &[ + Encoding::Pointer(&Encoding::Array(8, &Encoding::Bool)), + Encoding::Union("def", &[Encoding::Block]), + Encoding::Pointer(&Encoding::Pointer(&Encoding::BitField(255, &Encoding::Int))), + Encoding::Unknown, + ] + )); + assert_eq!(VARIOUS, "{abc=^[8B](def=@?)^^b255?}"); + } +}