use core::mem::MaybeUninit;
use core::ptr;
use crate::types::LUA_VECTOR_SIZE;
pub const LUAI_MAXNUM2STR: usize = 48;
pub const LUAI_MAXINT2STR: usize = 30;
const POW10_TABLE_MIN: i32 = -292;
const POW5_TABLE: [u64; 16] = [
0x8000000000000000,
0xa000000000000000,
0xc800000000000000,
0xfa00000000000000,
0x9c40000000000000,
0xc350000000000000,
0xf424000000000000,
0x9896800000000000,
0xbebc200000000000,
0xee6b280000000000,
0x9502f90000000000,
0xba43b74000000000,
0xe8d4a51000000000,
0x9184e72a00000000,
0xb5e620f480000000,
0xe35fa931a0000000,
];
const POW10_TABLE: [[u64; 3]; 39] = [
[0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b, 0x333443443333443b],
[0x8dd01fad907ffc3b, 0xae3da7d97f6792e4, 0xbbb3ab3cb3ba3cbc],
[0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa, 0x4ba4bc4bb4bb4bcc],
[0xaecc49914078536d, 0x58fae9f773886e19, 0x3ba3bc33b43b43bb],
[0xc21094364dfb5636, 0x985915fc12f542e5, 0x33b43b43a33b33cb],
[0xd77485cb25823ac7, 0x7d633293366b828c, 0x34b44c444343443c],
[0xef340a98172aace4, 0x86fb897116c87c35, 0x333343333343334b],
[0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074, 0xccaccbbcbcbb4bbc],
[0x936b9fcebb25c995, 0xcab10dd900beec35, 0x3ab3ab3ab3bb3bbb],
[0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb, 0x4cc3dc4db4db4dbb],
[0xb5b5ada8aaff80b8, 0x0d819992132456bb, 0x33b33a34c33b34ab],
[0xc9bcff6034c13052, 0xfc89b393dd02f0b6, 0x33c33b44b43c34bc],
[0xdff9772470297ebd, 0x59787e2b93bc56f8, 0x43b444444443434c],
[0xf8a95fcf88747d94, 0x75a44c6397ce912b, 0x443334343443343b],
[0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900, 0xbbabab3aa3ab4ccc],
[0x993fe2c6d07b7fab, 0xe546a8038efe402a, 0x4cb4bc4db4db4bcc],
[0xaa242499697392d2, 0xdde50bd1d5d0b9ea, 0x3ba3ba3bb33b33bc],
[0xbce5086492111aea, 0x88f4bb1ca6bcf585, 0x44b44c44c44c43cb],
[0xd1b71758e219652b, 0xd3c36113404ea4a9, 0x44c44c44c444443b],
[0xe8d4a51000000000, 0x0000000000000000, 0x444444444444444c],
[0x813f3978f8940984, 0x4000000000000000, 0xcccccccccccccccc],
[0x8f7e32ce7bea5c6f, 0xe4820023a2000000, 0xbba3bc4cc4cc4ccc],
[0x9f4f2726179a2245, 0x01d762422c946591, 0x4aa3bb3aa3ba3bab],
[0xb0de65388cc8ada8, 0x3b25a55f43294bcc, 0x3ca33b33b44b43bc],
[0xc45d1df942711d9a, 0x3ba5d0bd324f8395, 0x44c44c34c44b44cb],
[0xda01ee641a708de9, 0xe80e6f4820cc9496, 0x33b33b343333333c],
[0xf209787bb47d6b84, 0xc0678c5dbd23a49b, 0x443444444443443b],
[0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3, 0xdbccbcccb4cb3bbb],
[0x952ab45cfa97a0b2, 0xdd945a747bf26184, 0x3bc4bb4ab3ca3cbc],
[0xa59bc234db398c25, 0x43fab9837e699096, 0x3bb3ac3ab3bb33ac],
[0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30, 0x33b43b43b34c34dc],
[0xcc20ce9bd35c78a5, 0x31ec038df7b441f5, 0x34c44c43c44b44cb],
[0xe2a0b5dc971f303a, 0x2e44ae64840fd61e, 0x333333333333333c],
[0xfb9b7cd9a4a7443c, 0x169840ef017da3b2, 0x433344443333344c],
[0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f, 0xdcbdcc3cc4cc4bcb],
[0x9b10a4e5e9913128, 0xca7cf2b4191c8327, 0x3ab3cb3bc3bb4bbb],
[0xac2820d9623bf429, 0x546345fa9fbdcd45, 0x3bb3cc43c43c43cb],
[0xbf21e44003acdd2c, 0xe0470a63e6bd56c4, 0x44b34a43b44c44bc],
[0xd433179d9c8cb841, 0x5fa60692a46151ec, 0x43a33a33a333333c],
];
const DIGIT_TABLE: &[u8] = b"0001020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849\
5051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
struct Decimal {
s: u64,
k: i32,
}
struct NumericParse<'a> {
string: &'a [u8],
index: usize,
}
impl<'a> NumericParse<'a> {
fn new(string: &'a [u8]) -> Self {
let mut parse = Self { string, index: 0 };
parse.skip_ascii_whitespace();
parse
}
fn remainder(&self) -> &'a [u8] {
&self.string[self.index..]
}
fn finish(self) -> bool {
self.string[self.index..]
.iter()
.all(|byte| byte.is_ascii_whitespace())
}
fn peek(&self) -> Option<u8> {
self.string.get(self.index).copied()
}
fn skip_ascii_whitespace(&mut self) {
while matches!(self.peek(), Some(byte) if byte.is_ascii_whitespace()) {
self.index += 1;
}
}
fn take_sign(&mut self) -> bool {
match self.peek() {
Some(b'+') => {
self.index += 1;
false
}
Some(b'-') => {
self.index += 1;
true
}
_ => false,
}
}
fn consume_hex_prefix(&mut self) {
if matches!(
self.string.get(self.index..self.index + 2),
Some([b'0', b'x' | b'X'])
) {
self.index += 2;
}
}
fn scan_unsigned_radix(&mut self, base: u32) -> Option<&'a [u8]> {
let start = self.index;
self.take_sign();
if base == 16 {
self.consume_hex_prefix();
}
let digit_start = self.index;
while let Some(byte) = self.peek() {
let Some(digit) = digit_value(byte) else {
break;
};
if digit >= base {
break;
}
self.index += 1;
}
(self.index != digit_start).then_some(&self.string[start..self.index])
}
fn scan_signed_decimal(&mut self) -> Option<&'a [u8]> {
let start = self.index;
self.take_sign();
let digit_start = self.index;
while let Some(byte) = self.peek() {
let Some(digit) = digit_value(byte) else {
break;
};
if digit >= 10 {
break;
}
self.index += 1;
}
(self.index != digit_start).then_some(&self.string[start..self.index])
}
}
fn digit_value(byte: u8) -> Option<u32> {
match byte {
b'0'..=b'9' => Some(u32::from(byte - b'0')),
b'a'..=b'z' => Some(u32::from(byte - b'a') + 10),
b'A'..=b'Z' => Some(u32::from(byte - b'A') + 10),
_ => None,
}
}
fn parse_unsigned_slice(slice: &[u8], base: u32) -> u64 {
let mut digits = slice;
let negative = matches!(digits.first(), Some(b'-'));
if matches!(digits.first(), Some(b'+' | b'-')) {
digits = &digits[1..];
}
if base == 16 && matches!(digits.get(..2), Some([b'0', b'x' | b'X'])) {
digits = &digits[2..];
}
let mut value = 0u64;
for byte in digits {
let Some(digit) = digit_value(*byte) else {
value = u64::MAX;
break;
};
debug_assert!(digit < base);
if digit >= base {
value = u64::MAX;
break;
}
match value
.checked_mul(u64::from(base))
.and_then(|value| value.checked_add(u64::from(digit)))
{
Some(next) => value = next,
None => {
value = u64::MAX;
break;
}
}
}
if negative {
0u64.wrapping_sub(value)
} else {
value
}
}
fn parse_signed_decimal_slice(slice: &[u8]) -> i64 {
let mut digits = slice;
let negative = matches!(digits.first(), Some(b'-'));
if matches!(digits.first(), Some(b'+' | b'-')) {
digits = &digits[1..];
}
let limit = if negative {
i64::MAX as u64 + 1
} else {
i64::MAX as u64
};
let mut value = 0u64;
for byte in digits {
let Some(digit) = byte.checked_sub(b'0').filter(|digit| *digit <= 9) else {
return if negative { i64::MIN } else { i64::MAX };
};
let digit = u64::from(digit);
debug_assert!(*byte >= b'0' && *byte <= b'9');
match value
.checked_mul(10)
.and_then(|value| value.checked_add(digit))
{
Some(next) if next <= limit => value = next,
_ => {
return if negative { i64::MIN } else { i64::MAX };
}
}
}
if negative {
if value == limit {
i64::MIN
} else {
-(value as i64)
}
} else {
value as i64
}
}
fn mul128(x: u64, y: u64) -> (u64, u64) {
let result = (x as u128) * (y as u128);
(result as u64, (result >> 64) as u64)
}
fn mul192hi(x_hi: u64, x_lo: u64, y: u64) -> (u64, u64) {
let (z1, mut z2) = mul128(x_hi, y);
let (_, z1c) = mul128(x_lo, y);
let sum = z1.wrapping_add(z1c);
if sum < z1c {
z2 = z2.wrapping_add(1);
}
(sum, z2)
}
fn round_odd(g_hi: u64, g_lo: u64, cp: u64) -> u64 {
let (_, x_hi) = mul128(g_lo, cp);
let (y_lo, y_hi) = mul128(g_hi, cp);
let z = y_lo.wrapping_add(x_hi);
(y_hi + u64::from(z < x_hi)) | u64::from(z > 1)
}
fn schubfach(exponent: i32, fraction: u64) -> Decimal {
let mut c = fraction;
let mut q = exponent - 1023 - 51;
if exponent != 0 {
c |= 1u64 << 52;
q -= 1;
}
if (-q as u32) < 53 && (c & ((1u64 << (-q)) - 1)) == 0 {
return Decimal { s: c >> (-q), k: 0 };
}
let irr = i32::from(c == (1u64 << 52) && q != -1074);
let out = i32::from(c as u8 & 1);
let cbl = 4 * c - 2 + irr as u64;
let cb = 4 * c;
let cbr = 4 * c + 2;
const Q: i32 = 20;
const C: i32 = 315652;
const A: i32 = -131008;
const C2: i32 = 3483294;
let k = (q * C + if irr != 0 { A } else { 0 }) >> Q;
let h = q + ((-k * C2) >> Q) + 1;
let gtoff = -k - POW10_TABLE_MIN;
let gt = POW10_TABLE[(gtoff >> 4) as usize];
let (mut g_lo, mut g_hi) = mul192hi(gt[0], gt[1], POW5_TABLE[(gtoff & 15) as usize]);
let gterr = ((gt[2] >> ((gtoff & 15) * 4)) & 15) as i32;
let gtscale = gterr >> 3;
g_hi <<= gtscale;
g_hi += (g_lo >> 63) & gtscale as u64;
g_lo <<= gtscale;
g_lo = g_lo.wrapping_sub(((gterr & 7) - 4) as u64);
let vbl = round_odd(g_hi, g_lo, cbl << h);
let vb = round_odd(g_hi, g_lo, cb << h);
let vbr = round_odd(g_hi, g_lo, cbr << h);
let s = vb / 4;
if s >= 10 {
let sp = s / 10;
let upin = vbl + out as u64 <= 40 * sp;
let wpin = vbr >= 40 * sp + 40 + out as u64;
if upin != wpin {
return Decimal {
s: sp + u64::from(wpin),
k: k + 1,
};
}
}
let uin = vbl + out as u64 <= 4 * s;
let win = 4 * s + 4 + out as u64 <= vbr;
let rup = vb >= 4 * s + 2 + 1 - (s & 1);
Decimal {
s: s + u64::from(if uin != win { win } else { rup }),
k,
}
}
fn print_special(buffer: &mut [u8], sign: bool, fraction: u64) -> usize {
if fraction == 0 {
let bytes = if sign {
b"-inf".as_slice()
} else {
b"inf".as_slice()
};
buffer[..bytes.len()].copy_from_slice(bytes);
bytes.len()
} else {
buffer[..3].copy_from_slice(b"nan");
3
}
}
unsafe fn print_unsigned_rev(mut end: *mut u8, mut num: u64) -> *mut u8 {
unsafe {
let digits = DIGIT_TABLE.as_ptr();
while num >= 10000 {
let tail = (num % 10000) as usize;
let hi = (tail / 100) * 2;
let lo = (tail % 100) * 2;
end = end.sub(4);
ptr::copy_nonoverlapping(digits.add(hi), end, 2);
ptr::copy_nonoverlapping(digits.add(lo), end.add(2), 2);
num /= 10000;
}
let mut rest = num as usize;
while rest >= 10 {
let pair = (rest % 100) * 2;
end = end.sub(2);
ptr::copy_nonoverlapping(digits.add(pair), end, 2);
rest /= 100;
}
if rest != 0 {
end = end.sub(1);
end.write(b'0' + rest as u8);
}
end
}
}
unsafe fn print_exp(buffer: *mut u8, num: i32) -> *mut u8 {
unsafe {
buffer.write(b'e');
buffer.add(1).write(if num < 0 { b'-' } else { b'+' });
let mut value = if num < 0 { -num } else { num } as usize;
let mut cursor = buffer.add(2);
if value >= 100 {
cursor.write(b'0' + (value / 100) as u8);
cursor = cursor.add(1);
value %= 100;
}
let pair = value * 2;
ptr::copy_nonoverlapping(DIGIT_TABLE.as_ptr().add(pair), cursor, 2);
cursor.add(2)
}
}
unsafe fn trim_zero(mut end: *mut u8) -> *mut u8 {
unsafe {
while end.sub(1).read() == b'0' {
end = end.sub(1);
}
end
}
}
unsafe fn fast_copy<const SIZE: usize>(dst: *mut u8, src: *const u8, len: usize) {
debug_assert!(len <= SIZE);
unsafe {
ptr::copy_nonoverlapping(src, dst, SIZE);
}
}
unsafe fn fast_fill<const SIZE: usize>(dst: *mut u8, byte: u8, len: usize) {
debug_assert!(len <= SIZE);
unsafe {
ptr::write_bytes(dst, byte, SIZE);
}
}
pub fn num_add(left: f64, right: f64) -> f64 {
left + right
}
pub fn num_sub(left: f64, right: f64) -> f64 {
left - right
}
pub fn num_mul(left: f64, right: f64) -> f64 {
left * right
}
pub fn num_div(left: f64, right: f64) -> f64 {
left / right
}
pub fn num_pow(left: f64, right: f64) -> f64 {
left.powf(right)
}
pub fn num_unm(value: f64) -> f64 {
-value
}
pub fn num_eq(left: f64, right: f64) -> bool {
left == right
}
pub fn num_lt(left: f64, right: f64) -> bool {
left < right
}
pub fn num_le(left: f64, right: f64) -> bool {
left <= right
}
pub fn int_eq(left: i64, right: i64) -> bool {
left == right
}
pub fn vec_eq(left: &[f32; LUA_VECTOR_SIZE], right: &[f32; LUA_VECTOR_SIZE]) -> bool {
left == right
}
pub fn vec_is_nan(vector: &[f32; LUA_VECTOR_SIZE]) -> bool {
vector.iter().any(|component| component.is_nan())
}
pub fn num_mod(left: f64, right: f64) -> f64 {
left - (left / right).floor() * right
}
pub fn num_idiv(left: f64, right: f64) -> f64 {
(left / right).floor()
}
pub fn sign_f(value: f32) -> f32 {
if value > 0.0 {
1.0
} else if value < 0.0 {
-1.0
} else {
0.0
}
}
pub fn clamp_f(value: f32, min: f32, max: f32) -> f32 {
let value = if value < min { min } else { value };
if value > max { max } else { value }
}
pub fn lerp_f(left: f32, right: f32, t: f32) -> f32 {
if t == 1.0 {
right
} else {
left + (right - left) * t
}
}
pub fn num_to_str(buffer: &mut [u8; LUAI_MAXNUM2STR], number: f64) -> usize {
let bits = number.to_bits();
let sign = (bits >> 63) != 0;
let exponent = ((bits >> 52) & 2047) as i32;
let fraction = bits & ((1u64 << 52) - 1);
if exponent == 0x7ff {
return print_special(buffer, sign, fraction);
}
unsafe {
let out = buffer.as_mut_ptr();
let mut write = out;
if sign {
write.write(b'-');
write = write.add(1);
}
if exponent == 0 && fraction == 0 {
write.write(b'0');
return write.add(1).offset_from(out) as usize;
}
let decimal = schubfach(exponent, fraction);
debug_assert!(decimal.s < 100_000_000_000_000_000);
let mut dec_buffer = MaybeUninit::<[u8; 40]>::uninit();
let dec_base = dec_buffer.as_mut_ptr().cast::<u8>();
let dec_end = dec_base.add(20);
ptr::write_bytes(dec_end, 0, 16);
let dec = print_unsigned_rev(dec_end, decimal.s);
let dec_len = dec_end.offset_from(dec) as i32;
debug_assert!(dec_len <= 17);
let dot = dec_len + decimal.k;
let dec_len = dec_len as usize;
let end = if (-5..=21).contains(&dot) {
if dot <= 0 {
write.write(b'0');
write.add(1).write(b'.');
let zero_count = (-dot) as usize;
fast_fill::<5>(write.add(2), b'0', zero_count);
let tail = write.add(2 + zero_count);
fast_copy::<17>(tail, dec, dec_len);
trim_zero(tail.add(dec_len))
} else if dot as usize == dec_len {
fast_copy::<17>(write, dec, dec_len);
write.add(dec_len)
} else if (dot as usize) < dec_len {
let dot = dot as usize;
fast_copy::<16>(write, dec, dot);
write.add(dot).write(b'.');
fast_copy::<16>(write.add(dot + 1), dec.add(dot), dec_len - dot);
trim_zero(write.add(dec_len + 1))
} else {
let dot = dot as usize;
fast_copy::<17>(write, dec, dec_len);
fast_fill::<8>(write.add(dec_len), b'0', dot - dec_len);
write.add(dot)
}
} else {
write.write(dec.read());
write.add(1).write(b'.');
fast_copy::<16>(write.add(2), dec.add(1), dec_len - 1);
let mut exp = trim_zero(write.add(dec_len + 1));
if exp.sub(1).read() == b'.' {
exp = exp.sub(1);
}
print_exp(exp, dot - 1)
};
end.offset_from(out) as usize
}
}
pub fn int_to_str(buffer: &mut [u8; LUAI_MAXINT2STR], value: i64) -> usize {
let mut magnitude = if value < 0 {
(!(value as u64)).wrapping_add(1)
} else {
value as u64
};
let mut digit_count = 1usize;
let mut cap = 10u64;
while digit_count < 19 && cap <= magnitude {
digit_count += 1;
cap *= 10;
}
let mut index = if value < 0 {
digit_count
} else {
digit_count - 1
};
buffer[index + 1] = 0;
loop {
buffer[index] = b'0' + (magnitude % 10) as u8;
magnitude /= 10;
if magnitude == 0 {
break;
}
index -= 1;
}
if value < 0 {
debug_assert!(index > 0);
buffer[index - 1] = b'-';
digit_count + 1
} else {
digit_count
}
}
pub fn str_to_num(bytes: &[u8]) -> Option<f64> {
let mut parse = NumericParse::new(bytes);
let (mut result, end) = lexical::parse_partial::<f64, _>(parse.remainder()).ok()?;
parse.index += end;
if matches!(parse.peek(), Some(b'x' | b'X')) {
parse = NumericParse::new(bytes);
let digits = parse.scan_unsigned_radix(16)?;
result = parse_unsigned_slice(digits, 16) as f64;
}
parse.finish().then_some(result)
}
pub fn str_to_long(bytes: &[u8], base: u32) -> Option<i64> {
let mut parse = NumericParse::new(bytes);
let result = if base == 10 {
let decimal = parse.scan_signed_decimal()?;
let mut result = parse_signed_decimal_slice(decimal);
if matches!(parse.peek(), Some(b'x' | b'X')) {
parse = NumericParse::new(bytes);
let digits = parse.scan_unsigned_radix(16)?;
result = parse_unsigned_slice(digits, 16) as i64;
}
result
} else {
let digits = parse.scan_unsigned_radix(base)?;
parse_unsigned_slice(digits, base) as i64
};
parse.finish().then_some(result)
}
pub fn num_to_int(value: f64) -> i32 {
value as i32
}
pub fn num_to_unsigned(value: f64) -> u32 {
(value as i64) as u32
}