diff options
| author | Joey Adams | 2010-07-24 01:41:04 +0000 |
|---|---|---|
| committer | Joey Adams | 2010-07-24 01:41:04 +0000 |
| commit | b32257221b4b8e15fada7aabd0fe6e129e00d3e8 (patch) | |
| tree | cffb5336f46610208fff4f4a43c6f179f1a5990d /json.c | |
| parent | bc9e250d090da889a3e1e1eb82b5dfc0a32bc52e (diff) | |
* Removed the string buffer code in json.c and used StringInfo instead.
* Removed json_cleanup and json_validate_liberal.
json_cleanup was badly in need of a rewrite.
Diffstat (limited to 'json.c')
| -rw-r--r-- | json.c | 497 |
1 files changed, 73 insertions, 424 deletions
@@ -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; } |
