Skip to content

Commit ceb5ee3

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 ceb5ee3

File tree

2 files changed

+268
-0
lines changed

2 files changed

+268
-0
lines changed

objc2-encode/src/lib.rs

+4
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

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

0 commit comments

Comments
 (0)