diff --git a/configure.ac b/configure.ac index 4f640822bd..558d366e6f 100644 --- a/configure.ac +++ b/configure.ac @@ -80,6 +80,8 @@ if test "x$valgrind_cmd" = "x" ; then fi AC_CHECK_FUNCS(memmem) AC_CHECK_FUNCS(mkstemp) +AC_CHECK_FUNCS(strtoimax) +AC_CHECK_FUNCS(strtoumax) AC_CHECK_HEADER("shlwapi.h",[have_win32=1;]) AM_CONDITIONAL([WIN32], [test "x$have_win32" = x1]) diff --git a/src/jv.c b/src/jv.c index e064baf572..64e358448f 100644 --- a/src/jv.c +++ b/src/jv.c @@ -47,6 +47,10 @@ jv_kind jv_get_kind(jv x) { return x.kind_flags & KIND_MASK; } +jv_subkind jv_get_subkind(jv x) { + return x.subkind_flags & KIND_MASK; +} + const char* jv_kind_name(jv_kind k) { switch (k) { case JV_KIND_INVALID: return ""; @@ -138,12 +142,72 @@ static void jvp_invalid_free(jv x) { */ jv jv_number(double x) { - jv j = {JV_KIND_NUMBER, 0, 0, 0, {.number = x}}; + jv j = {JV_KIND_NUMBER, JV_SUBKIND_NONE, 0, 0, {.number = x}}; + return j; +} + +jv jv_int64(int64_t x) { +#ifndef JQ_OMIT_INTS + jv j = {JV_KIND_NUMBER, JV_SUBKIND_INT64, 0, 0, {.int64 = x}}; +#else + jv j = {JV_KIND_NUMBER, JV_SUBKIND_INT64, 0, 0, {.number = x}}; +#endif + return j; +} + +jv jv_uint64(uint64_t x) { +#ifndef JQ_OMIT_INTS + jv j = {JV_KIND_NUMBER, JV_SUBKIND_UINT64, 0, 0, {.uint64 = x}}; +#else + jv j = {JV_KIND_NUMBER, JV_SUBKIND_UINT64, 0, 0, {.number = x}}; +#endif return j; } double jv_number_value(jv j) { assert(jv_get_kind(j) == JV_KIND_NUMBER); +#ifndef JQ_OMIT_INTS + char sk = jv_get_subkind(j); + if (sk == JV_SUBKIND_NONE) + return j.u.number; + if (sk == JV_SUBKIND_INT64) + return j.u.int64; + assert(sk == JV_SUBKIND_UINT64); + return j.u.uint64; +#else + return j.u.number; +#endif +} + +int64_t jv_int64_value(jv j) +{ + assert(jv_get_kind(j) == JV_KIND_NUMBER); +#ifndef JQ_OMIT_INTS + char sk = jv_get_subkind(j); + if (sk == JV_SUBKIND_INT64) + return j.u.int64; + if (sk == JV_SUBKIND_UINT64) { + if (j.u.uint64 <= INT64_MAX) + return (int64_t)j.u.uint64; + return INT64_MAX; + } +#endif + return j.u.number; +} + +uint64_t jv_uint64_value(jv j) +{ + assert(jv_get_kind(j) == JV_KIND_NUMBER); +#ifndef JQ_OMIT_INTS + char sk = jv_get_subkind(j); + if (sk == JV_SUBKIND_UINT64) + return j.u.uint64; + if (sk == JV_SUBKIND_INT64) { + if (j.u.int64 >= 0) + return j.u.int64; + return 0; + } +#endif return j.u.number; } @@ -151,6 +215,13 @@ int jv_is_integer(jv j){ if(jv_get_kind(j) != JV_KIND_NUMBER){ return 0; } +#ifndef JQ_OMIT_INTS + char sk = jv_get_subkind(j); + if (sk != JV_SUBKIND_NONE) { + assert(sk == JV_SUBKIND_UINT64 || sk == JV_SUBKIND_INT64); + return 1; + } +#endif double x = jv_number_value(j); if(x != x || x > INT_MAX || x < INT_MIN){ return 0; @@ -159,6 +230,42 @@ int jv_is_integer(jv j){ return x == (int)x; } +int jv_is_int64(jv j) +{ + if(jv_get_kind(j) != JV_KIND_NUMBER){ + return 0; + } +#ifndef JQ_OMIT_INTS + char sk = jv_get_subkind(j); + if (sk == JV_SUBKIND_INT64) + return 1; + if (sk == JV_SUBKIND_UINT64) { + if (j.u.uint64 <= INT64_MAX) + return 1; + return 0; + } +#endif + return jv_is_integer(j); +} + +int jv_is_uint64(jv j) +{ + if(jv_get_kind(j) != JV_KIND_NUMBER){ + return 0; + } +#ifndef JQ_OMIT_INTS + char sk = jv_get_subkind(j); + if (sk == JV_SUBKIND_UINT64) + return 1; + if (sk == JV_SUBKIND_INT64) { + if (j.u.int64 >= 0) + return 1; + return 0; + } +#endif + return jv_is_integer(j); +} + /* * Arrays (internal helpers) */ @@ -1302,9 +1409,21 @@ int jv_identical(jv a, jv b) { case JV_KIND_OBJECT: r = a.u.ptr == b.u.ptr; break; - case JV_KIND_NUMBER: + case JV_KIND_NUMBER: { +#ifndef JQ_OMIT_INTS + char ask = jv_get_subkind(a); + char bsk = jv_get_subkind(b); + if (ask != bsk) + r = jv_number_value(a) == jv_number_value(b); + else if (ask == JV_SUBKIND_NONE) + r = memcmp(&a.u.number, &b.u.number, sizeof(a.u.number)) == 0; + else + r = a.u.int64 == b.u.int64; /* this handles int64 and uint64 */ +#else r = memcmp(&a.u.number, &b.u.number, sizeof(a.u.number)) == 0; +#endif break; + } default: r = 1; break; diff --git a/src/jv.h b/src/jv.h index 9e74c9d8ae..f9b9bae982 100644 --- a/src/jv.h +++ b/src/jv.h @@ -16,18 +16,26 @@ typedef enum { JV_KIND_OBJECT } jv_kind; +typedef enum { + JV_SUBKIND_NONE, + JV_SUBKIND_INT64, + JV_SUBKIND_UINT64, +} jv_subkind; + struct jv_refcnt; /* All of the fields of this struct are private. Really. Do not play with them. */ typedef struct { unsigned char kind_flags; - unsigned char pad_; + unsigned char subkind_flags; unsigned short offset; /* array offsets */ int size; union { struct jv_refcnt* ptr; double number; + int64_t int64; + uint64_t uint64; } u; } jv; @@ -37,6 +45,7 @@ typedef struct { */ jv_kind jv_get_kind(jv); +jv_subkind jv_get_subkind(jv); const char* jv_kind_name(jv_kind); static int jv_is_valid(jv x) { return jv_get_kind(x) != JV_KIND_INVALID; } @@ -61,8 +70,14 @@ jv jv_false(void); jv jv_bool(int); jv jv_number(double); +jv jv_int64(int64_t); +jv jv_uint64(uint64_t); double jv_number_value(jv); +int64_t jv_int64_value(jv); +uint64_t jv_uint64_value(jv); int jv_is_integer(jv); +int jv_is_int64(jv); +int jv_is_uint64(jv); jv jv_array(void); jv jv_array_sized(int); diff --git a/src/jv_parse.c b/src/jv_parse.c index 51ad9f0947..2343b226d9 100644 --- a/src/jv_parse.c +++ b/src/jv_parse.c @@ -1,7 +1,10 @@ +#include +#include +#include +#include #include #include #include -#include #include "jv.h" #include "jv_dtoa.h" #include "jv_unicode.h" @@ -496,8 +499,52 @@ static pfunc check_literal(struct jv_parser* p) { double d = jvp_strtod(&p->dtoa, p->tokenbuf, &end); if (end == 0 || *end != 0) return "Invalid numeric literal"; + +#ifndef JQ_OMIT_INTS + if (d == (int64_t)d || d == (uint64_t)d) { + if (d >= INT64_MIN && d <= INT64_MAX) { + TRY(value(p, jv_int64(d))); + goto out; + } else if (d >= 0 && d <= UINT64_MAX) { + TRY(value(p, jv_uint64(d))); + goto out; + } + + char *q = p->tokenbuf; + int is_signed = 0; + while (isspace(*q)) + q++; + if (*q == '-') { + is_signed = 1; + q++; + } + errno = 0; + if (is_signed) { +#ifdef HAVE_STRTOIMAX + int64_t i64 = strtoimax(p->tokenbuf, &q, 10); +#else + int64_t i64 = strtoll(p->tokenbuf, &q, 10); +#endif + if (q == end && i64 < 0 && errno == 0) { + TRY(value(p, jv_int64(i64))); + goto out; + } + } else { +#ifdef HAVE_STRTOUMAX + uint64_t u64 = strtoumax(p->tokenbuf, &q, 10); +#else + uint64_t u64 = strtoull(p->tokenbuf, &q, 10); +#endif + if (q == end && errno == 0) { + TRY(value(p, jv_int64(u64))); + goto out; + } + } + } +#endif TRY(value(p, jv_number(d))); } +out: p->tokenpos = 0; return 0; } diff --git a/src/jv_print.c b/src/jv_print.c index ce4a59afa0..94dcbd0d1b 100644 --- a/src/jv_print.c +++ b/src/jv_print.c @@ -1,6 +1,7 @@ #include -#include +#include #include +#include #include #ifdef WIN32 @@ -182,6 +183,21 @@ static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FI put_str("true", F, S, flags & JV_PRINT_ISATTY); break; case JV_KIND_NUMBER: { +#ifndef JQ_OMIT_INTS + if (jv_is_int64(x)) { + int64_t i64 = jv_int64_value(x); + char buf[21]; + (void) snprintf(buf, sizeof(buf), "%" PRId64, i64); + put_str(buf, F, S, flags & JV_PRINT_ISATTY); + break; + } else if (jv_is_uint64(x)) { + uint64_t u64 = jv_uint64_value(x); + char buf[21]; + (void) snprintf(buf, sizeof(buf), "%" PRIu64, u64); + put_str(buf, F, S, flags & JV_PRINT_ISATTY); + break; + } +#endif double d = jv_number_value(x); if (d != d) { // JSON doesn't have NaN, so we'll render it as "null"