Skip to content

Commit 3d1a729

Browse files
committed
Add const functions to generate a &'static str from Encoding
Will be used at some point when generic constants are available, see #70
1 parent 02cf1d2 commit 3d1a729

File tree

2 files changed

+267
-0
lines changed

2 files changed

+267
-0
lines changed

objc2-encode/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,9 @@ mod encode;
107107
mod encoding;
108108
mod parse;
109109

110+
// Will be used at some point when generic constants are available
111+
#[allow(dead_code)]
112+
mod static_str;
113+
110114
pub use self::encode::{Encode, EncodeArguments, RefEncode};
111115
pub use self::encoding::Encoding;

objc2-encode/src/static_str.rs

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
use super::Encoding;
2+
3+
pub(crate) const fn static_int_str_len(mut n: u128) -> usize {
4+
let mut i = 0;
5+
if n == 0 {
6+
return 1;
7+
}
8+
while n > 0 {
9+
n = n / 10;
10+
i += 1;
11+
}
12+
i
13+
}
14+
15+
pub(crate) const fn static_int_str_array<const RES: usize>(mut n: u128) -> [u8; RES] {
16+
let mut res: [u8; RES] = [0; RES];
17+
let mut i = 0;
18+
if n == 0 {
19+
res[0] = '0' as u8;
20+
return res;
21+
}
22+
while n > 0 {
23+
res[i] = '0' as u8 + (n % 10) as u8;
24+
n = n / 10;
25+
i += 1;
26+
}
27+
28+
let mut rev: [u8; RES] = [0; RES];
29+
let mut rev_i = 0;
30+
while 0 < i {
31+
i -= 1;
32+
rev[rev_i] = res[i];
33+
n = n / 10;
34+
rev_i += 1;
35+
}
36+
rev
37+
}
38+
39+
pub(crate) const fn static_encoding_str_len(encoding: Encoding<'_>) -> usize {
40+
use Encoding::*;
41+
42+
match encoding {
43+
Char | Short | Int | Long | LongLong | UChar | UShort | UInt | ULong | ULongLong
44+
| Float | Double | LongDouble | Bool | Void | String | Object | Class | Sel
45+
| Unknown => 1,
46+
Block | FloatComplex | DoubleComplex | LongDoubleComplex => 2,
47+
BitField(b, _type) => 1 + static_int_str_len(b as u128),
48+
Pointer(&t) => 1 + static_encoding_str_len(t),
49+
Array(len, &item) => {
50+
1 + static_int_str_len(len as u128) + static_encoding_str_len(item) + 1
51+
}
52+
Struct(name, items) | Union(name, items) => {
53+
let mut res = 1 + name.len() + 1;
54+
let mut i = 0;
55+
while i < items.len() {
56+
res += static_encoding_str_len(items[i]);
57+
i += 1;
58+
}
59+
res + 1
60+
}
61+
}
62+
}
63+
64+
pub(crate) const fn static_encoding_str_array<const LEN: usize>(encoding: Encoding<'_>) -> [u8; LEN] {
65+
use Encoding::*;
66+
67+
let mut res: [u8; LEN] = [0; LEN];
68+
69+
match encoding {
70+
Char => res[0] = 'c' as u8,
71+
Short => res[0] = 's' as u8,
72+
Int => res[0] = 'i' as u8,
73+
Long => res[0] = 'l' as u8,
74+
LongLong => res[0] = 'q' as u8,
75+
UChar => res[0] = 'C' as u8,
76+
UShort => res[0] = 'S' as u8,
77+
UInt => res[0] = 'I' as u8,
78+
ULong => res[0] = 'L' as u8,
79+
ULongLong => res[0] = 'Q' as u8,
80+
Float => res[0] = 'f' as u8,
81+
Double => res[0] = 'd' as u8,
82+
LongDouble => res[0] = 'D' as u8,
83+
FloatComplex => {
84+
res[0] = 'j' as u8;
85+
res[1] = 'f' as u8;
86+
}
87+
DoubleComplex => {
88+
res[0] = 'j' as u8;
89+
res[1] = 'd' as u8;
90+
}
91+
LongDoubleComplex => {
92+
res[0] = 'j' as u8;
93+
res[1] = 'D' as u8;
94+
}
95+
Bool => res[0] = 'B' as u8,
96+
Void => res[0] = 'v' as u8,
97+
Block => {
98+
res[0] = '@' as u8;
99+
res[1] = '?' as u8;
100+
}
101+
String => res[0] = '*' as u8,
102+
Object => res[0] = '@' as u8,
103+
Class => res[0] = '#' as u8,
104+
Sel => res[0] = ':' as u8,
105+
Unknown => res[0] = '?' as u8,
106+
BitField(b, &_type) => {
107+
let mut res_i = 0;
108+
109+
res[res_i] = 'b' as u8;
110+
res_i += 1;
111+
112+
let mut i = 0;
113+
// We use 3 even though it creates an oversized array
114+
let arr = static_int_str_array::<3>(b as u128);
115+
while i < static_int_str_len(b as u128) {
116+
res[res_i] = arr[i];
117+
res_i += 1;
118+
i += 1;
119+
}
120+
}
121+
Pointer(&t) => {
122+
let mut res_i = 0;
123+
124+
res[res_i] = '^' as u8;
125+
res_i += 1;
126+
127+
let mut i = 0;
128+
// We use LEN even though it creates an oversized array
129+
let arr = static_encoding_str_array::<LEN>(t);
130+
while i < static_encoding_str_len(t) {
131+
res[res_i] = arr[i];
132+
res_i += 1;
133+
i += 1;
134+
}
135+
}
136+
Array(len, &item) => {
137+
let mut res_i = 0;
138+
139+
res[res_i] = '[' as u8;
140+
res_i += 1;
141+
142+
let mut i = 0;
143+
// We use 20 even though it creates an oversized array
144+
let arr = static_int_str_array::<20>(len as u128);
145+
while i < static_int_str_len(len as u128) {
146+
res[res_i] = arr[i];
147+
res_i += 1;
148+
i += 1;
149+
}
150+
151+
let mut i = 0;
152+
// We use LEN even though it creates an oversized array
153+
let arr = static_encoding_str_array::<LEN>(item);
154+
while i < static_encoding_str_len(item) {
155+
res[res_i] = arr[i];
156+
res_i += 1;
157+
i += 1;
158+
}
159+
160+
res[res_i] = ']' as u8;
161+
}
162+
Struct(name, items) | Union(name, items) => {
163+
let mut res_i = 0;
164+
165+
match encoding {
166+
Struct(_, _) => res[res_i] = '{' as u8,
167+
Union(_, _) => res[res_i] = '(' as u8,
168+
_ => {}
169+
};
170+
res_i += 1;
171+
172+
let mut name_i = 0;
173+
let name = name.as_bytes();
174+
while name_i < name.len() {
175+
res[res_i] = name[name_i];
176+
res_i += 1;
177+
name_i += 1;
178+
}
179+
180+
res[res_i] = '=' as u8;
181+
res_i += 1;
182+
183+
let mut items_i = 0;
184+
while items_i < items.len() {
185+
// We use LEN even though it creates an oversized array
186+
let field_res = static_encoding_str_array::<LEN>(items[items_i]);
187+
188+
let mut item_res_i = 0;
189+
while item_res_i < static_encoding_str_len(items[items_i]) {
190+
res[res_i] = field_res[item_res_i];
191+
res_i += 1;
192+
item_res_i += 1;
193+
}
194+
items_i += 1;
195+
}
196+
197+
match encoding {
198+
Struct(_, _) => res[res_i] = '}' as u8,
199+
Union(_, _) => res[res_i] = ')' as u8,
200+
_ => {}
201+
};
202+
}
203+
};
204+
res
205+
}
206+
207+
#[cfg(test)]
208+
mod tests {
209+
use super::*;
210+
211+
macro_rules! const_int_str {
212+
($n:expr) => {{
213+
const X: [u8; static_int_str_len($n as u128)] = static_int_str_array($n as u128);
214+
unsafe { core::mem::transmute::<&[u8], &str>(&X) }
215+
}};
216+
}
217+
218+
#[test]
219+
fn test_const_int_str() {
220+
const STR_0: &'static str = const_int_str!(0);
221+
const STR_4: &'static str = const_int_str!(4);
222+
const STR_42: &'static str = const_int_str!(42);
223+
const STR_100: &'static str = const_int_str!(100);
224+
const STR_999: &'static str = const_int_str!(999);
225+
const STR_1236018655: &'static str = const_int_str!(1236018655);
226+
227+
assert_eq!(STR_0, "0");
228+
assert_eq!(STR_4, "4");
229+
assert_eq!(STR_42, "42");
230+
assert_eq!(STR_100, "100");
231+
assert_eq!(STR_999, "999");
232+
assert_eq!(STR_1236018655, "1236018655");
233+
}
234+
235+
macro_rules! const_encoding {
236+
($e:expr) => {{
237+
const E: $crate::Encoding<'static> = $e;
238+
const X: [u8; static_encoding_str_len(E)] = static_encoding_str_array(E);
239+
unsafe { core::mem::transmute::<&'static [u8], &'static str>(&X) }
240+
}};
241+
}
242+
243+
#[test]
244+
fn test_const_encoding() {
245+
const CHAR: &'static str = const_encoding!(Encoding::Char);
246+
assert_eq!(CHAR, "c");
247+
const BLOCK: &'static str = const_encoding!(Encoding::Block);
248+
assert_eq!(BLOCK, "@?");
249+
const STRUCT: &'static str =
250+
const_encoding!(Encoding::Struct("abc", &[Encoding::Int, Encoding::Double]));
251+
assert_eq!(STRUCT, "{abc=id}");
252+
const VARIOUS: &'static str = const_encoding!(Encoding::Struct(
253+
"abc",
254+
&[
255+
Encoding::Pointer(&Encoding::Array(8, &Encoding::Bool)),
256+
Encoding::Union("def", &[Encoding::Block]),
257+
Encoding::Pointer(&Encoding::Pointer(&Encoding::BitField(255, &Encoding::Int))),
258+
Encoding::Unknown,
259+
]
260+
));
261+
assert_eq!(VARIOUS, "{abc=^[8B](def=@?)^^b255?}");
262+
}
263+
}

0 commit comments

Comments
 (0)