-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
jv: Add some support for 64 bit ints in a very conservative way (ALTERNATIVE) #1327
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -363,6 +363,33 @@ static jv f_tonumber(jq_state *jq, jv input) { | |
return type_error(input, "cannot be parsed as a number"); | ||
} | ||
|
||
static jv f_toint(jq_state *jq, jv input) { | ||
input = f_tonumber(jq, input); | ||
if (!jv_is_valid(input)) | ||
return input; | ||
if (jv_is_int64(input) || jv_is_uint64(input)) | ||
return input; | ||
double d = jv_number_value(input); | ||
if (d < 0 && d >= INT64_MIN) { | ||
int64_t i = d; | ||
if (i < 0) | ||
return jv_int64(i); | ||
} else if (d < UINT64_MAX) { | ||
uint64_t u = d; | ||
if (d == (double)u) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also this one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto. Here we know that |
||
return jv_uint64(u); | ||
} | ||
return jv_number(nearbyint(d)); | ||
} | ||
|
||
static jv f_isint(jq_state *jq, jv input) { | ||
if (jv_get_kind(input) != JV_KIND_NUMBER) | ||
return type_error(input, "only numbers can be integers"); | ||
if (jv_is_int64(input) || jv_is_uint64(input) || jv_is_integer(input)) | ||
return jv_true(); | ||
return jv_false(); | ||
} | ||
|
||
static jv f_length(jq_state *jq, jv input) { | ||
if (jv_get_kind(input) == JV_KIND_ARRAY) { | ||
return jv_number(jv_array_length(input)); | ||
|
@@ -1285,6 +1312,8 @@ static const struct cfunction function_list[] = { | |
{(cfunction_ptr)f_json_parse, "fromjson", 1}, | ||
{(cfunction_ptr)f_tonumber, "tonumber", 1}, | ||
{(cfunction_ptr)f_tostring, "tostring", 1}, | ||
{(cfunction_ptr)f_toint, "tointeger", 1}, | ||
{(cfunction_ptr)f_isint, "isinteger", 1}, | ||
{(cfunction_ptr)f_keys, "keys", 1}, | ||
{(cfunction_ptr)f_keys_unsorted, "keys_unsorted", 1}, | ||
{(cfunction_ptr)f_startswith, "startswith", 2}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 "<invalid>"; | ||
|
@@ -138,27 +142,139 @@ 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}}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This uses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops! (It would have no effect, given that other code that would check it would be omitted. But it's still a bug.) |
||
#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; | ||
} | ||
if (j.u.number > 0 && (int64_t)j.u.number < 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm concerned this might be processor-specific behavior and prone to breaking.
Basically, we get the next double in the direction of zero from whatever we get after converting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this! Thanks! |
||
return INT64_MAX; | ||
if (j.u.number < 0 && (int64_t)j.u.number > 0) | ||
return INT64_MIN; | ||
#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; | ||
} | ||
if (j.u.number < 0) | ||
return 0; | ||
if (j.u.number > (double)UINT64_MAX) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, perhaps something like this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes! |
||
return UINT64_MAX; | ||
#endif | ||
return j.u.number; | ||
} | ||
|
||
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); | ||
/* XXX Check against actual double min/max integers */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I seem to repeat myself. 😜
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough. |
||
if(x != x || x > INT_MAX || x < INT_MIN){ | ||
return 0; | ||
} | ||
|
||
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 +1418,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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And now our padding is gone. 😢 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, yes, we lose the padding. I suppose I could encode sub-kind into kind_flags. We could always do that later if ever we want to use the padding for something else. |
||
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); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's happening here? Is this a guard against the cast turning
-0.5
into0
instead of-1
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's just for deciding whether to return a signed 64-bit integer representation or unsigned 64-bit integer representation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if the check is false, the next code that runs is
return jv_number(nearbyint(d));
, which is a double representation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These checks should use the
nextafter
stuff from below forINT64_MIN
andUINT64_MAX
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eh? No, there's an
else if
at 377.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps I misunderstand.
Line 373 is checking to see if
d is negative AND d fits into an int64_t
Line 374 does the cast
What does line 375 check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yes, i'm the one who's confused here. I need to s/else // here :)