mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-30 23:23:07 +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:
		| @@ -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 | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -35,7 +35,8 @@ | ||||
|    (def b (u64 0x1fffffffffffff)) | ||||
|    (def b (u64 (math/pow 2 53))) | ||||
|    # from string | ||||
|    (def c (u64 "0xffffffffffffffff")) | ||||
|    (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 | ||||
| @@ -61,12 +62,12 @@ | ||||
|    # 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") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 J.-F. Cap
					J.-F. Cap