summaryrefslogtreecommitdiff
path: root/json.c
diff options
context:
space:
mode:
Diffstat (limited to 'json.c')
-rw-r--r--json.c497
1 files changed, 73 insertions, 424 deletions
diff --git a/json.c b/json.c
index 748eeac..ec4e0ca 100644
--- a/json.c
+++ b/json.c
@@ -26,34 +26,6 @@
#include <ctype.h>
-#define JSON_malloc palloc
-
-/* repalloc and pfree can't take a null pointer, unlike normal realloc and free. */
-static void *
-JSON_realloc(void *ptr, Size size)
-{
- if (ptr)
- return repalloc(ptr, size);
- else
- return palloc(size);
-}
-static void
-JSON_free(void *ptr)
-{
- if (ptr)
- pfree(ptr);
-}
-
-static char *
-JSON_strdup(const char *str, size_t length)
-{
- char *ret = JSON_malloc(length + 1);
-
- memcpy(ret, str, length);
- ret[length] = 0;
- return ret;
-}
-
#define is_internal(node) ((node)->type == JSON_ARRAY || (node)->type == JSON_OBJECT)
/* We can't use isspace() because it also accepts \v and \f, which
@@ -135,87 +107,12 @@ static void utf8_decode_char_nocheck(const char **sp, unsigned int *uc);
static int utf8_encode_char(char *out, unsigned int uc);
-/*************************** String buffer ***************************/
-
-typedef struct
-{
- char *buffer;
- size_t length;
- size_t alloc;
-} String[1];
-
-/* Declare and initialize a String with the given name. */
-#define String(name) String name = NewString()
-
-#define NewString() {{NULL, 0, 0}}
-
-/* Grow the string by @need characters, reallocating if necessary.
- * Returns a pointer to the uninitialized range where text is to go.
- * A '\0' terminator is added automatically. */
-static char *
-string_grow(String str, size_t need)
-{
- size_t end = str->length;
-
- str->length += need;
- if (str->alloc <= str->length)
- {
- str->alloc = str->length * 3 / 2 + 1;
- if (str->alloc < 8)
- str->alloc = 8;
- str->buffer = JSON_realloc(str->buffer, str->alloc);
- }
- str->buffer[str->length] = '\0';
- return str->buffer + end;
-}
-static char *
-string_buffer(String str)
-{
- if (!str->buffer)
- string_grow(str, 0);
- return str->buffer;
-}
-static inline void
-string_append_length(String str, const char *append, size_t len)
-{
- char *dest = string_grow(str, len);
-
- memcpy(dest, append, len);
-}
-static inline void
-string_append(String str, const char *append)
-{
- string_append_length(str, append, strlen(append));
-}
-static inline void
-string_append_range(String str, const char *start, const char *end)
-{
- string_append_length(str, start, end - start);
-}
-static inline void
-string_append_char(String str, char c)
-{
- *string_grow(str, 1) = c;
-}
-static inline void
-string_trunc(String str, size_t len)
-{
- str->length = len;
- str->buffer[len] = '\0';
-}
-static inline void
-string_free(String str)
-{
- JSON_free(str->buffer);
-}
-
-
/*********** json_node creation, manipulation, and deletion **********/
json_node *
json_mknode(json_type type)
{
- json_node *node = JSON_malloc(sizeof(*node));
+ json_node *node = palloc(sizeof(*node));
memset(node, 0, sizeof(*node));
node->type = type;
@@ -238,7 +135,7 @@ json_mkstring(const char *str, size_t length)
if (str)
{
- node->v.string.str = JSON_strdup(str, length);
+ node->v.string.str = pnstrdup(str, length);
node->v.string.length = length;
}
return node;
@@ -250,7 +147,7 @@ json_mknumber(const char *number, size_t length)
json_node *node = json_mknode(JSON_NUMBER);
if (number)
- node->v.number = JSON_strdup(number, length);
+ node->v.number = pnstrdup(number, length);
return node;
}
@@ -351,10 +248,10 @@ json_set_string(json_node * node, const char *str, size_t length)
{
Assert(node->type == JSON_STRING);
if (node->v.string.str)
- JSON_free(node->v.string.str);
+ pfree(node->v.string.str);
if (str)
{
- node->v.string.str = JSON_strdup(str, length);
+ node->v.string.str = pnstrdup(str, length);
node->v.string.length = length;
}
else
@@ -377,9 +274,9 @@ json_set_number(json_node * node, const char *number, size_t length)
{
Assert(node->type == JSON_NUMBER);
if (node->v.number)
- JSON_free(node->v.number);
+ pfree(node->v.number);
if (number)
- node->v.number = JSON_strdup(number, length);
+ node->v.number = pnstrdup(number, length);
else
node->v.number = NULL;
json_touch_value(node);
@@ -390,12 +287,20 @@ static void
free_node(json_node * node)
{
if (node->type == JSON_STRING)
- JSON_free(node->v.string.str);
+ {
+ if (node->v.string.str)
+ pfree(node->v.string.str);
+ }
else if (node->type == JSON_NUMBER)
- JSON_free(node->v.number);
+ {
+ if (node->v.number)
+ pfree(node->v.number);
+ }
+
if (node->key)
- JSON_free(node->key);
- JSON_free(node);
+ pfree(node->key);
+
+ pfree(node);
}
void
@@ -635,7 +540,7 @@ end: /* Expect end of text */
failed: /* Handle failure */
if (key)
- JSON_free(key);
+ pfree(key);
json_delete(root);
return NULL;
}
@@ -767,15 +672,16 @@ decode_number(const char **sp)
char *
json_decode_string(const char **sp, size_t *length, bool strict)
{
- const char *s = *sp;
-
- String(ret);
- char *out;
- size_t size;
- char quote;
+ const char *s = *sp;
+ StringInfoData ret;
+ char buf[4];
+ int len;
+ char quote;
Assert(length != NULL);
+ initStringInfo(&ret);
+
quote = *s++;
if (strict)
{
@@ -791,8 +697,8 @@ json_decode_string(const char **sp, size_t *length, bool strict)
while (*s && *s != quote)
{
unsigned char c = *s++;
- unsigned int uc,
- lc;
+ unsigned int uc;
+ unsigned int lc;
if (c == '\\')
{
@@ -818,9 +724,6 @@ json_decode_string(const char **sp, size_t *length, bool strict)
c = '\t';
break;
case 'u':
- size = ret->length;
- out = string_grow(ret, 4);
-
if (!read_hex16(s, &uc))
goto failed;
s += 4;
@@ -850,8 +753,8 @@ json_decode_string(const char **sp, size_t *length, bool strict)
if (uc == 0xFFFE || uc == 0xFFFF)
goto failed;
- size += utf8_encode_char(out, uc);
- string_trunc(ret, size);
+ len = utf8_encode_char(buf, uc);
+ appendBinaryStringInfo(&ret, buf, len);
continue; /* Continue the enclosing while loop to skip
* the str_append below. */
@@ -868,18 +771,18 @@ json_decode_string(const char **sp, size_t *length, bool strict)
/* Control characters not allowed in string literals. */
goto failed;
}
- string_append_char(ret, c);
+ appendStringInfoChar(&ret, c);
}
if (!*s++)
goto failed;
- *length = ret->length;
+ *length = ret.len;
*sp = s;
- return string_buffer(ret);
+ return ret.data;
failed:
- string_free(ret);
+ pfree(ret.data);
return NULL;
}
@@ -923,7 +826,7 @@ json_text_type(const char *str, size_t nbytes)
/****************************** Encoding *****************************/
static bool
-encode_string(String out, const char *string, size_t length, char quote, bool escape_unicode)
+encode_string(StringInfo out, const char *string, size_t length, char quote, bool escape_unicode)
{
const char *s = string;
const char *e = s + length;
@@ -931,7 +834,7 @@ encode_string(String out, const char *string, size_t length, char quote, bool es
if (!utf8_validate(string, length) || quote == '\\')
return false;
- string_append_char(out, quote);
+ appendStringInfoChar(out, quote);
while (s < e)
{
@@ -996,25 +899,25 @@ encode_string(String out, const char *string, size_t length, char quote, bool es
txt[12] = '\0';
}
- string_append(out, txt);
+ appendStringInfoString(out, txt);
continue; /* Skip backslash-encoding code below. */
}
e = 0;
}
}
- string_append_char(out, e ? '\\' : c);
+ appendStringInfoChar(out, e ? '\\' : c);
if (e)
- string_append_char(out, e);
+ appendStringInfoChar(out, e);
}
- string_append_char(out, quote);
+ appendStringInfoChar(out, quote);
return true;
}
static bool
-encode_number(String out, const char *string)
+encode_number(StringInfo out, const char *string)
{
const char *s = string;
const char *start,
@@ -1034,17 +937,17 @@ encode_number(String out, const char *string)
return false;
/* Append number to out */
- string_append_length(out, start, end - start);
+ appendBinaryStringInfo(out, start, end - start);
return true;
}
typedef struct
{
- String str;
- bool use_orig;
- bool escape_unicode;
- bool trim;
+ StringInfoData str;
+ bool use_orig;
+ bool escape_unicode;
+ bool trim;
} json_encode_ctx;
static bool json_encode_recurse(json_node * node, json_encode_ctx * ctx);
@@ -1052,19 +955,20 @@ static bool json_encode_recurse(json_node * node, json_encode_ctx * ctx);
char *
json_encode(json_node * node, int options)
{
- json_encode_ctx ctx = {
- NewString(),
- !!(options & JSONOPT_USE_ORIG),
- !!(options & JSONOPT_ESCAPE_UNICODE),
- !(options & JSONOPT_NO_TRIM)};
+ json_encode_ctx ctx;
+
+ initStringInfo(&ctx.str);
+ ctx.use_orig = !!(options & JSONOPT_USE_ORIG);
+ ctx.escape_unicode = !!(options & JSONOPT_ESCAPE_UNICODE);
+ ctx.trim = !(options & JSONOPT_NO_TRIM);
if (!json_encode_recurse(node, &ctx))
{
- string_free(ctx.str);
+ pfree(ctx.str.data);
return NULL;
}
- return string_buffer(ctx.str);
+ return ctx.str.data;
}
static bool
@@ -1073,7 +977,8 @@ json_encode_recurse(json_node * node, json_encode_ctx * ctx)
#define has_orig(field) \
(use_orig && node->orig.field.start)
#define push_orig(field) \
- string_append_range(ctx->str, node->orig.field.start, node->orig.field.end)
+ appendBinaryStringInfo(&ctx->str, node->orig.field.start, \
+ node->orig.field.end - node->orig.field.start)
bool use_orig = ctx->use_orig;
bool trim = ctx->trim;
@@ -1105,7 +1010,7 @@ json_encode_recurse(json_node * node, json_encode_ctx * ctx)
txt = "false";
break;
case JSON_STRING:
- if (!encode_string(ctx->str,
+ if (!encode_string(&ctx->str,
node->v.string.str,
node->v.string.length,
'"',
@@ -1113,23 +1018,23 @@ json_encode_recurse(json_node * node, json_encode_ctx * ctx)
return false;
break;
case JSON_NUMBER:
- if (!encode_number(ctx->str, node->v.number))
+ if (!encode_number(&ctx->str, node->v.number))
return false;
break;
case JSON_ARRAY:
- string_append_char(ctx->str, '[');
+ appendStringInfoChar(&ctx->str, '[');
json_foreach(child, node)
{
json_encode_recurse(child, ctx);
if (child->next)
- string_append_char(ctx->str, ',');
+ appendStringInfoChar(&ctx->str, ',');
}
- string_append_char(ctx->str, ']');
+ appendStringInfoChar(&ctx->str, ']');
break;
case JSON_OBJECT:
- string_append_char(ctx->str, '{');
+ appendStringInfoChar(&ctx->str, '{');
json_foreach(child, node)
{
@@ -1149,7 +1054,7 @@ json_encode_recurse(json_node * node, json_encode_ctx * ctx)
}
else
{
- if (!encode_string(ctx->str,
+ if (!encode_string(&ctx->str,
node->key,
node->key_length,
'"',
@@ -1160,22 +1065,22 @@ json_encode_recurse(json_node * node, json_encode_ctx * ctx)
if (has_orig(key_right_space))
push_orig(key_right_space);
- string_append_char(ctx->str, ':');
+ appendStringInfoChar(&ctx->str, ':');
json_encode_recurse(node, ctx);
if (node->next)
- string_append_char(ctx->str, ',');
+ appendStringInfoChar(&ctx->str, ',');
}
- string_append_char(ctx->str, '}');
+ appendStringInfoChar(&ctx->str, '}');
break;
default:
return false;
}
if (txt)
- string_append(ctx->str, txt);
+ appendStringInfoString(&ctx->str, txt);
}
if (!trim && has_orig(right_space))
@@ -1190,272 +1095,16 @@ json_encode_recurse(json_node * node, json_encode_ctx * ctx)
char *
json_encode_string(const char *str, size_t length, char quote, bool escape_unicode)
{
- String(ret);
+ StringInfoData ret;
- if (!encode_string(ret, str, length, quote, escape_unicode))
+ initStringInfo(&ret);
+ if (!encode_string(&ret, str, length, quote, escape_unicode))
{
- string_free(ret);
- return NULL;
- }
-
- return string_buffer(ret);
-}
-
-
-/************************ Liberal JSON support ***********************/
-
-bool
-json_validate_liberal(const char *str)
-{
- char *cleaned = json_cleanup(str);
- json_node *node = json_decode(cleaned);
- bool ret = !!node;
-
- if (node)
- json_delete(node);
- if (cleaned)
- JSON_free(cleaned);
-
- return ret;
-}
-
-char *
-json_cleanup(const char *str)
-{
- String(ret);
- const char *p = str;
- const char *s = str;
- int comment_start_width = 0;
- char quote_char = 0;
-
- /*
- * flush(): flush content we have scanned, meaning append characters from
- * p thru s to ret, then set p to s.
- */
-#define flush() do { \
- string_append_length(ret, p, s-p); \
- p = s; \
- } while(0)
-
- if (!str)
+ pfree(ret.data);
return NULL;
-
- goto begin;
-
-begin:
- for (; *s; s++)
- {
- if (*s == '"' || *s == '\'')
- goto quote;
- if (isdigit(*s) || *s == '-' || *s == '+' || *s == '.')
- goto number;
- if (s[0] == '#')
- {
- comment_start_width = 1;
- goto line_comment;
- }
- if (s[0] == '/' && s[1] == '/')
- {
- comment_start_width = 2;
- goto line_comment;
- }
- if (s[0] == '/' && s[1] == '*')
- {
- comment_start_width = 2;
- goto c_comment;
- }
- }
- flush();
- return ret->buffer;
-
-quote:
- quote_char = *s;
- if (*s == '\'')
- {
- flush();
- string_append_char(ret, '"');
- p = s = s + 1;
}
- else
- {
- s++;
- }
- while (*s)
- {
- if (*s == quote_char)
- {
- if (*s == '\'')
- {
- flush();
- string_append_char(ret, '"');
- p = s = s + 1;
- }
- else
- {
- s++;
- }
- break;
- }
- else if (*s == '"')
- {
- /*
- * We're converting single quotes to double quotes, so double
- * quotes need to be automatically escaped.
- */
- flush();
- string_append_char(ret, '\\');
- s++;
- }
- else if (*s == '\\')
- {
- s++;
- switch (*s)
- {
- case '\0':
- break;
- case '\'':
- /* Convert \' to \u0027 */
- flush();
- string_append(ret, "u0027");
- p = s = s + 1;
- break;
- default:
- s++;
- }
- }
- else
- {
- s++;
- }
- }
- goto begin;
-
-number:
- /* Skip a '-', or remove a '+' if present. */
- if (*s == '-')
- {
- s++;
- }
- else if (*s == '+')
- {
- flush();
- p = s = s + 1;
- }
- /* Make sure number has at least one digit. */
- if (!isdigit(*s))
- {
- if (*s != '.')
- goto failed;
- if (!isdigit(s[1]))
- goto failed;
- }
-
- /*
- * Make sure that if first digit before '.' is '0', that it is the only
- * digit. Leading 0s are not allowed, and for a good reason: to avoid
- * ambiguity between octal and decimal formats.
- */
- if (*s == '0')
- {
- s++;
- if (isdigit(*s))
- goto failed;
- goto frac;
- }
- /* Skip digits, or add a '0' if none are present. */
- if (isdigit(*s))
- {
- do
- s++;
- while (isdigit(*s));
- }
- else
- {
- flush();
- string_append_char(ret, '0');
- }
- goto frac;
-
-frac:
- if (*s == '.')
- {
- s++;
- if (isdigit(*s))
- {
- do
- s++;
- while (isdigit(*s));
- }
- else
- {
- flush();
- string_append_char(ret, '0');
- }
- }
-/* exp: */
- if (*s == 'E' || *s == 'e')
- {
- s++;
- if (*s == '+' || *s == '-')
- s++;
- if (!isdigit(*s))
- goto failed;
- do
- s++;
- while (isdigit(*s));
- }
-
- /*
- * The isdigit check is not needed, but here for clarity and safety.
- */
- if (isdigit(*s) || *s == '-' || *s == '+' || *s == '.')
- goto failed;
- goto begin;
-
-line_comment: /* Remove all characters up to newline */
- flush();
- s += comment_start_width;
- /* Skip characters up to newline */
- while (*s && !(*s == '\n' || *s == '\r'))
- s++;
- /* Skip newline character and its complement (if present) */
- if (s[0])
- {
- if (s[1] == '\n' + '\r' - s[0])
- s++;
- s++;
- }
-
- /*
- * Set begin marker so characters skipped are not appended to output on
- * next flush.
- */
- p = s;
- goto begin;
-
-c_comment: /* Remove all characters up to star-slash */
- flush();
- s += comment_start_width;
- /* Skip characters up to and including star-slash */
- while (*s && !(s[0] == '*' && s[1] == '/'))
- s++;
- if (*s)
- s += 2;
- else
- goto failed; /* No star-slash present */
-
- /*
- * Set begin marker so characters skipped are not appended to output on
- * next flush.
- */
- p = s;
- goto begin;
-
-failed:
- string_free(ret);
- return NULL;
-#undef flush
+ return ret.data;
}