Skip to main content

luau_vm/libs/
integer.rs

1use crate::VmResult;
2use crate::native::{NativeCallContext, NativeCallResult, NativeFunction};
3use crate::number::str_to_long;
4use crate::thread::Thread;
5
6static INTEGER_LIB: [NativeFunction; 39] = [
7    NativeFunction {
8        name: "create",
9        function: int64_create,
10    },
11    NativeFunction {
12        name: "tonumber",
13        function: int64_tonumber,
14    },
15    NativeFunction {
16        name: "neg",
17        function: int64_neg,
18    },
19    NativeFunction {
20        name: "add",
21        function: int64_add,
22    },
23    NativeFunction {
24        name: "sub",
25        function: int64_sub,
26    },
27    NativeFunction {
28        name: "mul",
29        function: int64_mul,
30    },
31    NativeFunction {
32        name: "div",
33        function: int64_div,
34    },
35    NativeFunction {
36        name: "min",
37        function: int64_min,
38    },
39    NativeFunction {
40        name: "max",
41        function: int64_max,
42    },
43    NativeFunction {
44        name: "rem",
45        function: int64_rem,
46    },
47    NativeFunction {
48        name: "idiv",
49        function: int64_idiv,
50    },
51    NativeFunction {
52        name: "udiv",
53        function: int64_udiv,
54    },
55    NativeFunction {
56        name: "urem",
57        function: int64_urem,
58    },
59    NativeFunction {
60        name: "mod",
61        function: int64_mod,
62    },
63    NativeFunction {
64        name: "clamp",
65        function: int64_clamp,
66    },
67    NativeFunction {
68        name: "band",
69        function: int64_band,
70    },
71    NativeFunction {
72        name: "bor",
73        function: int64_bor,
74    },
75    NativeFunction {
76        name: "bnot",
77        function: int64_bnot,
78    },
79    NativeFunction {
80        name: "bxor",
81        function: int64_bxor,
82    },
83    NativeFunction {
84        name: "lt",
85        function: int64_lt,
86    },
87    NativeFunction {
88        name: "le",
89        function: int64_le,
90    },
91    NativeFunction {
92        name: "ult",
93        function: int64_ult,
94    },
95    NativeFunction {
96        name: "ule",
97        function: int64_ule,
98    },
99    NativeFunction {
100        name: "gt",
101        function: int64_gt,
102    },
103    NativeFunction {
104        name: "ge",
105        function: int64_ge,
106    },
107    NativeFunction {
108        name: "ugt",
109        function: int64_ugt,
110    },
111    NativeFunction {
112        name: "uge",
113        function: int64_uge,
114    },
115    NativeFunction {
116        name: "lshift",
117        function: int64_lshift,
118    },
119    NativeFunction {
120        name: "rshift",
121        function: int64_rshift,
122    },
123    NativeFunction {
124        name: "arshift",
125        function: int64_arshift,
126    },
127    NativeFunction {
128        name: "lrotate",
129        function: int64_lrotate,
130    },
131    NativeFunction {
132        name: "rrotate",
133        function: int64_rrotate,
134    },
135    NativeFunction {
136        name: "extract",
137        function: int64_extract,
138    },
139    NativeFunction {
140        name: "replace",
141        function: int64_replace,
142    },
143    NativeFunction {
144        name: "btest",
145        function: int64_btest,
146    },
147    NativeFunction {
148        name: "countrz",
149        function: int64_countrz,
150    },
151    NativeFunction {
152        name: "countlz",
153        function: int64_countlz,
154    },
155    NativeFunction {
156        name: "bswap",
157        function: int64_bswap,
158    },
159    NativeFunction {
160        name: "fromstring",
161        function: int64_from_string,
162    },
163];
164
165const MASK64: u64 = u64::MAX;
166
167/// `int64_create`
168fn int64_create(ctx: NativeCallContext) -> NativeCallResult {
169    let thread = ctx.raw_thread();
170    unsafe {
171        let value = thread.check_number(1)?;
172        if (-9223372036854775808.0..9223372036854775808.0).contains(&value) {
173            let integer = value as i64;
174            if (integer as f64) == value {
175                thread.push_integer64(integer)?;
176                return Ok(1);
177            }
178        }
179
180        thread.push_nil()?;
181        Ok(1)
182    }
183}
184
185/// `int64_fromstring`
186fn int64_from_string(ctx: NativeCallContext) -> NativeCallResult {
187    let thread = ctx.raw_thread();
188    unsafe {
189        let string = thread.check_string(1)?;
190        let base = thread.opt_integer(2, 10)?;
191        if !(2..=36).contains(&base) {
192            return thread
193                .lua_arg_error(2, "base out of range")
194                .map_err(Into::into);
195        }
196
197        if let Some(result) = str_to_long(string, base as u32) {
198            thread.push_integer64(result)?;
199        } else {
200            thread.push_nil()?;
201        }
202    }
203
204    Ok(1)
205}
206
207/// `int64_tonumber`
208fn int64_tonumber(ctx: NativeCallContext) -> NativeCallResult {
209    ctx.push_number(ctx.arg(1).integer64()? as f64)?;
210    Ok(1)
211}
212
213/// `int64_neg`
214fn int64_neg(ctx: NativeCallContext) -> NativeCallResult {
215    ctx.push_integer64((!(ctx.arg(1).integer64()? as u64)).wrapping_add(1) as i64)?;
216    Ok(1)
217}
218
219/// `int64_add`
220fn int64_add(ctx: NativeCallContext) -> NativeCallResult {
221    let x = ctx.arg(1).integer64()? as u64;
222    let y = ctx.arg(2).integer64()? as u64;
223    ctx.push_integer64(x.wrapping_add(y) as i64)?;
224    Ok(1)
225}
226
227/// `int64_sub`
228fn int64_sub(ctx: NativeCallContext) -> NativeCallResult {
229    let x = ctx.arg(1).integer64()? as u64;
230    let y = ctx.arg(2).integer64()? as u64;
231    ctx.push_integer64(x.wrapping_sub(y) as i64)?;
232    Ok(1)
233}
234
235/// `int64_mul`
236fn int64_mul(ctx: NativeCallContext) -> NativeCallResult {
237    let x = ctx.arg(1).integer64()? as u64;
238    let y = ctx.arg(2).integer64()? as u64;
239    ctx.push_integer64(x.wrapping_mul(y) as i64)?;
240    Ok(1)
241}
242
243/// `int64_div`
244fn int64_div(ctx: NativeCallContext) -> NativeCallResult {
245    let thread = ctx.raw_thread();
246    let a = ctx.arg(1).integer64()?;
247    let b = ctx.arg(2).integer64()?;
248
249    if b == 0 {
250        return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
251    }
252    if a == i64::MIN && b == -1 {
253        return unsafe { crate::error!(thread, "integer overflow") }.map_err(Into::into);
254    }
255
256    ctx.push_integer64(a / b)?;
257    Ok(1)
258}
259
260/// `int64_idiv`
261fn int64_idiv(ctx: NativeCallContext) -> NativeCallResult {
262    let thread = ctx.raw_thread();
263    let a = ctx.arg(1).integer64()?;
264    let b = ctx.arg(2).integer64()?;
265
266    if b == 0 {
267        return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
268    }
269    if a == i64::MIN && b == -1 {
270        return unsafe { crate::error!(thread, "integer overflow") }.map_err(Into::into);
271    }
272
273    let result = a / b;
274    ctx.push_integer64(if result < 0 && a % b != 0 {
275        result - 1
276    } else {
277        result
278    })?;
279    Ok(1)
280}
281
282/// `int64_rem`
283fn int64_rem(ctx: NativeCallContext) -> NativeCallResult {
284    let thread = ctx.raw_thread();
285    let a = ctx.arg(1).integer64()?;
286    let b = ctx.arg(2).integer64()?;
287
288    if b == 0 {
289        return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
290    }
291
292    ctx.push_integer64(if a == i64::MIN && b == -1 { 0 } else { a % b })?;
293    Ok(1)
294}
295
296/// `int64_mod`
297fn int64_mod(ctx: NativeCallContext) -> NativeCallResult {
298    let thread = ctx.raw_thread();
299    let a = ctx.arg(1).integer64()?;
300    let b = ctx.arg(2).integer64()?;
301
302    if b == 0 {
303        return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
304    }
305
306    let mut remainder = 0i64;
307    if a != i64::MIN || b != -1 {
308        remainder = a % b;
309        if remainder != 0 && ((a < 0) != (b < 0)) {
310            remainder += b;
311        }
312    }
313
314    ctx.push_integer64(remainder)?;
315    Ok(1)
316}
317
318/// `int64_udiv`
319fn int64_udiv(ctx: NativeCallContext) -> NativeCallResult {
320    let thread = ctx.raw_thread();
321    let a = ctx.arg(1).integer64()? as u64;
322    let b = ctx.arg(2).integer64()? as u64;
323
324    if b == 0 {
325        return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
326    }
327
328    ctx.push_integer64((a / b) as i64)?;
329    Ok(1)
330}
331
332/// `int64_urem`
333fn int64_urem(ctx: NativeCallContext) -> NativeCallResult {
334    let thread = ctx.raw_thread();
335    let a = ctx.arg(1).integer64()? as u64;
336    let b = ctx.arg(2).integer64()? as u64;
337
338    if b == 0 {
339        return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
340    }
341
342    ctx.push_integer64((a % b) as i64)?;
343    Ok(1)
344}
345
346/// `int64_min`
347fn int64_min(ctx: NativeCallContext) -> NativeCallResult {
348    let mut result = ctx.arg(1).integer64()?;
349    for argument in ctx.args().skip(1) {
350        let value = argument.integer64()?;
351        if value < result {
352            result = value;
353        }
354    }
355    ctx.push_integer64(result)?;
356    Ok(1)
357}
358
359/// `int64_max`
360fn int64_max(ctx: NativeCallContext) -> NativeCallResult {
361    let mut result = ctx.arg(1).integer64()?;
362    for argument in ctx.args().skip(1) {
363        let value = argument.integer64()?;
364        if value > result {
365            result = value;
366        }
367    }
368    ctx.push_integer64(result)?;
369    Ok(1)
370}
371
372/// `int64_band`
373fn int64_band(ctx: NativeCallContext) -> NativeCallResult {
374    let mut result = u64::MAX;
375    for argument in ctx.args() {
376        result &= argument.integer64()? as u64;
377    }
378    ctx.push_integer64(result as i64)?;
379    Ok(1)
380}
381
382/// `int64_bor`
383fn int64_bor(ctx: NativeCallContext) -> NativeCallResult {
384    let mut result = 0u64;
385    for argument in ctx.args() {
386        result |= argument.integer64()? as u64;
387    }
388    ctx.push_integer64(result as i64)?;
389    Ok(1)
390}
391
392/// `int64_bnot`
393fn int64_bnot(ctx: NativeCallContext) -> NativeCallResult {
394    ctx.push_integer64((!(ctx.arg(1).integer64()? as u64)) as i64)?;
395    Ok(1)
396}
397
398/// `int64_bxor`
399fn int64_bxor(ctx: NativeCallContext) -> NativeCallResult {
400    let mut result = 0u64;
401    for argument in ctx.args() {
402        result ^= argument.integer64()? as u64;
403    }
404    ctx.push_integer64(result as i64)?;
405    Ok(1)
406}
407
408/// `int64_lt`
409fn int64_lt(ctx: NativeCallContext) -> NativeCallResult {
410    ctx.push_boolean(ctx.arg(1).integer64()? < ctx.arg(2).integer64()?)?;
411    Ok(1)
412}
413
414/// `int64_le`
415fn int64_le(ctx: NativeCallContext) -> NativeCallResult {
416    ctx.push_boolean(ctx.arg(1).integer64()? <= ctx.arg(2).integer64()?)?;
417    Ok(1)
418}
419
420/// `int64_ult`
421fn int64_ult(ctx: NativeCallContext) -> NativeCallResult {
422    ctx.push_boolean((ctx.arg(1).integer64()? as u64) < (ctx.arg(2).integer64()? as u64))?;
423    Ok(1)
424}
425
426/// `int64_ule`
427fn int64_ule(ctx: NativeCallContext) -> NativeCallResult {
428    ctx.push_boolean((ctx.arg(1).integer64()? as u64) <= (ctx.arg(2).integer64()? as u64))?;
429    Ok(1)
430}
431
432/// `int64_gt`
433fn int64_gt(ctx: NativeCallContext) -> NativeCallResult {
434    ctx.push_boolean(ctx.arg(1).integer64()? > ctx.arg(2).integer64()?)?;
435    Ok(1)
436}
437
438/// `int64_ge`
439fn int64_ge(ctx: NativeCallContext) -> NativeCallResult {
440    ctx.push_boolean(ctx.arg(1).integer64()? >= ctx.arg(2).integer64()?)?;
441    Ok(1)
442}
443
444/// `int64_ugt`
445fn int64_ugt(ctx: NativeCallContext) -> NativeCallResult {
446    ctx.push_boolean((ctx.arg(1).integer64()? as u64) > (ctx.arg(2).integer64()? as u64))?;
447    Ok(1)
448}
449
450/// `int64_uge`
451fn int64_uge(ctx: NativeCallContext) -> NativeCallResult {
452    ctx.push_boolean((ctx.arg(1).integer64()? as u64) >= (ctx.arg(2).integer64()? as u64))?;
453    Ok(1)
454}
455
456/// `int64_lshift`
457fn int64_lshift(ctx: NativeCallContext) -> NativeCallResult {
458    let n = ctx.arg(1).integer64()? as u64;
459    let shift = ctx.arg(2).integer64()?;
460    let result = if (-63..=63).contains(&shift) {
461        if shift < 0 {
462            n >> ((-shift) as u32)
463        } else {
464            n << (shift as u32)
465        }
466    } else {
467        0
468    };
469    ctx.push_integer64(result as i64)?;
470    Ok(1)
471}
472
473/// `int64_rshift`
474fn int64_rshift(ctx: NativeCallContext) -> NativeCallResult {
475    let n = ctx.arg(1).integer64()? as u64;
476    let shift = ctx.arg(2).integer64()?;
477    let result = if (-63..=63).contains(&shift) {
478        if shift < 0 {
479            n << ((-shift) as u32)
480        } else {
481            n >> (shift as u32)
482        }
483    } else {
484        0
485    };
486    ctx.push_integer64(result as i64)?;
487    Ok(1)
488}
489
490/// `int64_arshift`
491fn int64_arshift(ctx: NativeCallContext) -> NativeCallResult {
492    let n = ctx.arg(1).integer64()?;
493    let shift = ctx.arg(2).integer64()?;
494    let result = if (-63..=63).contains(&shift) {
495        if shift < 0 {
496            ((n as u64) << ((-shift) as u32)) as i64
497        } else {
498            n >> (shift as u32)
499        }
500    } else if shift < -63 {
501        0
502    } else if n < 0 {
503        -1
504    } else {
505        0
506    };
507    ctx.push_integer64(result)?;
508    Ok(1)
509}
510
511/// `int64_lrotate`
512fn int64_lrotate(ctx: NativeCallContext) -> NativeCallResult {
513    let n = ctx.arg(1).integer64()? as u64;
514    let shift = (ctx.arg(2).integer64()? as u64 % 64) as u32;
515    let result = if shift != 0 { n.rotate_left(shift) } else { n };
516    ctx.push_integer64(result as i64)?;
517    Ok(1)
518}
519
520/// `int64_rrotate`
521fn int64_rrotate(ctx: NativeCallContext) -> NativeCallResult {
522    let n = ctx.arg(1).integer64()? as u64;
523    let shift = (ctx.arg(2).integer64()? as u64 % 64) as u32;
524    let result = if shift != 0 { n.rotate_right(shift) } else { n };
525    ctx.push_integer64(result as i64)?;
526    Ok(1)
527}
528
529fn field_args(ctx: &NativeCallContext, field_arg: i32, width_arg: i32) -> VmResult<(i64, i64)> {
530    let field = ctx.arg(field_arg).integer64()?;
531    let width = ctx.arg(width_arg).integer64_or(1)?;
532
533    if !(0..=63).contains(&field) {
534        return ctx
535            .arg(field_arg)
536            .error("field cannot be negative")
537            .map_err(Into::into);
538    }
539    if width <= 0 {
540        return ctx
541            .arg(width_arg)
542            .error("width must be positive")
543            .map_err(Into::into);
544    }
545    if field + width > 64 {
546        return ctx
547            .error("trying to access non-existent bits", [])
548            .map_err(Into::into);
549    }
550
551    Ok((field, width))
552}
553
554/// `int64_extract`
555fn int64_extract(ctx: NativeCallContext) -> NativeCallResult {
556    let n = ctx.arg(1).integer64()? as u64;
557    let (field, width) = field_args(&ctx, 2, 3)?;
558    ctx.push_integer64(((n >> field) & (MASK64 >> (64 - width))) as i64)?;
559    Ok(1)
560}
561
562/// `int64_replace`
563fn int64_replace(ctx: NativeCallContext) -> NativeCallResult {
564    let n = ctx.arg(1).integer64()? as u64;
565    let replacement = ctx.arg(2).integer64()? as u64;
566    let (field, width) = field_args(&ctx, 3, 4)?;
567    let base_mask = MASK64 >> (64 - width);
568    let replacement_bits = (replacement & base_mask) << field;
569    let mask = MASK64 ^ (base_mask << field);
570    ctx.push_integer64(((n & mask) | replacement_bits) as i64)?;
571    Ok(1)
572}
573
574/// `int64_clamp`
575fn int64_clamp(ctx: NativeCallContext) -> NativeCallResult {
576    let value = ctx.arg(1).integer64()?;
577    let min = ctx.arg(2).integer64()?;
578    let max = ctx.arg(3).integer64()?;
579    if min > max {
580        return ctx
581            .arg(3)
582            .error("max must be greater than or equal to min")
583            .map_err(Into::into);
584    }
585
586    ctx.push_integer64(if value < min {
587        min
588    } else if value > max {
589        max
590    } else {
591        value
592    })?;
593    Ok(1)
594}
595
596/// `int64_btest`
597fn int64_btest(ctx: NativeCallContext) -> NativeCallResult {
598    let mut result = u64::MAX;
599    for argument in ctx.args() {
600        result &= argument.integer64()? as u64;
601    }
602    ctx.push_boolean(result != 0)?;
603    Ok(1)
604}
605
606/// `int64_countrz`
607fn int64_countrz(ctx: NativeCallContext) -> NativeCallResult {
608    let value = ctx.arg(1).integer64()? as u64;
609    ctx.push_integer64(if value == 0 {
610        64
611    } else {
612        value.trailing_zeros() as i64
613    })?;
614    Ok(1)
615}
616
617/// `int64_countlz`
618fn int64_countlz(ctx: NativeCallContext) -> NativeCallResult {
619    let value = ctx.arg(1).integer64()? as u64;
620    ctx.push_integer64(if value == 0 {
621        64
622    } else {
623        value.leading_zeros() as i64
624    })?;
625    Ok(1)
626}
627
628/// `int64_bswap`
629fn int64_bswap(ctx: NativeCallContext) -> NativeCallResult {
630    ctx.push_integer64((ctx.arg(1).integer64()? as u64).swap_bytes() as i64)?;
631    Ok(1)
632}
633
634impl Thread {
635    /// `luaopen_integer`
636    pub unsafe fn open_integer(&self) -> NativeCallResult {
637        unsafe { self.register(Some(super::LUA_INTLIB_NAME), &INTEGER_LIB[..])? };
638        unsafe {
639            self.push_integer64(i64::MAX)?;
640            self.raw_set_field(-2, "maxsigned")?;
641            self.push_integer64(i64::MIN)?;
642            self.raw_set_field(-2, "minsigned")?;
643        }
644        Ok(1)
645    }
646}