mirror of
https://github.com/janet-lang/janet
synced 2024-11-24 17:27:18 +00:00
use custom string to bigint reader in place of strtol
for better compatibility with default janet number reader
This commit is contained in:
parent
1db6d0e0bc
commit
a07d76b264
@ -83,29 +83,6 @@ static const JanetAbstractType bi_uint64_type = {
|
||||
uint64_unmarshal
|
||||
};
|
||||
|
||||
static int parse_int64(const char *str, bi_int64 *box) {
|
||||
char *endptr;
|
||||
int base = ((str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) ||
|
||||
(str[0] == '-' && str[1] == '0' && (str[2] == 'x' || str[2] == 'X'))) ? 16 : 10;
|
||||
errno = 0;
|
||||
*box = (bi_int64)strtoll(str, &endptr, base);
|
||||
if ((errno == ERANGE && (*box == LLONG_MAX || *box == LLONG_MIN)) ||
|
||||
(errno != 0 && *box == 0) ||
|
||||
(endptr == str)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_uint64(const char *str, bi_uint64 *box) {
|
||||
char *endptr;
|
||||
int base = (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) ? 16 : 10;
|
||||
errno = 0;
|
||||
*box = (bi_int64)strtoull(str, &endptr, base);
|
||||
if ((errno == ERANGE && (*box == ULLONG_MAX)) ||
|
||||
(errno != 0 && *box == 0) ||
|
||||
(endptr == str)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static bi_int64 check_bi_int64(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
@ -117,7 +94,8 @@ static bi_int64 check_bi_int64(Janet x) {
|
||||
}
|
||||
case JANET_STRING: {
|
||||
bi_int64 value;
|
||||
if (parse_int64((const char *)janet_unwrap_string(x), &value))
|
||||
const uint8_t *str = janet_unwrap_string(x);
|
||||
if (janet_scan_int64(str, janet_string_length(str), &value))
|
||||
return value;
|
||||
break;
|
||||
}
|
||||
@ -142,7 +120,8 @@ static bi_uint64 check_bi_uint64(Janet x) {
|
||||
}
|
||||
case JANET_STRING: {
|
||||
bi_uint64 value;
|
||||
if (parse_uint64((const char *)janet_unwrap_string(x), &value))
|
||||
const uint8_t *str = janet_unwrap_string(x);
|
||||
if (janet_scan_uint64(str, janet_string_length(str), &value))
|
||||
return value;
|
||||
break;
|
||||
}
|
||||
|
@ -361,3 +361,101 @@ error:
|
||||
free(mant.digits);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef JANET_BIGINT
|
||||
|
||||
static int scan_bigint(
|
||||
const uint8_t *str,
|
||||
int32_t len,
|
||||
uint64_t *out,
|
||||
int *neg
|
||||
) {
|
||||
const uint8_t *end = str + len;
|
||||
int seenadigit = 0;
|
||||
int base = 10;
|
||||
*neg = 0;
|
||||
*out = 0;
|
||||
uint64_t accum = 0;
|
||||
/* len max is INT64_MAX in base 2 with _ between each bits */
|
||||
/* '2r' + 64 bits + 63 _ + sign = 130 => 150 for some leading */
|
||||
/* zeros */
|
||||
if (len > 150) return 0;
|
||||
/* Get sign */
|
||||
if (str >= end) return 0;
|
||||
if (*str == '-') {
|
||||
*neg = 1;
|
||||
str++;
|
||||
} else if (*str == '+') {
|
||||
str++;
|
||||
}
|
||||
/* Check for leading 0x or digit digit r */
|
||||
if (str + 1 < end && str[0] == '0' && str[1] == 'x') {
|
||||
base = 16;
|
||||
str += 2;
|
||||
} else if (str + 1 < end &&
|
||||
str[0] >= '0' && str[0] <= '9' &&
|
||||
str[1] == 'r') {
|
||||
base = str[0] - '0';
|
||||
str += 2;
|
||||
} else if (str + 2 < end &&
|
||||
str[0] >= '0' && str[0] <= '9' &&
|
||||
str[1] >= '0' && str[1] <= '9' &&
|
||||
str[2] == 'r') {
|
||||
base = 10 * (str[0] - '0') + (str[1] - '0');
|
||||
if (base < 2 || base > 36) return 0;
|
||||
str += 3;
|
||||
}
|
||||
|
||||
/* Skip leading zeros */
|
||||
while (str < end && *str == '0') {
|
||||
seenadigit = 1;
|
||||
str++;
|
||||
}
|
||||
/* Parse significant digits */
|
||||
while (str < end) {
|
||||
if (*str == '_') {
|
||||
if (!seenadigit) return 0;
|
||||
} else {
|
||||
int digit = digit_lookup[*str & 0x7F];
|
||||
if (*str > 127 || digit >= base) return 0;
|
||||
if (accum > (UINT64_MAX - digit) / base) return 0;
|
||||
accum = accum * base + digit;
|
||||
seenadigit = 1;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
if (!seenadigit) return 0;
|
||||
*out = accum;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int janet_scan_int64(const uint8_t *str, int32_t len, int64_t *out) {
|
||||
int neg;
|
||||
uint64_t bi;
|
||||
if (scan_bigint(str, len, &bi, &neg)) {
|
||||
if (neg && bi <= 0x8000000000000000UL) {
|
||||
*out = -bi;
|
||||
return 1;
|
||||
}
|
||||
if (!neg && bi <= 0x7FFFFFFFFFFFFFFFUL) {
|
||||
*out = bi;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out) {
|
||||
int neg;
|
||||
uint64_t bi;
|
||||
if (scan_bigint(str, len, &bi, &neg)) {
|
||||
if (!neg) {
|
||||
*out = bi;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1338,6 +1338,8 @@ JANET_API Janet janet_bigint_int64(int64_t x);
|
||||
JANET_API Janet janet_bigint_uint64(uint64_t x);
|
||||
JANET_API int64_t janet_checkbigint_int64(Janet x);
|
||||
JANET_API uint64_t janet_checkbigint_uint64(Janet x);
|
||||
JANET_API int janet_scan_int64(const uint8_t *str, int32_t len, int64_t *out);
|
||||
JANET_API int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out);
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
(import test/helper :prefix "" :exit true)
|
||||
(start-suite 6)
|
||||
|
||||
# some tests for bigint
|
||||
# some tests for bigint
|
||||
|
||||
(def i64 bigint/int64)
|
||||
(def u64 bigint/uint64)
|
||||
@ -34,8 +34,9 @@
|
||||
# max double we can convert to int (2^53)
|
||||
(def b (u64 0x1fffffffffffff))
|
||||
(def b (u64 (math/pow 2 53)))
|
||||
# from string
|
||||
(def c (u64 "0xffffffffffffffff"))
|
||||
# from string
|
||||
(def c (u64 "0xffff_ffff_ffff_ffff"))
|
||||
(def c (u64 "32rvv_vv_vv_vv"))
|
||||
(def d (u64 "123456789"))))
|
||||
|
||||
(assert-no-error
|
||||
@ -47,7 +48,7 @@
|
||||
(def b (i64 0x1fffffffffffff))
|
||||
(def b (i64 (math/pow 2 53)))
|
||||
# from string
|
||||
(def c (i64 "0x7fffffffffffffff"))
|
||||
(def c (i64 "0x7fff_ffff_ffff_ffff"))
|
||||
(def d (i64 "123456789"))))
|
||||
|
||||
(assert-error
|
||||
@ -58,15 +59,15 @@
|
||||
(def b (u64 (+ (math/pow 2 53) 1)))
|
||||
# out of range 65 bits
|
||||
(def c (u64 "0x1ffffffffffffffff"))
|
||||
# just to big
|
||||
# just to big
|
||||
(def d (u64 "123456789123456789123456789"))))
|
||||
|
||||
(assert (:== (:/ (u64 "0xffffffffffffffff") 8 2) "0xfffffffffffffff") "bigint operations")
|
||||
(assert (:== (:/ (u64 "0xffff_ffff_ffff_ffff") 8 2) "0xfffffffffffffff") "bigint operations")
|
||||
(assert (let [a (u64 0xff)] (:== (:+ a a a a) (:* a 2 2))) "bigint operations")
|
||||
|
||||
(assert-error
|
||||
"trap INT64_MIN / -1"
|
||||
(:/ (bigint/int64 "-0x8000000000000000") -1))
|
||||
(:/ (bigint/int64 "-0x8000_0000_0000_0000") -1))
|
||||
|
||||
# in place operators
|
||||
(assert (let [a (u64 1e10)] (:+! a 1000000 "1000000" "0xffff") (:== a 10002065535)) "in place operators")
|
||||
@ -79,7 +80,7 @@
|
||||
(set (t 2) "1000")
|
||||
(set (t 3) (t 0))
|
||||
(set (t 4) (u64 1000))
|
||||
(and
|
||||
(and
|
||||
(:== (t 0) (t 1))
|
||||
(:== (t 1) (t 2))
|
||||
(:== (t 2) (t 3))
|
||||
|
Loading…
Reference in New Issue
Block a user