Here the diff between the epics version (debian patch unapplyed) and the
current 2.1.0 version of yajl (debian patch unapplyed).
not that simple...
diff --git a/src/yajl.c b/src/yajl.c
index d477893..fdad3f6 100644
--- a/src/yajl.c
+++ b/src/yajl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,16 +14,16 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "api/yajl_parse.h"
-#include "yajl_lex.h"
-#include "yajl_parser.h"
-#include "yajl_alloc.h"
-
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
+#include "yajl_parse.h"
+#include "yajl_lex.h"
+#include "yajl_parser.h"
+#include "yajl_alloc.h"
+
const char *
yajl_status_to_string(yajl_status stat)
{
@@ -62,16 +62,19 @@ yajl_alloc(const yajl_callbacks * callbacks,
}
hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t));
+ if (hand == NULL) {
+ return NULL;
+ }
/* copy in pointers to allocation routines */
memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
hand->callbacks = callbacks;
hand->ctx = ctx;
- hand->lexer = NULL;
+ hand->lexer = NULL;
hand->bytesConsumed = 0;
hand->decodeBuf = yajl_buf_alloc(&(hand->alloc));
- hand->flags = 0;
+ hand->flags = yajl_allow_json5 | yajl_allow_comments;
yajl_bs_init(hand->stateStack, &(hand->alloc));
yajl_bs_push(hand->stateStack, yajl_state_start);
@@ -79,13 +82,16 @@ yajl_alloc(const yajl_callbacks * callbacks,
}
int
-yajl_config(yajl_handle h, yajl_option opt, ...)
+yajl_config(yajl_handle h, int option, ...)
{
+ yajl_option opt = option; /* UB to use an enum in va_start */
int rv = 1;
va_list ap;
- va_start(ap, opt);
+ va_start(ap, option);
switch(opt) {
+ case yajl_allow_json5:
+ opt |= yajl_allow_comments; /* JSON5 allows comments */
case yajl_allow_comments:
case yajl_dont_validate_strings:
case yajl_allow_trailing_garbage:
@@ -124,7 +130,11 @@ yajl_parse(yajl_handle hand, const unsigned char * jsonText,
if (hand->lexer == NULL) {
hand->lexer = yajl_lex_alloc(&(hand->alloc),
hand->flags & yajl_allow_comments,
- !(hand->flags & yajl_dont_validate_strings));
+ !(hand->flags & yajl_dont_validate_strings),
+ hand->flags & yajl_allow_json5);
+ }
+ if (hand->lexer == NULL) {
+ return yajl_status_error;
}
status = yajl_do_parse(hand, jsonText, jsonTextLen);
@@ -144,7 +154,8 @@ yajl_complete_parse(yajl_handle hand)
if (hand->lexer == NULL) {
hand->lexer = yajl_lex_alloc(&(hand->alloc),
hand->flags & yajl_allow_comments,
- !(hand->flags & yajl_dont_validate_strings));
+ !(hand->flags & yajl_dont_validate_strings),
+ hand->flags & yajl_allow_json5);
}
return yajl_do_finish(hand);
diff --git a/src/yajl_alloc.c b/src/yajl_alloc.c
index 96ad1d3..2388814 100644
--- a/src/yajl_alloc.c
+++ b/src/yajl_alloc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -20,25 +20,23 @@
* free
*/
-#include "yajl_alloc.h"
#include <stdlib.h>
+#include "yajl_alloc.h"
+
static void * yajl_internal_malloc(void *ctx, size_t sz)
{
- (void)ctx;
return malloc(sz);
}
static void * yajl_internal_realloc(void *ctx, void * previous,
size_t sz)
{
- (void)ctx;
return realloc(previous, sz);
}
static void yajl_internal_free(void *ctx, void * ptr)
{
- (void)ctx;
free(ptr);
}
diff --git a/src/yajl_alloc.h b/src/yajl_alloc.h
index 203c2f9..406310a 100644
--- a/src/yajl_alloc.h
+++ b/src/yajl_alloc.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -16,19 +16,31 @@
/**
* \file yajl_alloc.h
- * default memory allocation routines for yajl which use malloc/realloc and
- * free
+ * \brief Memory allocation macros for yajl
+ * \author Lloyd Hilaiel
+ *
+ * These macros are used inside YAJL instead of directly calling
+ * malloc(), realloc() or free(). They call the equivalent method
+ * in their \ref yajl_alloc_funcs parameter \a afs.
*/
#ifndef __YAJL_ALLOC_H__
#define __YAJL_ALLOC_H__
-#include "api/yajl_common.h"
+#include "yajl_common.h"
#define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz))
#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr))
#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz))
-void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf);
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+YAJL_API void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf);
+
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/src/yajl_buf.c b/src/yajl_buf.c
index 55c11ad..4bd8d74 100644
--- a/src/yajl_buf.c
+++ b/src/yajl_buf.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,12 +14,12 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "yajl_buf.h"
-
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include "yajl_buf.h"
+
#define YAJL_BUF_INIT_SIZE 2048
struct yajl_buf_t {
@@ -33,7 +33,7 @@ static
void yajl_buf_ensure_available(yajl_buf buf, size_t want)
{
size_t need;
-
+
assert(buf != NULL);
/* first call */
@@ -45,17 +45,7 @@ void yajl_buf_ensure_available(yajl_buf buf, size_t want)
need = buf->len;
- if (((buf->used > want) ? buf->used : want) > (size_t)(buf->used + want)) {
- /* We cannot allocate more memory than SIZE_MAX. */
- abort();
- }
- while (want >= (need - buf->used)) {
- if (need >= (size_t)((size_t)(-1)<<1)>>1) {
- /* need would overflow. */
- abort();
- }
- need <<= 1;
- }
+ while (want >= (need - buf->used)) need <<= 1;
if (need != buf->len) {
buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need);
@@ -66,6 +56,10 @@ void yajl_buf_ensure_available(yajl_buf buf, size_t want)
yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc)
{
yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t));
+ if (b == NULL) {
+ return NULL;
+ }
+
memset((void *) b, 0, sizeof(struct yajl_buf_t));
b->alloc = alloc;
return b;
diff --git a/src/yajl_buf.h b/src/yajl_buf.h
index a358246..40a6fbd 100644
--- a/src/yajl_buf.h
+++ b/src/yajl_buf.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,13 +17,13 @@
#ifndef __YAJL_BUF_H__
#define __YAJL_BUF_H__
-#include "api/yajl_common.h"
+#include "yajl_common.h"
#include "yajl_alloc.h"
/*
* Implementation/performance notes. If this were moved to a header
- * only implementation using #define's where possible we might be
- * able to sqeeze a little performance out of the guy by killing function
+ * only implementation using #define's where possible we might be
+ * able to squeeze a little performance out of the guy by killing function
* call overhead. YMMV.
*/
@@ -33,6 +33,10 @@
*/
typedef struct yajl_buf_t * yajl_buf;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* allocate a new buffer */
yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc);
@@ -54,4 +58,8 @@ size_t yajl_buf_len(yajl_buf buf);
/* truncate the buffer */
void yajl_buf_truncate(yajl_buf buf, size_t len);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/yajl_bytestack.h b/src/yajl_bytestack.h
index 9ea7d15..d75e456 100644
--- a/src/yajl_bytestack.h
+++ b/src/yajl_bytestack.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -22,7 +22,7 @@
#ifndef __YAJL_BYTESTACK_H__
#define __YAJL_BYTESTACK_H__
-#include "api/yajl_common.h"
+#include "yajl_common.h"
#define YAJL_BS_INC 128
diff --git a/src/yajl_encode.c b/src/yajl_encode.c
index 0d97cc5..33cc780 100644
--- a/src/yajl_encode.c
+++ b/src/yajl_encode.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,13 +14,13 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "yajl_encode.h"
-
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
+#include "yajl_encode.h"
+
static void CharToHex(unsigned char c, char * hexBuf)
{
const char * hexchar = "0123456789ABCDEF";
@@ -33,13 +33,22 @@ yajl_string_encode(const yajl_print_t print,
void * ctx,
const unsigned char * str,
size_t len,
- int escape_solidus)
+ int escape_solidus,
+ int output_json5)
{
size_t beg = 0;
size_t end = 0;
char hexBuf[7];
- hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0';
- hexBuf[6] = 0;
+ char *hexAt;
+ if (output_json5) {
+ hexBuf[0] = '\\'; hexBuf[1] = 'x';
+ hexBuf[4] = 0;
+ hexAt = &hexBuf[2];
+ } else {
+ hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0';
+ hexBuf[6] = 0;
+ hexAt = &hexBuf[4];
+ }
while (end < len) {
const char * escaped = NULL;
@@ -57,9 +66,20 @@ yajl_string_encode(const yajl_print_t print,
case '\f': escaped = "\\f"; break;
case '\b': escaped = "\\b"; break;
case '\t': escaped = "\\t"; break;
+ case '\0':
+ if (output_json5) {
+ escaped = "\\0"; break;
+ }
+ goto ashex;
+ case '\v':
+ if (output_json5) {
+ escaped = "\\v"; break;
+ }
+ goto ashex;
default:
if ((unsigned char) str[end] < 32) {
- CharToHex(str[end], hexBuf + 4);
+ ashex:
+ CharToHex(str[end], hexAt);
escaped = hexBuf;
}
break;
@@ -75,10 +95,10 @@ yajl_string_encode(const yajl_print_t print,
print(ctx, (const char *) (str + beg), end - beg);
}
-static void hexToDigit(unsigned int * val, const unsigned char * hex)
+static void hexToDigit(unsigned int * val, unsigned int len, const unsigned char * hex)
{
unsigned int i;
- for (i=0;i<4;i++) {
+ for (i=0;i<len;i++) {
unsigned char c = hex[i];
if (c >= 'A') c = (c & ~0x20) - 7;
c -= '0';
@@ -87,7 +107,7 @@ static void hexToDigit(unsigned int * val, const unsigned char * hex)
}
}
-static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf)
+static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf)
{
if (codepoint < 0x80) {
utf8Buf[0] = (char) codepoint;
@@ -117,7 +137,7 @@ void yajl_string_decode(yajl_buf buf, const unsigned char * str,
size_t len)
{
size_t beg = 0;
- size_t end = 0;
+ size_t end = 0;
while (end < len) {
if (str[end] == '\\') {
@@ -128,24 +148,21 @@ void yajl_string_decode(yajl_buf buf, const unsigned char * str,
case 'r': unescaped = "\r"; break;
case 'n': unescaped = "\n"; break;
case '\\': unescaped = "\\"; break;
- case '/': unescaped = "/"; break;
- case '"': unescaped = "\""; break;
case 'f': unescaped = "\f"; break;
case 'b': unescaped = "\b"; break;
case 't': unescaped = "\t"; break;
case 'u': {
unsigned int codepoint = 0;
- hexToDigit(&codepoint, str + ++end);
+ hexToDigit(&codepoint, 4, str + ++end);
end+=3;
/* check if this is a surrogate */
if ((codepoint & 0xFC00) == 0xD800) {
- if (end + 2 < len && str[end + 1] == '\\' && str[end + 2] == 'u') {
- end++;
+ if (str[end + 1] == '\\' && str[end + 2] == 'u') {
unsigned int surrogate = 0;
- hexToDigit(&surrogate, str + end + 2);
+ hexToDigit(&surrogate, 4, str + ++end + 2);
codepoint =
- (((codepoint & 0x3F) << 10) |
- ((((codepoint >> 6) & 0xF) + 1) << 16) |
+ (((codepoint & 0x3F) << 10) |
+ ((((codepoint >> 6) & 0xF) + 1) << 16) |
(surrogate & 0x3FF));
end += 5;
} else {
@@ -153,7 +170,7 @@ void yajl_string_decode(yajl_buf buf, const unsigned char * str,
break;
}
}
-
+
Utf32toUtf8(codepoint, utf8Buf);
unescaped = utf8Buf;
@@ -165,8 +182,33 @@ void yajl_string_decode(yajl_buf buf, const unsigned char * str,
break;
}
+ /* The following escapes are only valid when parsing JSON5.
+ * The lexer catches them when allowJson5 is not set.
+ */
+ case '\n': beg = ++end; continue;
+ case '\r':
+ if (str[++end] == '\n') ++end;
+ beg = end;
+ continue;
+ case '0':
+ utf8Buf[0] = '\0';
+ yajl_buf_append(buf, utf8Buf, 1);
+ beg = ++end;
+ continue;
+ case 'v': unescaped = "\v"; break;
+ case 'x': {
+ unsigned int codepoint = 0;
+ hexToDigit(&codepoint, 2, str + ++end);
+ end++;
+ utf8Buf[0] = (char) codepoint;
+ yajl_buf_append(buf, utf8Buf, 1);
+ beg = ++end;
+ continue;
+ }
default:
- assert("this should never happen" == NULL);
+ utf8Buf[0] = str[end];
+ utf8Buf[1] = 0;
+ unescaped = utf8Buf;
}
yajl_buf_append(buf, unescaped, (unsigned int)strlen(unescaped));
beg = ++end;
@@ -183,13 +225,13 @@ int yajl_string_validate_utf8(const unsigned char * s, size_t len)
{
if (!len) return 1;
if (!s) return 0;
-
+
while (len--) {
/* single byte */
if (*s <= 0x7f) {
/* noop */
}
- /* two byte */
+ /* two byte */
else if ((*s >> 5) == 0x6) {
ADV_PTR;
if (!((*s >> 6) == 0x2)) return 0;
@@ -201,7 +243,7 @@ int yajl_string_validate_utf8(const unsigned char * s, size_t len)
ADV_PTR;
if (!((*s >> 6) == 0x2)) return 0;
}
- /* four byte */
+ /* four byte */
else if ((*s >> 3) == 0x1e) {
ADV_PTR;
if (!((*s >> 6) == 0x2)) return 0;
@@ -212,9 +254,33 @@ int yajl_string_validate_utf8(const unsigned char * s, size_t len)
} else {
return 0;
}
-
+
s++;
}
-
+
+ return 1;
+}
+
+int yajl_string_validate_identifier(const unsigned char * str, size_t len)
+{
+ const unsigned char * s = str;
+ int c;
+
+ if (!len || !str) return 0;
+
+ c = *s++; /* First character [$_A-Za-z] */
+ if ((c != '$' && c < 'A') ||
+ (c > 'Z' && c != '_' && c < 'a') ||
+ (c > 'z'))
+ return 0;
+
+ while (--len) {
+ c = *s++; /* Remaining characters [$_A-Za-z0-9] */
+ if ((c != '$' && c < '0') ||
+ (c > '9' && c < 'A') ||
+ (c > 'Z' && c != '_' && c < 'a') ||
+ (c > 'z'))
+ return 0;
+ }
return 1;
}
diff --git a/src/yajl_encode.h b/src/yajl_encode.h
index 853a1a7..fd58dec 100644
--- a/src/yajl_encode.h
+++ b/src/yajl_encode.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,17 +18,28 @@
#define __YAJL_ENCODE_H__
#include "yajl_buf.h"
-#include "api/yajl_gen.h"
+#include "yajl_gen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
void yajl_string_encode(const yajl_print_t printer,
void * ctx,
const unsigned char * str,
size_t length,
- int escape_solidus);
+ int escape_solidus,
+ int output_json5);
void yajl_string_decode(yajl_buf buf, const unsigned char * str,
size_t length);
int yajl_string_validate_utf8(const unsigned char * s, size_t len);
+int yajl_string_validate_identifier(const unsigned char * str, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/yajl_gen.c b/src/yajl_gen.c
index 0f5c68e..6d93784 100644
--- a/src/yajl_gen.c
+++ b/src/yajl_gen.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,16 +14,16 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "api/yajl_gen.h"
-#include "yajl_buf.h"
-#include "yajl_encode.h"
-
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
-#include <math.h>
#include <stdarg.h>
+#include "epicsMath.h"
+#include "yajl_gen.h"
+#include "yajl_buf.h"
+#include "yajl_encode.h"
+
typedef enum {
yajl_gen_start,
yajl_gen_map_start,
@@ -48,16 +48,17 @@ struct yajl_gen_t
};
int
-yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...)
+yajl_gen_config(yajl_gen g, int option, ...)
{
+ yajl_gen_option opt = option; /* UB to use an enum in va_start */
int rv = 1;
va_list ap;
- va_start(ap, opt);
+ va_start(ap, option);
switch(opt) {
case yajl_gen_beautify:
case yajl_gen_validate_utf8:
- case yajl_gen_escape_solidus:
+ case yajl_gen_json5:
if (va_arg(ap, int)) g->flags |= opt;
else g->flags &= ~opt;
break;
@@ -141,17 +142,17 @@ yajl_gen_free(yajl_gen g)
}
#define INSERT_SEP \
- if (g->state[g->depth] == yajl_gen_map_key || \
- g->state[g->depth] == yajl_gen_in_array) { \
- g->print(g->ctx, ",", 1); \
- if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \
- } else if (g->state[g->depth] == yajl_gen_map_val) { \
- g->print(g->ctx, ":", 1); \
- if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \
- }
-
-#define INSERT_WHITESPACE \
- if ((g->flags & yajl_gen_beautify)) { \
+ if (g->state[g->depth] == yajl_gen_map_key || \
+ g->state[g->depth] == yajl_gen_in_array) { \
+ g->print(g->ctx, ",", 1); \
+ if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \
+ } else if (g->state[g->depth] == yajl_gen_map_val) { \
+ g->print(g->ctx, ":", 1); \
+ if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \
+ }
+
+#define INSERT_WHITESPACE \
+ if ((g->flags & yajl_gen_beautify)) { \
if (g->state[g->depth] != yajl_gen_map_val) { \
unsigned int _i; \
for (_i=0;_i<g->depth;_i++) \
@@ -170,8 +171,8 @@ yajl_gen_free(yajl_gen g)
/* check that we're not complete, or in error state. in a valid state
* to be generating */
#define ENSURE_VALID_STATE \
- if (g->state[g->depth] == yajl_gen_error) { \
- return yajl_gen_in_error_state;\
+ if (g->state[g->depth] == yajl_gen_error) { \
+ return yajl_gen_in_error_state; \
} else if (g->state[g->depth] == yajl_gen_complete) { \
return yajl_gen_generation_complete; \
}
@@ -180,7 +181,7 @@ yajl_gen_free(yajl_gen g)
if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded;
#define DECREMENT_DEPTH \
- if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_gen_generation_complete;
+ if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded;
#define APPENDED_ATOM \
switch (g->state[g->depth]) { \
@@ -201,8 +202,9 @@ yajl_gen_free(yajl_gen g)
break; \
} \
-#define FINAL_NEWLINE \
- if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \
+#define FINAL_NEWLINE \
+ if ((g->flags & yajl_gen_beautify) && \
+ g->state[g->depth] == yajl_gen_complete) \
g->print(g->ctx, "\n", 1);
yajl_gen_status
@@ -217,23 +219,28 @@ yajl_gen_integer(yajl_gen g, long long int number)
return yajl_gen_status_ok;
}
-#if defined(_WIN32) || defined(WIN32)
-#include <float.h>
-#define isnan _isnan
-#define isinf !_finite
-#endif
-
yajl_gen_status
yajl_gen_double(yajl_gen g, double number)
{
char i[32];
+ int special = 1;
ENSURE_VALID_STATE; ENSURE_NOT_KEY;
- if (isnan(number) || isinf(number)) return yajl_gen_invalid_number;
- INSERT_SEP; INSERT_WHITESPACE;
- sprintf(i, "%.20g", number);
- if (strspn(i, "0123456789-") == strlen(i)) {
- strcat(i, ".0");
+ if (isnan(number)) {
+ strcpy(i, "NaN");
+ }
+ else if (isinf(number)) {
+ sprintf(i, "%cInfinity", number < 0 ? '-' : '+');
+ }
+ else {
+ special = 0;
+ sprintf(i, "%.17g", number);
+ if (strspn(i, "0123456789-") == strlen(i)) {
+ strcat(i, ".0");
+ }
}
+ if (special && !(g->flags & yajl_gen_json5))
+ return yajl_gen_invalid_number;
+ INSERT_SEP; INSERT_WHITESPACE;
g->print(g->ctx, i, (unsigned int)strlen(i));
APPENDED_ATOM;
FINAL_NEWLINE;
@@ -263,9 +270,19 @@ yajl_gen_string(yajl_gen g, const unsigned char * str,
}
}
ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE;
- g->print(g->ctx, "\"", 1);
- yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus);
- g->print(g->ctx, "\"", 1);
+ if (g->flags & yajl_gen_json5 &&
+ (g->state[g->depth] == yajl_gen_map_key ||
+ g->state[g->depth] == yajl_gen_map_start) &&
+ yajl_string_validate_identifier(str, len)) {
+ /* No need to quote this key */
+ g->print(g->ctx, (const char *) str, len);
+ }
+ else {
+ g->print(g->ctx, "\"", 1);
+ yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus,
+ g->flags & yajl_gen_json5);
+ g->print(g->ctx, "\"", 1);
+ }
APPENDED_ATOM;
FINAL_NEWLINE;
return yajl_gen_status_ok;
@@ -286,7 +303,7 @@ yajl_gen_bool(yajl_gen g, int boolean)
{
const char * val = boolean ? "true" : "false";
- ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
+ ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
g->print(g->ctx, val, (unsigned int)strlen(val));
APPENDED_ATOM;
FINAL_NEWLINE;
diff --git a/src/yajl_lex.c b/src/yajl_lex.c
index 0b6f7cc..f79291c 100644
--- a/src/yajl_lex.c
+++ b/src/yajl_lex.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,14 +14,14 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "yajl_lex.h"
-#include "yajl_buf.h"
-
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
+#include "yajl_lex.h"
+#include "yajl_buf.h"
+
#ifdef YAJL_LEXER_DEBUG
static const char *
tokToStr(yajl_tok tok)
@@ -66,7 +66,7 @@ tokToStr(yajl_tok tok)
*/
struct yajl_lexer_t {
- /* the overal line and char offset into the data */
+ /* the overall line and char offset into the data */
size_t lineOff;
size_t charOff;
@@ -87,6 +87,9 @@ struct yajl_lexer_t {
/* shall we allow comments? */
unsigned int allowComments;
+ /* are we parsing JSON5? */
+ unsigned int allowJson5;
+
/* shall we validate utf8 inside strings? */
unsigned int validateUTF8;
@@ -102,13 +105,19 @@ struct yajl_lexer_t {
yajl_lexer
yajl_lex_alloc(yajl_alloc_funcs * alloc,
- unsigned int allowComments, unsigned int validateUTF8)
+ unsigned int allowComments, unsigned int validateUTF8,
+ unsigned int allowJson5)
{
yajl_lexer lxr = (yajl_lexer) YA_MALLOC(alloc, sizeof(struct yajl_lexer_t));
+ if (lxr == NULL) {
+ return NULL;
+ }
+
memset((void *) lxr, 0, sizeof(struct yajl_lexer_t));
lxr->buf = yajl_buf_alloc(alloc);
lxr->allowComments = allowComments;
lxr->validateUTF8 = validateUTF8;
+ lxr->allowJson5 = !!allowJson5;
lxr->alloc = alloc;
return lxr;
}
@@ -121,19 +130,21 @@ yajl_lex_free(yajl_lexer lxr)
return;
}
-/* a lookup table which lets us quickly determine three things:
+/* a lookup table which lets us quickly determine various things:
* VEC - valid escaped control char
- * note. the solidus '/' may be escaped or not.
+ * Note: the solidus '/' may be escaped or not.
* IJC - invalid json char
* VHC - valid hex char
* NFP - needs further processing (from a string scanning perspective)
* NUC - needs utf8 checking when enabled (from a string scanning perspective)
+ * VIC - valid identifier char (after the first char)
*/
#define VEC 0x01
#define IJC 0x02
#define VHC 0x04
#define NFP 0x08
#define NUC 0x10
+#define VIC 0x20
static const char charLookupTable[256] =
{
@@ -142,20 +153,20 @@ static const char charLookupTable[256] =
/*10*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC ,
/*18*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC ,
-/*20*/ 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 , 0 , 0 ,
+/*20*/ 0 , 0 , NFP|VEC, 0 , VIC , 0 , 0 , NFP|VEC,
/*28*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , VEC ,
-/*30*/ VHC , VHC , VHC , VHC , VHC , VHC , VHC , VHC ,
-/*38*/ VHC , VHC , 0 , 0 , 0 , 0 , 0 , 0 ,
+/*30*/ VHC|VIC, VHC|VIC, VHC|VIC, VHC|VIC, VHC|VIC, VHC|VIC, VHC|VIC, VHC|VIC,
+/*38*/ VHC|VIC, VHC|VIC, 0 , 0 , 0 , 0 , 0 , 0 ,
-/*40*/ 0 , VHC , VHC , VHC , VHC , VHC , VHC , 0 ,
-/*48*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
-/*50*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
-/*58*/ 0 , 0 , 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 ,
+/*40*/ 0 , VHC|VIC, VHC|VIC, VHC|VIC, VHC|VIC, VHC|VIC, VHC|VIC, VIC ,
+/*48*/ VIC , VIC , VIC , VIC , VIC , VIC , VIC , VIC ,
+/*50*/ VIC , VIC , VIC , VIC , VIC , VIC , VIC , VIC ,
+/*58*/ VIC , VIC , VIC , 0 , NFP|VEC|IJC, 0 , 0 , VIC ,
-/*60*/ 0 , VHC , VEC|VHC, VHC , VHC , VHC , VEC|VHC, 0 ,
-/*68*/ 0 , 0 , 0 , 0 , 0 , 0 , VEC , 0 ,
-/*70*/ 0 , 0 , VEC , 0 , VEC , 0 , 0 , 0 ,
-/*78*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
+/*60*/ 0 , VHC|VIC, VEC|VHC|VIC, VHC|VIC, VHC|VIC, VHC|VIC, VEC|VHC|VIC, VIC,
+/*68*/ VIC , VIC , VIC , VIC , VIC , VIC , VEC|VIC, VIC ,
+/*70*/ VIC , VIC , VEC|VIC, VIC , VEC|VIC, VIC , VIC , VIC ,
+/*78*/ VIC , VIC , VIC , 0 , 0 , 0 , 0 , 0 ,
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
@@ -189,7 +200,7 @@ static const char charLookupTable[256] =
*
* NOTE: on error the offset will point to the first char of the
* invalid utf8 */
-#define UTF8_CHECK_EOF if (*offset >= jsonTextLen) { return yajl_tok_eof; }
+#define UTF8_CHECK_EOF if (*offset >= jsonTextLen) return yajl_tok_eof;
static yajl_tok
yajl_lex_utf8_char(yajl_lexer lexer, const unsigned char * jsonText,
@@ -266,7 +277,7 @@ yajl_string_scan(const unsigned char * buf, size_t len, int utf8check)
static yajl_tok
yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText,
- size_t jsonTextLen, size_t * offset)
+ size_t jsonTextLen, size_t * offset, const char quote)
{
yajl_tok tok = yajl_tok_error;
int hasEscapes = 0;
@@ -301,7 +312,7 @@ yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText,
curChar = readChar(lexer, jsonText, offset);
/* quote terminates */
- if (curChar == '"') {
+ if (curChar == quote) {
tok = yajl_tok_string;
break;
}
@@ -321,16 +332,38 @@ yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText,
if (!(charLookupTable[curChar] & VHC)) {
/* back up to offending char */
unreadChar(lexer, offset);
- lexer->error = yajl_lex_string_invalid_hex_char;
+ lexer->error = yajl_lex_string_invalid_hex_u_char;
goto finish_string_lex;
}
}
- } else if (!(charLookupTable[curChar] & VEC)) {
+ }
+ else if (lexer->allowJson5 && curChar == 'x') {
+ unsigned int i = 0;
+
+ for (i=0;i<2;i++) {
+ STR_CHECK_EOF;
+ curChar = readChar(lexer, jsonText, offset);
+ if (!(charLookupTable[curChar] & VHC)) {
+ /* back up to offending char */
+ unreadChar(lexer, offset);
+ lexer->error = yajl_lex_string_invalid_hex_x_char;
+ goto finish_string_lex;
+ }
+ }
+ }
+ else if (lexer->allowJson5 ? (curChar >= '1' && curChar <= '9')
+ : !(charLookupTable[curChar] & VEC)) {
/* back up to offending char */
unreadChar(lexer, offset);
lexer->error = yajl_lex_string_invalid_escaped_char;
goto finish_string_lex;
}
+ else if (lexer->allowJson5 && curChar == '\r') {
+ STR_CHECK_EOF;
+ curChar = readChar(lexer, jsonText, offset);
+ if (curChar != '\n')
+ unreadChar(lexer, offset);
+ }
}
/* when not validating UTF8 it's a simple table lookup to determine
* if the present character is invalid */
@@ -367,36 +400,83 @@ yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText,
#define RETURN_IF_EOF if (*offset >= jsonTextLen) return yajl_tok_eof;
+/* For both identifiers and numbers, we always have to lex one
+ * character too many to know when they are complete.
+ */
+
+static yajl_tok
+yajl_lex_identifier(yajl_lexer lexer, const unsigned char * jsonText,
+ size_t jsonTextLen, size_t * offset)
+{
+ unsigned char c;
+
+ do {
+ RETURN_IF_EOF;
+ c = readChar(lexer, jsonText, offset);
+ } while (charLookupTable[c] & VIC);
+
+ /* we always go "one too far" */
+ unreadChar(lexer, offset);
+
+ return yajl_tok_identifier;
+}
+
static yajl_tok
yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText,
size_t jsonTextLen, size_t * offset)
{
- /** XXX: numbers are the only entities in json that we must lex
- * _beyond_ in order to know that they are complete. There
- * is an ambiguous case for integers at EOF. */
-
+ const char hexDigits[] = "0123456789abcdefABCDEF";
unsigned char c;
+ int numRd = 0;
yajl_tok tok = yajl_tok_integer;
RETURN_IF_EOF;
c = readChar(lexer, jsonText, offset);
- /* optional leading minus */
- if (c == '-') {
+ /* optional leading plus/minus */
+ if (c == '-' || (lexer->allowJson5 && c == '+')) {
RETURN_IF_EOF;
c = readChar(lexer, jsonText, offset);
}
- /* a single zero, or a series of integers */
+ if (c == 'I') {
+ const char * want = "nfinity";
+ do {
+ RETURN_IF_EOF;
+ c = readChar(lexer, jsonText, offset);
+ if (c != *want) {
+ unreadChar(lexer, offset);
+ lexer->error = yajl_lex_invalid_string;
+ return yajl_tok_error;
+ }
+ } while (*(++want));
+ if (!lexer->allowJson5) {
+ unreadChar(lexer, offset);
+ lexer->error = yajl_lex_unallowed_special_number;
+ return yajl_tok_error;
+ }
+ return yajl_tok_double;
+ }
+
+ /* a single zero, hex number, or a series of decimal digits */
if (c == '0') {
+ numRd++;
RETURN_IF_EOF;
c = readChar(lexer, jsonText, offset);
+ if (c == 'x' || c == 'X') {
+ if (lexer->allowJson5) goto got_hex;
+ lexer->error = yajl_lex_unallowed_hex_integer;
+ return yajl_tok_error;
+ }
} else if (c >= '1' && c <= '9') {
do {
+ numRd++;
RETURN_IF_EOF;
c = readChar(lexer, jsonText, offset);
} while (c >= '0' && c <= '9');
+ } else if (lexer->allowJson5 && c == '.') {
+ goto got_decimal;
} else {
unreadChar(lexer, offset);
lexer->error = yajl_lex_missing_integer_after_minus;
@@ -405,10 +485,10 @@ yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText,
/* optional fraction (indicates this is floating point) */
if (c == '.') {
- int numRd = 0;
-
+ got_decimal:
RETURN_IF_EOF;
c = readChar(lexer, jsonText, offset);
+ if (!lexer->allowJson5) numRd = 0;
while (c >= '0' && c <= '9') {
numRd++;
@@ -448,6 +528,25 @@ yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText,
tok = yajl_tok_double;
}
+ goto end_number;
+
+ got_hex:
+ RETURN_IF_EOF;
+ c = readChar(lexer, jsonText, offset);
+
+ if (strchr(hexDigits, c)) {
+ do {
+ RETURN_IF_EOF;
+ c = readChar(lexer, jsonText, offset);
+ } while (strchr(hexDigits, c));
+ }
+ else {
+ unreadChar(lexer, offset);
+ lexer->error = yajl_lex_missing_hex_digit_after_0x;
+ return yajl_tok_error;
+ }
+
+ end_number:
/* we always go "one too far" */
unreadChar(lexer, offset);
@@ -495,6 +594,23 @@ yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText,
return tok;
}
+/* Macro to reduce code duplication in yajl_lex_lex() */
+#define LEX_WANT(tring) \
+ const char * want = tring; \
+ do { \
+ if (*offset >= jsonTextLen) { \
+ tok = yajl_tok_eof; \
+ goto lexed; \
+ } \
+ c = readChar(lexer, jsonText, offset); \
+ if (c != *want) { \
+ unreadChar(lexer, offset); \
+ lexer->error = yajl_lex_invalid_string; \
+ tok = yajl_tok_error; \
+ goto lexed; \
+ } \
+ } while (*(++want))
+
yajl_tok
yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
size_t jsonTextLen, size_t * offset,
@@ -519,16 +635,16 @@ yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
switch (c) {
case '{':
- tok = yajl_tok_left_bracket;
+ tok = yajl_tok_left_brace;
goto lexed;
case '}':
- tok = yajl_tok_right_bracket;
+ tok = yajl_tok_right_brace;
goto lexed;
case '[':
- tok = yajl_tok_left_brace;
+ tok = yajl_tok_left_bracket;
goto lexed;
case ']':
- tok = yajl_tok_right_brace;
+ tok = yajl_tok_right_bracket;
goto lexed;
case ',':
tok = yajl_tok_comma;
@@ -540,71 +656,58 @@ yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
startOffset++;
break;
case 't': {
- const char * want = "rue";
- do {
- if (*offset >= jsonTextLen) {
- tok = yajl_tok_eof;
- goto lexed;
- }
- c = readChar(lexer, jsonText, offset);
- if (c != *want) {
- unreadChar(lexer, offset);
- lexer->error = yajl_lex_invalid_string;
- tok = yajl_tok_error;
- goto lexed;
- }
- } while (*(++want));
+ LEX_WANT("rue");
tok = yajl_tok_bool;
goto lexed;
}
case 'f': {
- const char * want = "alse";
- do {
- if (*offset >= jsonTextLen) {
- tok = yajl_tok_eof;
- goto lexed;
- }
- c = readChar(lexer, jsonText, offset);
- if (c != *want) {
- unreadChar(lexer, offset);
- lexer->error = yajl_lex_invalid_string;
- tok = yajl_tok_error;
- goto lexed;
- }
- } while (*(++want));
+ LEX_WANT("alse");
tok = yajl_tok_bool;
goto lexed;
}
case 'n': {
- const char * want = "ull";
- do {
- if (*offset >= jsonTextLen) {
- tok = yajl_tok_eof;
- goto lexed;
- }
- c = readChar(lexer, jsonText, offset);
- if (c != *want) {
- unreadChar(lexer, offset);
- lexer->error = yajl_lex_invalid_string;
- tok = yajl_tok_error;
- goto lexed;
- }
- } while (*(++want));
+ LEX_WANT("ull");
tok = yajl_tok_null;
goto lexed;
}
+ case 'I': {
+ LEX_WANT("nfinity");
+ if (!lexer->allowJson5) {
+ unreadChar(lexer, offset);
+ lexer->error = yajl_lex_unallowed_special_number;
+ tok = yajl_tok_error;
+ } else {
+ tok = yajl_tok_double;
+ }
+ goto lexed;
+ }
+ case 'N': {
+ LEX_WANT("aN");
+ if (!lexer->allowJson5) {
+ unreadChar(lexer, offset);
+ lexer->error = yajl_lex_unallowed_special_number;
+ tok = yajl_tok_error;
+ } else {
+ tok = yajl_tok_double;
+ }
+ goto lexed;
+ }
+ case '\'':
+ if (!lexer->allowJson5) goto invalid;
+ /* Fall through... */
case '"': {
- tok = yajl_lex_string(lexer, (const unsigned char *) jsonText,
- jsonTextLen, offset);
+ tok = yajl_lex_string(lexer, jsonText, jsonTextLen, offset, c);
goto lexed;
}
+ case '+': case '.':
+ if (!lexer->allowJson5)
+ goto invalid;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
/* integer parsing wants to start from the beginning */
unreadChar(lexer, offset);
- tok = yajl_lex_number(lexer, (const unsigned char *) jsonText,
- jsonTextLen, offset);
+ tok = yajl_lex_number(lexer, jsonText, jsonTextLen, offset);
goto lexed;
}
case '/':
@@ -636,6 +739,7 @@ yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
/* hit error or eof, bail */
goto lexed;
default:
+ invalid:
lexer->error = yajl_lex_invalid_char;
tok = yajl_tok_error;
goto lexed;
@@ -670,6 +774,126 @@ yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
*outLen -= 2;
}
+#ifdef YAJL_LEXER_DEBUG
+ if (tok == yajl_tok_error) {
+ printf("lexical error: %s\n",
+ yajl_lex_error_to_string(yajl_lex_get_error(lexer)));
+ } else if (tok == yajl_tok_eof) {
+ printf("EOF hit\n");
+ } else {
+ printf("lexed %s: '", tokToStr(tok));
+ fwrite(*outBuf, 1, *outLen, stdout);
+ printf("'\n");
+ }
+#endif
+
+ return tok;
+}
+
+yajl_tok yajl_lex_key(yajl_lexer lexer, const unsigned char * jsonText,
+ size_t jsonTextLen, size_t * offset,
+ const unsigned char ** outBuf, size_t * outLen)
+{
+ yajl_tok tok = yajl_tok_error;
+ unsigned char c;
+ size_t startOffset = *offset;
+
+ *outBuf = NULL;
+ *outLen = 0;
+
+ for (;;) {
+ assert(*offset <= jsonTextLen);
+
+ if (*offset >= jsonTextLen) {
+ tok = yajl_tok_eof;
+ goto lexed;
+ }
+
+ c = readChar(lexer, jsonText, offset);
+
+ switch (c) {
+ case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
+ startOffset++;
+ break;
+ case '}':
+ tok = yajl_tok_right_brace;
+ goto lexed;
+ case '\'':
+ if (!lexer->allowJson5) goto invalid;
+ /* Fall through... */
+ case '"': {
+ tok = yajl_lex_string(lexer, jsonText, jsonTextLen, offset, c);
+ goto lexed;
+ }
+ case '/':
+ /* If comments are disabled this is an error. */
+ if (!lexer->allowComments) {
+ unreadChar(lexer, offset);
+ lexer->error = yajl_lex_unallowed_comment;
+ tok = yajl_tok_error;
+ goto lexed;
+ }
+ /* Comments are enabled, so lex it.
+ * Possible outcomes are:
+ * - successful lex (tok_comment, which means continue),
+ * - malformed comment opening (slash not followed by
+ * '*' or '/') (tok_error)
+ * - eof hit. (tok_eof) */
+ tok = yajl_lex_comment(lexer, jsonText, jsonTextLen, offset);
+ if (tok == yajl_tok_comment) {
+ /* "error" is silly, but that's the initial
+ * state of tok. guilty until proven innocent. */
+ tok = yajl_tok_error;
+ yajl_buf_clear(lexer->buf);
+ lexer->bufInUse = 0;
+ startOffset = *offset;
+ break;
+ }
+ /* hit error or eof, bail */
+ goto lexed;
+ default:
+ if (lexer->allowJson5 && (c == '$' || c == '_' ||
+ (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) {
+ tok = yajl_lex_identifier(lexer, jsonText, jsonTextLen, offset);
+ }
+ else {
+ invalid:
+ lexer->error = yajl_lex_invalid_char;
+ tok = yajl_tok_error;
+ }
+ goto lexed;
+ }
+ }
+
+ lexed:
+ /* need to append to buffer if the buffer is in use or
+ * if it's an EOF token */
+ if (tok == yajl_tok_eof || lexer->bufInUse) {
+ if (!lexer->bufInUse) yajl_buf_clear(lexer->buf);
+ lexer->bufInUse = 1;
+ yajl_buf_append(lexer->buf, jsonText + startOffset, *offset - startOffset);
+ lexer->bufOff = 0;
+
+ if (tok != yajl_tok_eof) {
+ *outBuf = yajl_buf_data(lexer->buf);
+ *outLen = yajl_buf_len(lexer->buf);
+ lexer->bufInUse = 0;
+ }
+ } else if (tok != yajl_tok_error) {
+ *outBuf = jsonText + startOffset;
+ *outLen = *offset - startOffset;
+ }
+
+ /* For strings skip the quotes. */
+ if (tok == yajl_tok_string ||
+ tok == yajl_tok_string_with_escapes) {
+ assert(*outLen >= 2);
+ (*outBuf)++;
+ *outLen -= 2;
+ }
+ else if (tok == yajl_tok_identifier) {
+ tok = yajl_tok_string;
+ }
#ifdef YAJL_LEXER_DEBUG
if (tok == yajl_tok_error) {
@@ -700,9 +924,12 @@ yajl_lex_error_to_string(yajl_lex_error error)
"which it may not.";
case yajl_lex_string_invalid_json_char:
return "invalid character inside string.";
- case yajl_lex_string_invalid_hex_char:
+ case yajl_lex_string_invalid_hex_u_char:
return "invalid (non-hex) character occurs after '\\u' inside "
"string.";
+ case yajl_lex_string_invalid_hex_x_char:
+ return "invalid (non-hex) character occurs after '\\x' inside "
+ "string.";
case yajl_lex_invalid_char:
return "invalid char in json text.";
case yajl_lex_invalid_string:
@@ -714,10 +941,16 @@ yajl_lex_error_to_string(yajl_lex_error error)
"decimal point.";
case yajl_lex_missing_integer_after_minus:
return "malformed number, a digit is required after the "
- "minus sign.";
+ "plus/minus sign.";
case yajl_lex_unallowed_comment:
return "probable comment found in input text, comments are "
"not enabled.";
+ case yajl_lex_missing_hex_digit_after_0x:
+ return "malformed number, a hex digit is required after the 0x/0X.";
+ case yajl_lex_unallowed_hex_integer:
+ return "probable hex number found, JSON5 is not enabled.";
+ case yajl_lex_unallowed_special_number:
+ return "special number Infinity or NaN found, JSON5 is not enabled.";
}
return "unknown error code";
}
diff --git a/src/yajl_lex.h b/src/yajl_lex.h
index fd17c00..8363de9 100644
--- a/src/yajl_lex.h
+++ b/src/yajl_lex.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,7 +17,7 @@
#ifndef __YAJL_LEX_H__
#define __YAJL_LEX_H__
-#include "api/yajl_common.h"
+#include "yajl_common.h"
typedef enum {
yajl_tok_bool,
@@ -41,27 +41,37 @@ typedef enum {
yajl_tok_string,
yajl_tok_string_with_escapes,
- /* comment tokens are not currently returned to the parser, ever */
+ /* These tokens are used within the lexer and never seen by the parser: */
+
+ /* An unquoted map key, for JSON5 only, returned as yajl_tok_string */
+ yajl_tok_identifier,
+
+ /* A comment token, never returned */
yajl_tok_comment
} yajl_tok;
typedef struct yajl_lexer_t * yajl_lexer;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc,
unsigned int allowComments,
- unsigned int validateUTF8);
+ unsigned int validateUTF8,
+ unsigned int allowJson5);
void yajl_lex_free(yajl_lexer lexer);
/**
* run/continue a lex. "offset" is an input/output parameter.
* It should be initialized to zero for a
- * new chunk of target text, and upon subsetquent calls with the same
+ * new chunk of target text, and upon subsequent calls with the same
* target text should passed with the value of the previous invocation.
*
* the client may be interested in the value of offset when an error is
* returned from the lexer. This allows the client to render useful
- * error messages.
+n * error messages.
*
* When you pass the next chunk of data, context should be reinitialized
* to zero.
@@ -79,6 +89,14 @@ yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
size_t jsonTextLen, size_t * offset,
const unsigned char ** outBuf, size_t * outLen);
+/**
+ * A specialized version of yajl_lex_lex for use when the next token is
+ * a map key, which the parser knows.
+ */
+yajl_tok yajl_lex_key(yajl_lexer lexer, const unsigned char * jsonText,
+ size_t jsonTextLen, size_t * offset,
+ const unsigned char ** outBuf, size_t * outLen);
+
/** have a peek at the next token, but don't move the lexer forward */
yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText,
size_t jsonTextLen, size_t offset);
@@ -89,13 +107,17 @@ typedef enum {
yajl_lex_string_invalid_utf8,
yajl_lex_string_invalid_escaped_char,
yajl_lex_string_invalid_json_char,
- yajl_lex_string_invalid_hex_char,
+ yajl_lex_string_invalid_hex_u_char,
+ yajl_lex_string_invalid_hex_x_char,
yajl_lex_invalid_char,
yajl_lex_invalid_string,
yajl_lex_missing_integer_after_decimal,
yajl_lex_missing_integer_after_exponent,
yajl_lex_missing_integer_after_minus,
- yajl_lex_unallowed_comment
+ yajl_lex_unallowed_comment,
+ yajl_lex_missing_hex_digit_after_0x,
+ yajl_lex_unallowed_hex_integer,
+ yajl_lex_unallowed_special_number,
} yajl_lex_error;
const char * yajl_lex_error_to_string(yajl_lex_error error);
@@ -114,4 +136,8 @@ size_t yajl_lex_current_line(yajl_lexer lexer);
* \n or \r */
size_t yajl_lex_current_char(yajl_lexer lexer);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/yajl_parser.c b/src/yajl_parser.c
index 1a528a6..0627215 100644
--- a/src/yajl_parser.c
+++ b/src/yajl_parser.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -14,12 +14,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "api/yajl_parse.h"
-#include "yajl_lex.h"
-#include "yajl_parser.h"
-#include "yajl_encode.h"
-#include "yajl_bytestack.h"
-
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
@@ -29,33 +23,65 @@
#include <assert.h>
#include <math.h>
-#define MAX_VALUE_TO_MULTIPLY ((LLONG_MAX / 10) + (LLONG_MAX % 10))
+#include "yajl_parse.h"
+#include "yajl_lex.h"
+#include "yajl_parser.h"
+#include "yajl_encode.h"
+#include "yajl_bytestack.h"
+
+#include <epicsStdlib.h>
+
+#ifndef LLONG_MAX
+#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
+#define LLONG_MIN (-0x7FFFFFFFFFFFFFFFLL - 1)
+#endif
- /* same semantics as strtol */
long long
-yajl_parse_integer(const unsigned char *number, unsigned int length)
+yajl_parse_integer(const unsigned char *number, size_t length)
{
long long ret = 0;
long sign = 1;
+ long base = 10;
+ long long max = LLONG_MAX / base;
const unsigned char *pos = number;
- if (*pos == '-') { pos++; sign = -1; }
- if (*pos == '+') { pos++; }
+ const unsigned char *end = number + length;
- while (pos < number + length) {
- if ( ret > MAX_VALUE_TO_MULTIPLY ) {
- errno = ERANGE;
- return sign == 1 ? LLONG_MAX : LLONG_MIN;
- }
- ret *= 10;
- if (LLONG_MAX - ret < (*pos - '0')) {
+ if (*pos == '-') {
+ pos++;
+ sign = -1;
+ }
+ else if (*pos == '+') {
+ pos++;
+ }
+
+ if (*pos == '0' &&
+ (pos[1] == 'x' || pos[1] == 'X')) {
+ base = 16;
+ max = LLONG_MAX / base;
+ pos += 2;
+ }
+
+ while (pos < end) {
+ int digit;
+
+ if (ret > max) {
errno = ERANGE;
return sign == 1 ? LLONG_MAX : LLONG_MIN;
}
- if (*pos < '0' || *pos > '9') {
+
+ ret *= base;
+ digit = *pos++ - '0';
+ /* Don't have to check for non-digit characters,
+ * the lexer has already rejected any bad digits.
+ */
+ if (digit > 9)
+ digit = (digit - ('A' - '0') + 10) & 0xf;
+
+ if (LLONG_MAX - ret < digit) {
errno = ERANGE;
return sign == 1 ? LLONG_MAX : LLONG_MIN;
}
- ret += (*pos++ - '0');
+ ret += digit;
}
return sign * ret;
@@ -145,7 +171,7 @@ yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
return str;
}
-/* check for client cancelation */
+/* check for client cancellation */
#define _CC_CHK(x) \
if (!(x)) { \
yajl_bs_set(hand->stateStack, yajl_state_parse_error); \
@@ -223,7 +249,7 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
/* for arrays and maps, we advance the state for this
* depth, then push the state of the next depth.
* If an error occurs during the parsing of the nesting
- * enitity, the state at this level will not matter.
+ * entity, the state at this level will not matter.
* a state that needs pushing will be anything other
* than state_start */
@@ -264,13 +290,13 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
_CC_CHK(hand->callbacks->yajl_null(hand->ctx));
}
break;
- case yajl_tok_left_bracket:
+ case yajl_tok_left_brace:
if (hand->callbacks && hand->callbacks->yajl_start_map) {
_CC_CHK(hand->callbacks->yajl_start_map(hand->ctx));
}
stateToPush = yajl_state_map_start;
break;
- case yajl_tok_left_brace:
+ case yajl_tok_left_bracket:
if (hand->callbacks && hand->callbacks->yajl_start_array) {
_CC_CHK(hand->callbacks->yajl_start_array(hand->ctx));
}
@@ -283,7 +309,6 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
hand->ctx,(const char *) buf, bufLen));
} else if (hand->callbacks->yajl_integer) {
long long int i = 0;
- errno = 0;
i = yajl_parse_integer(buf, bufLen);
if ((i == LLONG_MIN || i == LLONG_MAX) &&
errno == ERANGE)
@@ -311,8 +336,7 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
yajl_buf_clear(hand->decodeBuf);
yajl_buf_append(hand->decodeBuf, buf, bufLen);
buf = yajl_buf_data(hand->decodeBuf);
- errno = 0;
- d = strtod((char *) buf, NULL);
+ d = epicsStrtod((char *) buf, NULL);
if ((d == HUGE_VAL || d == -HUGE_VAL) &&
errno == ERANGE)
{
@@ -330,9 +354,11 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
}
}
break;
- case yajl_tok_right_brace: {
- if (yajl_bs_current(hand->stateStack) ==
- yajl_state_array_start)
+ case yajl_tok_right_bracket: {
+ yajl_state s = yajl_bs_current(hand->stateStack);
+ if (s == yajl_state_array_start ||
+ ((hand->flags & yajl_allow_json5) &&
+ (s == yajl_state_array_need_val)))
{
if (hand->callbacks &&
hand->callbacks->yajl_end_array)
@@ -346,7 +372,7 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
}
case yajl_tok_colon:
case yajl_tok_comma:
- case yajl_tok_right_bracket:
+ case yajl_tok_right_brace:
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
hand->parseError =
"unallowed token at this point in JSON text";
@@ -377,8 +403,8 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
case yajl_state_map_need_key: {
/* only difference between these two states is that in
* start '}' is valid, whereas in need_key, we've parsed
- * a comma, and a string key _must_ follow */
- tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
+ * a comma, so unless this is JSON5 a key _must_ follow. */
+ tok = yajl_lex_key(hand->lexer, jsonText, jsonTextLen,
offset, &buf, &bufLen);
switch (tok) {
case yajl_tok_eof:
@@ -401,20 +427,23 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
}
yajl_bs_set(hand->stateStack, yajl_state_map_sep);
goto around_again;
- case yajl_tok_right_bracket:
- if (yajl_bs_current(hand->stateStack) ==
- yajl_state_map_start)
- {
+ case yajl_tok_right_brace: {
+ yajl_state s = yajl_bs_current(hand->stateStack);
+ if (s == yajl_state_map_start ||
+ ((hand->flags & yajl_allow_json5) &&
+ (s == yajl_state_map_need_key))) {
if (hand->callbacks && hand->callbacks->yajl_end_map) {
_CC_CHK(hand->callbacks->yajl_end_map(hand->ctx));
}
yajl_bs_pop(hand->stateStack);
goto around_again;
}
+ }
default:
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
- hand->parseError =
- "invalid object key (must be a string)";
+ hand->parseError = hand->flags & yajl_allow_json5 ?
+ "invalid object key (must be a string or identifier)" :
+ "invalid object key (must be a string)";
goto around_again;
}
}
@@ -441,7 +470,7 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
offset, &buf, &bufLen);
switch (tok) {
- case yajl_tok_right_bracket:
+ case yajl_tok_right_brace:
if (hand->callbacks && hand->callbacks->yajl_end_map) {
_CC_CHK(hand->callbacks->yajl_end_map(hand->ctx));
}
@@ -469,7 +498,7 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
offset, &buf, &bufLen);
switch (tok) {
- case yajl_tok_right_brace:
+ case yajl_tok_right_bracket:
if (hand->callbacks && hand->callbacks->yajl_end_array) {
_CC_CHK(hand->callbacks->yajl_end_array(hand->ctx));
}
@@ -495,4 +524,3 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
abort();
return yajl_status_error;
}
-
diff --git a/src/yajl_parser.h b/src/yajl_parser.h
index c79299a..0cf9e98 100644
--- a/src/yajl_parser.h
+++ b/src/yajl_parser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014, Lloyd Hilaiel <m...@lloyd.io>
+ * Copyright (c) 2007-2011, Lloyd Hilaiel <ll...@hilaiel.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,7 +17,7 @@
#ifndef __YAJL_PARSER_H__
#define __YAJL_PARSER_H__
-#include "api/yajl_parse.h"
+#include "yajl_parse.h"
#include "yajl_bytestack.h"
#include "yajl_buf.h"
#include "yajl_lex.h"
@@ -58,6 +58,10 @@ struct yajl_handle_t {
unsigned int flags;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
yajl_status
yajl_do_parse(yajl_handle handle, const unsigned char * jsonText,
size_t jsonTextLen);
@@ -72,7 +76,10 @@ yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
/* A little built in integer parsing routine with the same semantics as strtol
* that's unaffected by LOCALE. */
long long
-yajl_parse_integer(const unsigned char *number, unsigned int length);
+yajl_parse_integer(const unsigned char *number, size_t length);
+#ifdef __cplusplus
+}
+#endif
#endif