1use crate::VmResult;
2use crate::native::{NativeCallContext, NativeCallResult, NativeFunction};
3use crate::thread::Thread;
4
5const ALL_ONES: u32 = !0;
6const N_BITS: i32 = u32::BITS as i32;
7
8static BIT_LIB: [NativeFunction; 15] = [
9 NativeFunction {
10 name: "arshift",
11 function: b_arshift,
12 },
13 NativeFunction {
14 name: "band",
15 function: b_and,
16 },
17 NativeFunction {
18 name: "bnot",
19 function: b_not,
20 },
21 NativeFunction {
22 name: "bor",
23 function: b_or,
24 },
25 NativeFunction {
26 name: "bxor",
27 function: b_xor,
28 },
29 NativeFunction {
30 name: "btest",
31 function: b_test,
32 },
33 NativeFunction {
34 name: "extract",
35 function: b_extract,
36 },
37 NativeFunction {
38 name: "lrotate",
39 function: b_lrot,
40 },
41 NativeFunction {
42 name: "lshift",
43 function: b_lshift,
44 },
45 NativeFunction {
46 name: "replace",
47 function: b_replace,
48 },
49 NativeFunction {
50 name: "rrotate",
51 function: b_rrot,
52 },
53 NativeFunction {
54 name: "rshift",
55 function: b_rshift,
56 },
57 NativeFunction {
58 name: "countlz",
59 function: b_countlz,
60 },
61 NativeFunction {
62 name: "countrz",
63 function: b_countrz,
64 },
65 NativeFunction {
66 name: "byteswap",
67 function: b_swap,
68 },
69];
70
71fn trim(value: u32) -> u32 {
73 value & ALL_ONES
74}
75
76fn mask(width: i32) -> u32 {
78 !((ALL_ONES << 1) << (width - 1))
79}
80
81fn and_aux(ctx: &NativeCallContext) -> VmResult<u32> {
83 let mut result = !0u32;
84 for argument in ctx.args() {
85 result &= argument.unsigned()?;
86 }
87 Ok(trim(result))
88}
89
90fn b_and(ctx: NativeCallContext) -> NativeCallResult {
92 ctx.push_unsigned(and_aux(&ctx)?)?;
93 Ok(1)
94}
95
96fn b_test(ctx: NativeCallContext) -> NativeCallResult {
98 ctx.push_boolean(and_aux(&ctx)? != 0)?;
99 Ok(1)
100}
101
102fn b_or(ctx: NativeCallContext) -> NativeCallResult {
104 let mut result = 0u32;
105 for argument in ctx.args() {
106 result |= argument.unsigned()?;
107 }
108 ctx.push_unsigned(trim(result))?;
109 Ok(1)
110}
111
112fn b_xor(ctx: NativeCallContext) -> NativeCallResult {
114 let mut result = 0u32;
115 for argument in ctx.args() {
116 result ^= argument.unsigned()?;
117 }
118 ctx.push_unsigned(trim(result))?;
119 Ok(1)
120}
121
122fn b_not(ctx: NativeCallContext) -> NativeCallResult {
124 ctx.push_unsigned(trim(!ctx.arg(1).unsigned()?))?;
125 Ok(1)
126}
127
128fn b_shift(mut value: u32, mut shift: i32) -> u32 {
130 if shift < 0 {
131 shift = -shift;
132 value = trim(value);
133 value = if shift >= N_BITS { 0 } else { value >> shift };
134 } else {
135 value = if shift >= N_BITS { 0 } else { value << shift };
136 value = trim(value);
137 }
138
139 value
140}
141
142fn b_lshift(ctx: NativeCallContext) -> NativeCallResult {
144 ctx.push_unsigned(b_shift(ctx.arg(1).unsigned()?, ctx.arg(2).integer()?))?;
145 Ok(1)
146}
147
148fn b_rshift(ctx: NativeCallContext) -> NativeCallResult {
150 ctx.push_unsigned(b_shift(ctx.arg(1).unsigned()?, -ctx.arg(2).integer()?))?;
151 Ok(1)
152}
153
154fn b_arshift(ctx: NativeCallContext) -> NativeCallResult {
156 let value = ctx.arg(1).unsigned()?;
157 let shift = ctx.arg(2).integer()?;
158
159 if shift < 0 || (value & (1u32 << (N_BITS - 1))) == 0 {
160 ctx.push_unsigned(b_shift(value, -shift))?;
161 return Ok(1);
162 }
163
164 let result = if shift >= N_BITS {
165 ALL_ONES
166 } else {
167 trim((value >> shift) | !(u32::MAX >> shift))
168 };
169 ctx.push_unsigned(result)?;
170 Ok(1)
171}
172
173fn b_rot(mut value: u32, shift: i32) -> u32 {
175 let shift = shift & (N_BITS - 1);
176 value = trim(value);
177 if shift != 0 {
178 value = (value << shift) | (value >> (N_BITS - shift));
179 }
180 trim(value)
181}
182
183fn b_lrot(ctx: NativeCallContext) -> NativeCallResult {
185 ctx.push_unsigned(b_rot(ctx.arg(1).unsigned()?, ctx.arg(2).integer()?))?;
186 Ok(1)
187}
188
189fn b_rrot(ctx: NativeCallContext) -> NativeCallResult {
191 ctx.push_unsigned(b_rot(ctx.arg(1).unsigned()?, -ctx.arg(2).integer()?))?;
192 Ok(1)
193}
194
195fn field_args(ctx: &NativeCallContext, field_arg: i32, width_arg: i32) -> VmResult<(i32, i32)> {
197 let field = ctx.arg(field_arg).integer()?;
198 let width = ctx.arg(width_arg).integer_or(1)?;
199
200 if field < 0 {
201 return ctx
202 .arg(field_arg)
203 .error("field cannot be negative")
204 .map_err(Into::into);
205 }
206 if width <= 0 {
207 return ctx
208 .arg(width_arg)
209 .error("width must be positive")
210 .map_err(Into::into);
211 }
212 if field + width > N_BITS {
213 return ctx
214 .error("trying to access non-existent bits", [])
215 .map_err(Into::into);
216 }
217
218 Ok((field, width))
219}
220
221fn b_extract(ctx: NativeCallContext) -> NativeCallResult {
223 let value = ctx.arg(1).unsigned()?;
224 let (field, width) = field_args(&ctx, 2, 3)?;
225 ctx.push_unsigned((value >> field) & mask(width))?;
226 Ok(1)
227}
228
229fn b_replace(ctx: NativeCallContext) -> NativeCallResult {
231 let value = ctx.arg(1).unsigned()?;
232 let mut replacement = ctx.arg(2).unsigned()?;
233 let (field, width) = field_args(&ctx, 3, 4)?;
234 let mask = mask(width);
235 replacement &= mask;
236 ctx.push_unsigned((value & !(mask << field)) | (replacement << field))?;
237 Ok(1)
238}
239
240fn b_countlz(ctx: NativeCallContext) -> NativeCallResult {
242 let value = ctx.arg(1).unsigned()?;
243 let mut result = N_BITS as u32;
244 for index in 0..N_BITS {
245 if (value & (1u32 << (N_BITS - 1 - index))) != 0 {
246 result = index as u32;
247 break;
248 }
249 }
250 ctx.push_unsigned(result)?;
251 Ok(1)
252}
253
254fn b_countrz(ctx: NativeCallContext) -> NativeCallResult {
256 let value = ctx.arg(1).unsigned()?;
257 let mut result = N_BITS as u32;
258 for index in 0..N_BITS {
259 if (value & (1u32 << index)) != 0 {
260 result = index as u32;
261 break;
262 }
263 }
264 ctx.push_unsigned(result)?;
265 Ok(1)
266}
267
268fn b_swap(ctx: NativeCallContext) -> NativeCallResult {
270 ctx.push_unsigned(ctx.arg(1).unsigned()?.swap_bytes())?;
271 Ok(1)
272}
273
274impl Thread {
275 pub unsafe fn open_bit32(&self) -> NativeCallResult {
277 unsafe { self.register(Some(super::LUA_BITLIB_NAME), &BIT_LIB[..])? };
278 Ok(1)
279 }
280}