use custom string to bigint reader in place of strtol

for better compatibility with default janet number reader
This commit is contained in:
J.-F. Cap 2019-03-16 16:46:57 +01:00 committed by Calvin Rose
parent 1db6d0e0bc
commit a07d76b264
4 changed files with 113 additions and 33 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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))