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
167fn 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
185fn 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
207fn int64_tonumber(ctx: NativeCallContext) -> NativeCallResult {
209 ctx.push_number(ctx.arg(1).integer64()? as f64)?;
210 Ok(1)
211}
212
213fn 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
219fn 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
227fn 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
235fn 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
243fn 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
260fn 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
282fn 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
296fn 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
318fn 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
332fn 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
346fn 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
359fn 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
372fn 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
382fn 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
392fn int64_bnot(ctx: NativeCallContext) -> NativeCallResult {
394 ctx.push_integer64((!(ctx.arg(1).integer64()? as u64)) as i64)?;
395 Ok(1)
396}
397
398fn 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
408fn int64_lt(ctx: NativeCallContext) -> NativeCallResult {
410 ctx.push_boolean(ctx.arg(1).integer64()? < ctx.arg(2).integer64()?)?;
411 Ok(1)
412}
413
414fn int64_le(ctx: NativeCallContext) -> NativeCallResult {
416 ctx.push_boolean(ctx.arg(1).integer64()? <= ctx.arg(2).integer64()?)?;
417 Ok(1)
418}
419
420fn 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
426fn 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
432fn int64_gt(ctx: NativeCallContext) -> NativeCallResult {
434 ctx.push_boolean(ctx.arg(1).integer64()? > ctx.arg(2).integer64()?)?;
435 Ok(1)
436}
437
438fn int64_ge(ctx: NativeCallContext) -> NativeCallResult {
440 ctx.push_boolean(ctx.arg(1).integer64()? >= ctx.arg(2).integer64()?)?;
441 Ok(1)
442}
443
444fn 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
450fn 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
456fn 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
473fn 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
490fn 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
511fn 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
520fn 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
554fn 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
562fn 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
574fn 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
596fn 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
606fn 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
617fn 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
628fn 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 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}