Skip to content

Commit 10b7550

Browse files
committed
Add const functions to generate a &'static str from Encoding
1 parent a52b847 commit 10b7550

File tree

3 files changed

+268
-0
lines changed

3 files changed

+268
-0
lines changed

objc2-encode/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ extern crate alloc;
2121
mod encode;
2222
mod encoding;
2323
mod parse;
24+
#[allow(dead_code)]
25+
mod static_encoding_str;
26+
mod static_int_str;
2427

2528
pub use self::encode::{Encode, EncodeArguments, RefEncode};
2629
pub use self::encoding::Encoding;
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
use super::static_int_str;
2+
use super::Encoding;
3+
4+
impl<'a> Encoding<'a> {
5+
const fn end_str_len(self) -> usize {
6+
use Encoding::*;
7+
8+
match self {
9+
Char | Short | Int | Long | LongLong | UChar | UShort | UInt | ULong | ULongLong
10+
| Float | Double | Bool | Void | String | Object | Class | Sel | Unknown => 1,
11+
Block => 2,
12+
BitField(b) => 1 + static_int_str::end_str_len(b as u128),
13+
Pointer(t) => 1 + t.end_str_len(),
14+
Array(len, item) => {
15+
1 + static_int_str::end_str_len(len as u128) + item.end_str_len() + 1
16+
}
17+
Struct(name, items) | Union(name, items) => {
18+
let mut res = 1 + name.len() + 1;
19+
let mut i = 0;
20+
while i < items.len() {
21+
res += items[i].end_str_len();
22+
i += 1;
23+
}
24+
res + 1
25+
}
26+
}
27+
}
28+
29+
const fn get_str_array<const LEN: usize>(self) -> [u8; LEN] {
30+
use Encoding::*;
31+
32+
let mut res: [u8; LEN] = [0; LEN];
33+
34+
match self {
35+
Char => res[0] = 'c' as u8,
36+
Short => res[0] = 's' as u8,
37+
Int => res[0] = 'i' as u8,
38+
Long => res[0] = 'l' as u8,
39+
LongLong => res[0] = 'q' as u8,
40+
UChar => res[0] = 'C' as u8,
41+
UShort => res[0] = 'S' as u8,
42+
UInt => res[0] = 'I' as u8,
43+
ULong => res[0] = 'L' as u8,
44+
ULongLong => res[0] = 'Q' as u8,
45+
Float => res[0] = 'f' as u8,
46+
Double => res[0] = 'd' as u8,
47+
Bool => res[0] = 'B' as u8,
48+
Void => res[0] = 'v' as u8,
49+
Block => {
50+
res[0] = '@' as u8;
51+
res[1] = '?' as u8;
52+
}
53+
String => res[0] = '*' as u8,
54+
Object => res[0] = '@' as u8,
55+
Class => res[0] = '#' as u8,
56+
Sel => res[0] = ':' as u8,
57+
Unknown => res[0] = '?' as u8,
58+
BitField(b) => {
59+
let mut res_i = 0;
60+
61+
res[res_i] = 'b' as u8;
62+
res_i += 1;
63+
64+
let mut i = 0;
65+
// We use 3 even though it creates an oversized array
66+
let arr = static_int_str::get_str_array::<3>(b as u128);
67+
while i < static_int_str::end_str_len(b as u128) {
68+
res[res_i] = arr[i];
69+
res_i += 1;
70+
i += 1;
71+
}
72+
}
73+
Pointer(t) => {
74+
let mut res_i = 0;
75+
76+
res[res_i] = '^' as u8;
77+
res_i += 1;
78+
79+
let mut i = 0;
80+
// We use LEN even though it creates an oversized array
81+
let arr = t.get_str_array::<LEN>();
82+
while i < t.end_str_len() {
83+
res[res_i] = arr[i];
84+
res_i += 1;
85+
i += 1;
86+
}
87+
}
88+
Array(len, item) => {
89+
let mut res_i = 0;
90+
91+
res[res_i] = '[' as u8;
92+
res_i += 1;
93+
94+
let mut i = 0;
95+
// We use 20 even though it creates an oversized array
96+
let arr = static_int_str::get_str_array::<20>(len as u128);
97+
while i < static_int_str::end_str_len(len as u128) {
98+
res[res_i] = arr[i];
99+
res_i += 1;
100+
i += 1;
101+
}
102+
103+
let mut i = 0;
104+
// We use LEN even though it creates an oversized array
105+
let arr = item.get_str_array::<LEN>();
106+
while i < item.end_str_len() {
107+
res[res_i] = arr[i];
108+
res_i += 1;
109+
i += 1;
110+
}
111+
112+
res[res_i] = ']' as u8;
113+
}
114+
Struct(name, items) | Union(name, items) => {
115+
let mut res_i = 0;
116+
117+
match self {
118+
Struct(_, _) => res[res_i] = '{' as u8,
119+
Union(_, _) => res[res_i] = '(' as u8,
120+
_ => {}
121+
};
122+
res_i += 1;
123+
124+
let mut name_i = 0;
125+
let name = name.as_bytes();
126+
while name_i < name.len() {
127+
res[res_i] = name[name_i];
128+
res_i += 1;
129+
name_i += 1;
130+
}
131+
132+
res[res_i] = '=' as u8;
133+
res_i += 1;
134+
135+
let mut items_i = 0;
136+
while items_i < items.len() {
137+
// We use LEN even though it creates an oversized array
138+
let field_res = items[items_i].get_str_array::<LEN>();
139+
140+
let mut item_res_i = 0;
141+
while item_res_i < items[items_i].end_str_len() {
142+
res[res_i] = field_res[item_res_i];
143+
res_i += 1;
144+
item_res_i += 1;
145+
}
146+
items_i += 1;
147+
}
148+
149+
match self {
150+
Struct(_, _) => res[res_i] = '}' as u8,
151+
Union(_, _) => res[res_i] = ')' as u8,
152+
_ => {}
153+
};
154+
}
155+
};
156+
res
157+
}
158+
159+
const fn end_cstr_len(self) -> usize {
160+
self.end_str_len() + 1
161+
}
162+
163+
const fn get_cstr_array<const RES: usize>(self) -> [u8; RES] {
164+
// Contains nul byte at the end
165+
self.get_str_array()
166+
}
167+
}
168+
169+
#[cfg(test)]
170+
mod tests {
171+
use super::Encoding;
172+
173+
macro_rules! const_encoding {
174+
($e:expr) => {{
175+
const E: $crate::Encoding<'static> = $e;
176+
const X: [u8; E.end_str_len()] = E.get_str_array();
177+
unsafe { core::mem::transmute::<&'static [u8], &'static str>(&X) }
178+
}};
179+
}
180+
181+
#[test]
182+
fn test_const_encoding() {
183+
const CHAR: &'static str = const_encoding!(Encoding::Char);
184+
assert_eq!(CHAR, "c");
185+
const BLOCK: &'static str = const_encoding!(Encoding::Block);
186+
assert_eq!(BLOCK, "@?");
187+
const STRUCT: &'static str =
188+
const_encoding!(Encoding::Struct("abc", &[Encoding::Int, Encoding::Double]));
189+
assert_eq!(STRUCT, "{abc=id}");
190+
const VARIOUS: &'static str = const_encoding!(Encoding::Struct(
191+
"abc",
192+
&[
193+
Encoding::Pointer(&Encoding::Array(8, &Encoding::Bool)),
194+
Encoding::Union("def", &[Encoding::Block]),
195+
Encoding::Pointer(&Encoding::Pointer(&Encoding::BitField(255))),
196+
Encoding::Unknown,
197+
]
198+
));
199+
assert_eq!(VARIOUS, "{abc=^[8B](def=@?)^^b255?}");
200+
}
201+
}

objc2-encode/src/static_int_str.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
pub(crate) const fn end_str_len(mut n: u128) -> usize {
2+
let mut i = 0;
3+
if n == 0 {
4+
return 1;
5+
}
6+
while n > 0 {
7+
n = n / 10;
8+
i += 1;
9+
}
10+
i
11+
}
12+
13+
pub(crate) const fn get_str_array<const RES: usize>(mut n: u128) -> [u8; RES] {
14+
let mut res: [u8; RES] = [0; RES];
15+
let mut i = 0;
16+
if n == 0 {
17+
res[0] = '0' as u8;
18+
return res;
19+
}
20+
while n > 0 {
21+
res[i] = '0' as u8 + (n % 10) as u8;
22+
n = n / 10;
23+
i += 1;
24+
}
25+
26+
let mut rev: [u8; RES] = [0; RES];
27+
let mut rev_i = 0;
28+
while 0 < i {
29+
i -= 1;
30+
rev[rev_i] = res[i];
31+
n = n / 10;
32+
rev_i += 1;
33+
}
34+
rev
35+
}
36+
37+
#[cfg(test)]
38+
mod tests {
39+
use super::*;
40+
41+
macro_rules! const_int_str {
42+
($n:expr) => {{
43+
const X: [u8; end_str_len($n as u128)] = get_str_array($n as u128);
44+
unsafe { core::mem::transmute::<&[u8], &str>(&X) }
45+
}};
46+
}
47+
48+
const STR_0: &'static str = const_int_str!(0);
49+
const STR_4: &'static str = const_int_str!(4);
50+
const STR_42: &'static str = const_int_str!(42);
51+
const STR_100: &'static str = const_int_str!(100);
52+
const STR_999: &'static str = const_int_str!(999);
53+
const STR_1236018655: &'static str = const_int_str!(1236018655);
54+
55+
#[test]
56+
fn test() {
57+
assert_eq!(STR_0, "0");
58+
assert_eq!(STR_4, "4");
59+
assert_eq!(STR_42, "42");
60+
assert_eq!(STR_100, "100");
61+
assert_eq!(STR_999, "999");
62+
assert_eq!(STR_1236018655, "1236018655");
63+
}
64+
}

0 commit comments

Comments
 (0)