Skip to main content

luau_vm/libs/
bit32.rs

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
71/// `trim`
72fn trim(value: u32) -> u32 {
73    value & ALL_ONES
74}
75
76/// `mask`
77fn mask(width: i32) -> u32 {
78    !((ALL_ONES << 1) << (width - 1))
79}
80
81/// `andaux`
82fn 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
90/// `b_and`
91fn b_and(ctx: NativeCallContext) -> NativeCallResult {
92    ctx.push_unsigned(and_aux(&ctx)?)?;
93    Ok(1)
94}
95
96/// `b_test`
97fn b_test(ctx: NativeCallContext) -> NativeCallResult {
98    ctx.push_boolean(and_aux(&ctx)? != 0)?;
99    Ok(1)
100}
101
102/// `b_or`
103fn 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
112/// `b_xor`
113fn 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
122/// `b_not`
123fn b_not(ctx: NativeCallContext) -> NativeCallResult {
124    ctx.push_unsigned(trim(!ctx.arg(1).unsigned()?))?;
125    Ok(1)
126}
127
128/// `b_shift`
129fn 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
142/// `b_lshift`
143fn b_lshift(ctx: NativeCallContext) -> NativeCallResult {
144    ctx.push_unsigned(b_shift(ctx.arg(1).unsigned()?, ctx.arg(2).integer()?))?;
145    Ok(1)
146}
147
148/// `b_rshift`
149fn b_rshift(ctx: NativeCallContext) -> NativeCallResult {
150    ctx.push_unsigned(b_shift(ctx.arg(1).unsigned()?, -ctx.arg(2).integer()?))?;
151    Ok(1)
152}
153
154/// `b_arshift`
155fn 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
173/// `b_rot`
174fn 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
183/// `b_lrot`
184fn b_lrot(ctx: NativeCallContext) -> NativeCallResult {
185    ctx.push_unsigned(b_rot(ctx.arg(1).unsigned()?, ctx.arg(2).integer()?))?;
186    Ok(1)
187}
188
189/// `b_rrot`
190fn b_rrot(ctx: NativeCallContext) -> NativeCallResult {
191    ctx.push_unsigned(b_rot(ctx.arg(1).unsigned()?, -ctx.arg(2).integer()?))?;
192    Ok(1)
193}
194
195/// `fieldargs`
196fn 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
221/// `b_extract`
222fn 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
229/// `b_replace`
230fn 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
240/// `b_countlz`
241fn 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
254/// `b_countrz`
255fn 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
268/// `b_swap`
269fn b_swap(ctx: NativeCallContext) -> NativeCallResult {
270    ctx.push_unsigned(ctx.arg(1).unsigned()?.swap_bytes())?;
271    Ok(1)
272}
273
274impl Thread {
275    /// `luaopen_bit32`
276    pub unsafe fn open_bit32(&self) -> NativeCallResult {
277        unsafe { self.register(Some(super::LUA_BITLIB_NAME), &BIT_LIB[..])? };
278        Ok(1)
279    }
280}