Skip to content

Commit

Permalink
introduction of yajl_number callback to allow clients to handle arbit…
Browse files Browse the repository at this point in the history
…rary

precision numbers in JSON data.


git-svn-id: http://yajl-c.googlecode.com/svn/trunk@102 e775cfb5-b74b-0410-aad5-5bebe4a96390
  • Loading branch information
llooyd committed Apr 25, 2008
1 parent 1e31767 commit 8c00428
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 51 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Expand Up @@ -32,7 +32,7 @@ PROJECT(YetAnotherJSONParser)

SET (YAJL_MAJOR 0)
SET (YAJL_MINOR 3)
SET (YAJL_MICRO 0)
SET (YAJL_MICRO 1)

SET (YAJL_DIST_NAME "yajl-${YAJL_MAJOR}.${YAJL_MINOR}.${YAJL_MICRO}")

Expand Down
2 changes: 2 additions & 0 deletions TODO
@@ -1,3 +1,5 @@
* add a test for 0x1F bug
* numeric overflow in integers and double
* line and char offsets in the lexer and in error messages
* numbers at eof bug. (lth: still pertinent?)
* testing:
Expand Down
20 changes: 7 additions & 13 deletions reformatter/json_reformat.c
Expand Up @@ -44,24 +44,17 @@ int reformat_null(void * ctx)
return 1;
}

int reformat_boolean(void * ctx, int boolVal)
int reformat_boolean(void * ctx, int boolean)
{
yajl_gen g = (yajl_gen) ctx;
yajl_gen_bool(g, boolVal);
yajl_gen_bool(g, boolean);
return 1;
}

int reformat_integer(void * ctx, long long integerVal)
int reformat_number(void * ctx, const char * s, unsigned int l)
{
yajl_gen g = (yajl_gen) ctx;
yajl_gen_integer(g, integerVal);
return 1;
}

int reformat_double(void * ctx, double doubleVal)
{
yajl_gen g = (yajl_gen) ctx;
yajl_gen_double(g, doubleVal);
yajl_gen_number(g, s, l);
return 1;
}

Expand Down Expand Up @@ -113,8 +106,9 @@ int reformat_end_array(void * ctx)
static yajl_callbacks callbacks = {
reformat_null,
reformat_boolean,
reformat_integer,
reformat_double,
NULL,
NULL,
reformat_number,
reformat_string,
reformat_start_map,
reformat_map_key,
Expand Down
6 changes: 4 additions & 2 deletions src/api/yajl_gen.h
Expand Up @@ -79,9 +79,11 @@ extern "C" {
/** free a generator handle */
void YAJL_API yajl_gen_free(yajl_gen handle);

yajl_gen_status YAJL_API yajl_gen_integer(yajl_gen hand,
long long int number);
yajl_gen_status YAJL_API yajl_gen_integer(yajl_gen hand, long int number);
yajl_gen_status YAJL_API yajl_gen_double(yajl_gen hand, double number);
yajl_gen_status YAJL_API yajl_gen_number(yajl_gen hand,
const char * num,
unsigned int len);
yajl_gen_status YAJL_API yajl_gen_string(yajl_gen hand,
const unsigned char * str,
unsigned int len);
Expand Down
17 changes: 16 additions & 1 deletion src/api/yajl_parse.h
Expand Up @@ -74,12 +74,27 @@ extern "C" {
* All callbacks return an integer. If non-zero, the parse will
* continue. If zero, the parse will be canceled and
* yajl_status_client_canceled will be returned from the parse.
*
* Note about handling of numbers:
* yajl will only convert numbers that can be represented in a double
* or a long int. All other numbers will be passed to the client
* in string form using the yajl_number callback. Furthermore, if
* yajl_number is not NULL, it will always be used to return numbers,
* that is yajl_integer and yajl_double will be ignored. If
* yajl_number is NULL but one of yajl_integer or yajl_double are
* defined, parsing of a number larger than is representable
* in a double or long int will result in a parse error.
*/
typedef struct {
int (* yajl_null)(void * ctx);
int (* yajl_boolean)(void * ctx, int boolVal);
int (* yajl_integer)(void * ctx, long long integerVal);
int (* yajl_integer)(void * ctx, long integerVal);
int (* yajl_double)(void * ctx, double doubleVal);
/** A callback which passes the string representation of the number
* back to the client. Will be used for all numbers when present */
int (* yajl_number)(void * ctx, const char * numberVal,
unsigned int numberLen);

/** strings are returned as pointers into the JSON text when,
* possible, as a result, they are _not_ null padded */
int (* yajl_string)(void * ctx, const unsigned char * stringVal,
Expand Down
2 changes: 1 addition & 1 deletion src/yajl_encode.c
Expand Up @@ -39,7 +39,7 @@

static void CharToHex(unsigned char c, char * hexBuf)
{
const char * hexchar = "0123456789ABCDE";
const char * hexchar = "0123456789ABCDEF";
hexBuf[0] = hexchar[c >> 4];
hexBuf[1] = hexchar[c & 0x0F];
}
Expand Down
15 changes: 13 additions & 2 deletions src/yajl_gen.c
Expand Up @@ -139,11 +139,11 @@ yajl_gen_free(yajl_gen g)
yajl_buf_append(g->buf, "\n", 1);

yajl_gen_status
yajl_gen_integer(yajl_gen g, long long int number)
yajl_gen_integer(yajl_gen g, long int number)
{
char i[32];
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
sprintf(i, "%lld", number);
sprintf(i, "%ld", number);
yajl_buf_append(g->buf, i, strlen(i));
APPENDED_ATOM;
FINAL_NEWLINE;
Expand All @@ -158,6 +158,17 @@ yajl_gen_double(yajl_gen g, double number)
sprintf(i, "%lf", number);
yajl_buf_append(g->buf, i, strlen(i));
APPENDED_ATOM;
FINAL_NEWLINE;
return yajl_gen_status_ok;
}

yajl_gen_status
yajl_gen_number(yajl_gen g, const char * s, unsigned int l)
{
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
yajl_buf_append(g->buf, s, l);
APPENDED_ATOM;
FINAL_NEWLINE;
return yajl_gen_status_ok;
}

Expand Down
77 changes: 52 additions & 25 deletions src/yajl_parser.c
Expand Up @@ -34,10 +34,13 @@
#include "yajl_parser.h"
#include "yajl_encode.h"
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <math.h>

unsigned char *
yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
Expand Down Expand Up @@ -145,7 +148,6 @@ yajl_do_parse(yajl_handle hand, unsigned int * offset,
case yajl_state_parse_error:
hand->errorOffset = *offset;
return yajl_status_error;

case yajl_state_start:
case yajl_state_map_need_val:
case yajl_state_array_need_val:
Expand Down Expand Up @@ -207,44 +209,69 @@ yajl_do_parse(yajl_handle hand, unsigned int * offset,
break;
case yajl_tok_integer:
/*
* note. sscanf does not respect the length of
* note. strtol does not respect the length of
* the lexical token. in a corner case where the
* lexed number is a integer with a trailing zero,
* immediately followed by the end of buffer,
* sscanf could run off into oblivion and cause a
* crash. for this reason we copy the integer
* (and doubles), into our parse buffer (the same
* one used for unescaping strings), before
* calling sscanf. yajl_buf ensures null padding,
* calling strtol. yajl_buf ensures null padding,
* so we're safe.
*/
if (hand->callbacks && hand->callbacks->yajl_integer) {
long long int i = 0;
int neg = 0;
yajl_buf_clear(hand->decodeBuf);
yajl_buf_append(hand->decodeBuf, buf, bufLen);
buf = yajl_buf_data(hand->decodeBuf);
if (*buf == '-') {
buf++; neg = 1;
if (hand->callbacks) {
if (hand->callbacks->yajl_number) {
_CC_CHK(hand->callbacks->yajl_number(hand->ctx,
(char *) buf,
bufLen));
} else if (hand->callbacks->yajl_integer) {
long int i = 0;
yajl_buf_clear(hand->decodeBuf);
yajl_buf_append(hand->decodeBuf, buf, bufLen);
buf = yajl_buf_data(hand->decodeBuf);
i = strtol((char *) buf, NULL, 10);
if ((i == LONG_MIN || i == LONG_MAX) &&
errno == ERANGE)
{
yajl_state_set(hand, yajl_state_parse_error);
hand->parseError = "integer overflow" ;
/* try to restore error offset */
if (*offset >= bufLen) *offset -= bufLen;
else *offset = 0;
goto around_again;
}
_CC_CHK(hand->callbacks->yajl_integer(hand->ctx,
i));
}
sscanf((char *) buf, "%lld", &i);
if (neg) i -= (i<<1);
_CC_CHK(hand->callbacks->yajl_integer(hand->ctx, i));
}
break;
case yajl_tok_double:
if (hand->callbacks && hand->callbacks->yajl_double) {
double d;
int neg = 0;
yajl_buf_clear(hand->decodeBuf);
yajl_buf_append(hand->decodeBuf, buf, bufLen);
buf = yajl_buf_data(hand->decodeBuf);
if (*buf == '-') {
buf++; neg = 1;
if (hand->callbacks) {
if (hand->callbacks->yajl_number) {
_CC_CHK(hand->callbacks->yajl_number(hand->ctx,
(char *) buf,
bufLen));
} else if (hand->callbacks->yajl_double) {
double d = 0.0;
yajl_buf_clear(hand->decodeBuf);
yajl_buf_append(hand->decodeBuf, buf, bufLen);
buf = yajl_buf_data(hand->decodeBuf);
d = strtod((char *) buf, NULL);
if ((d == HUGE_VAL || d == -HUGE_VAL) &&
errno == ERANGE)
{
yajl_state_set(hand, yajl_state_parse_error);
hand->parseError = "numeric (floating point) "
"overflow";
/* try to restore error offset */
if (*offset >= bufLen) *offset -= bufLen;
else *offset = 0;
goto around_again;
}
_CC_CHK(hand->callbacks->yajl_double(hand->ctx,
d));
}
sscanf((char *) buf, "%lf", &d);
if (neg) d *= -1.0;
_CC_CHK(hand->callbacks->yajl_double(hand->ctx, d));
}
break;
case yajl_tok_right_brace: {
Expand Down
3 changes: 2 additions & 1 deletion test/cases/integers.json
@@ -1,3 +1,4 @@
[ 1,2,3,4,5,6,7,
123456789 , -123456789,
9223372036854775807, -9223372036854775807 ]
2147483647, -2147483647,
2147483648 ]
6 changes: 3 additions & 3 deletions test/cases/integers.json.gold
Expand Up @@ -8,6 +8,6 @@ integer: 6
integer: 7
integer: 123456789
integer: -123456789
integer: 9223372036854775807
integer: -9223372036854775807
array close ']'
integer: 2147483647
integer: -2147483647
parse error: integer overflow
5 changes: 3 additions & 2 deletions test/yajl_test.c
Expand Up @@ -51,9 +51,9 @@ int test_yajl_boolean(void * ctx, int boolVal)
return 1;
}

int test_yajl_integer(void *ctx, long long integerVal)
int test_yajl_integer(void *ctx, long integerVal)
{
printf("integer: %lld\n", integerVal);
printf("integer: %ld\n", integerVal);
return 1;
}

Expand Down Expand Up @@ -113,6 +113,7 @@ static yajl_callbacks callbacks = {
test_yajl_boolean,
test_yajl_integer,
test_yajl_double,
NULL,
test_yajl_string,
test_yajl_start_map,
test_yajl_map_key,
Expand Down

0 comments on commit 8c00428

Please sign in to comment.