|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com)
|
|
Packit |
bfcc33 |
All rights reserved.
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
Packit |
bfcc33 |
of this software and associated documentation files (the "Software"), to deal
|
|
Packit |
bfcc33 |
in the Software without restriction, including without limitation the rights
|
|
Packit |
bfcc33 |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
Packit |
bfcc33 |
copies of the Software, and to permit persons to whom the Software is
|
|
Packit |
bfcc33 |
furnished to do so, subject to the following conditions:
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
The above copyright notice and this permission notice shall be included in
|
|
Packit |
bfcc33 |
all copies or substantial portions of the Software.
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
Packit |
bfcc33 |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
Packit |
bfcc33 |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
Packit |
bfcc33 |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
Packit |
bfcc33 |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
Packit |
bfcc33 |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
Packit |
bfcc33 |
THE SOFTWARE.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
#ifdef _MSC_VER
|
|
Packit |
bfcc33 |
#define _CRT_SECURE_NO_WARNINGS
|
|
Packit |
bfcc33 |
#define _CRT_NONSTDC_NO_DEPRECATE
|
|
Packit |
bfcc33 |
#endif
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
#include "json.hpp"
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// include utf8 library used by libsass
|
|
Packit |
bfcc33 |
// ToDo: replace internal json utf8 code
|
|
Packit |
bfcc33 |
#include "utf8.h"
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
#include <assert.h>
|
|
Packit |
bfcc33 |
#include <stdint.h>
|
|
Packit |
bfcc33 |
#include <stdio.h>
|
|
Packit |
bfcc33 |
#include <stdlib.h>
|
|
Packit |
bfcc33 |
#include <string.h>
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
Packit |
bfcc33 |
#include <stdarg.h>
|
|
Packit |
bfcc33 |
#ifdef snprintf
|
|
Packit |
bfcc33 |
#undef snprintf
|
|
Packit |
bfcc33 |
#endif
|
|
Packit |
bfcc33 |
extern "C" int snprintf(char *, size_t, const char *, ...);
|
|
Packit |
bfcc33 |
#endif
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
#define out_of_memory() do { \
|
|
Packit |
bfcc33 |
fprintf(stderr, "Out of memory.\n"); \
|
|
Packit |
bfcc33 |
exit(EXIT_FAILURE); \
|
|
Packit |
bfcc33 |
} while (0)
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Sadly, strdup is not portable. */
|
|
Packit |
bfcc33 |
static char *json_strdup(const char *str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
char *ret = (char*) malloc(strlen(str) + 1);
|
|
Packit |
bfcc33 |
if (ret == NULL)
|
|
Packit |
bfcc33 |
out_of_memory();
|
|
Packit |
bfcc33 |
strcpy(ret, str);
|
|
Packit |
bfcc33 |
return ret;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* String buffer */
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
typedef struct
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
char *cur;
|
|
Packit |
bfcc33 |
char *end;
|
|
Packit |
bfcc33 |
char *start;
|
|
Packit |
bfcc33 |
} SB;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void sb_init(SB *sb)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
sb->start = (char*) malloc(17);
|
|
Packit |
bfcc33 |
if (sb->start == NULL)
|
|
Packit |
bfcc33 |
out_of_memory();
|
|
Packit |
bfcc33 |
sb->cur = sb->start;
|
|
Packit |
bfcc33 |
sb->end = sb->start + 16;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* sb and need may be evaluated multiple times. */
|
|
Packit |
bfcc33 |
#define sb_need(sb, need) do { \
|
|
Packit |
bfcc33 |
if ((sb)->end - (sb)->cur < (need)) \
|
|
Packit |
bfcc33 |
sb_grow(sb, need); \
|
|
Packit |
bfcc33 |
} while (0)
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void sb_grow(SB *sb, int need)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
size_t length = sb->cur - sb->start;
|
|
Packit |
bfcc33 |
size_t alloc = sb->end - sb->start;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
do {
|
|
Packit |
bfcc33 |
alloc *= 2;
|
|
Packit |
bfcc33 |
} while (alloc < length + need);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
sb->start = (char*) realloc(sb->start, alloc + 1);
|
|
Packit |
bfcc33 |
if (sb->start == NULL)
|
|
Packit |
bfcc33 |
out_of_memory();
|
|
Packit |
bfcc33 |
sb->cur = sb->start + length;
|
|
Packit |
bfcc33 |
sb->end = sb->start + alloc;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void sb_put(SB *sb, const char *bytes, int count)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
sb_need(sb, count);
|
|
Packit |
bfcc33 |
memcpy(sb->cur, bytes, count);
|
|
Packit |
bfcc33 |
sb->cur += count;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
#define sb_putc(sb, c) do { \
|
|
Packit |
bfcc33 |
if ((sb)->cur >= (sb)->end) \
|
|
Packit |
bfcc33 |
sb_grow(sb, 1); \
|
|
Packit |
bfcc33 |
*(sb)->cur++ = (c); \
|
|
Packit |
bfcc33 |
} while (0)
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void sb_puts(SB *sb, const char *str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
sb_put(sb, str, (int)strlen(str));
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static char *sb_finish(SB *sb)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
*sb->cur = 0;
|
|
Packit |
bfcc33 |
assert(sb->start <= sb->cur && strlen(sb->start) == (size_t)(sb->cur - sb->start));
|
|
Packit |
bfcc33 |
return sb->start;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void sb_free(SB *sb)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
free(sb->start);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Unicode helper functions
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* These are taken from the ccan/charset module and customized a bit.
|
|
Packit |
bfcc33 |
* Putting them here means the compiler can (choose to) inline them,
|
|
Packit |
bfcc33 |
* and it keeps ccan/json from having a dependency.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* We use uint32_t Type for Unicode codepoints.
|
|
Packit |
bfcc33 |
* We need our own because wchar_t might be 16 bits.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Validate a single UTF-8 character starting at @s.
|
|
Packit |
bfcc33 |
* The string must be null-terminated.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* If it's valid, return its length (1 thru 4).
|
|
Packit |
bfcc33 |
* If it's invalid or clipped, return 0.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* This function implements the syntax given in RFC3629, which is
|
|
Packit |
bfcc33 |
* the same as that given in The Unicode Standard, Version 6.0.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* It has the following properties:
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* * All codepoints U+0000..U+10FFFF may be encoded,
|
|
Packit |
bfcc33 |
* except for U+D800..U+DFFF, which are reserved
|
|
Packit |
bfcc33 |
* for UTF-16 surrogate pair encoding.
|
|
Packit |
bfcc33 |
* * UTF-8 byte sequences longer than 4 bytes are not permitted,
|
|
Packit |
bfcc33 |
* as they exceed the range of Unicode.
|
|
Packit |
bfcc33 |
* * The sixty-six Unicode "non-characters" are permitted
|
|
Packit |
bfcc33 |
* (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF).
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
static int utf8_validate_cz(const char *s)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
unsigned char c = *s++;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (c <= 0x7F) { /* 00..7F */
|
|
Packit |
bfcc33 |
return 1;
|
|
Packit |
bfcc33 |
} else if (c <= 0xC1) { /* 80..C1 */
|
|
Packit |
bfcc33 |
/* Disallow overlong 2-byte sequence. */
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
} else if (c <= 0xDF) { /* C2..DF */
|
|
Packit |
bfcc33 |
/* Make sure subsequent byte is in the range 0x80..0xBF. */
|
|
Packit |
bfcc33 |
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return 2;
|
|
Packit |
bfcc33 |
} else if (c <= 0xEF) { /* E0..EF */
|
|
Packit |
bfcc33 |
/* Disallow overlong 3-byte sequence. */
|
|
Packit |
bfcc33 |
if (c == 0xE0 && (unsigned char)*s < 0xA0)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Disallow U+D800..U+DFFF. */
|
|
Packit |
bfcc33 |
if (c == 0xED && (unsigned char)*s > 0x9F)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Make sure subsequent bytes are in the range 0x80..0xBF. */
|
|
Packit |
bfcc33 |
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return 3;
|
|
Packit |
bfcc33 |
} else if (c <= 0xF4) { /* F0..F4 */
|
|
Packit |
bfcc33 |
/* Disallow overlong 4-byte sequence. */
|
|
Packit |
bfcc33 |
if (c == 0xF0 && (unsigned char)*s < 0x90)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Disallow codepoints beyond U+10FFFF. */
|
|
Packit |
bfcc33 |
if (c == 0xF4 && (unsigned char)*s > 0x8F)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Make sure subsequent bytes are in the range 0x80..0xBF. */
|
|
Packit |
bfcc33 |
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return 4;
|
|
Packit |
bfcc33 |
} else { /* F5..FF */
|
|
Packit |
bfcc33 |
return 0;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Validate a null-terminated UTF-8 string. */
|
|
Packit |
bfcc33 |
static bool utf8_validate(const char *s)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
int len;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
for (; *s != 0; s += len) {
|
|
Packit |
bfcc33 |
len = utf8_validate_cz(s);
|
|
Packit |
bfcc33 |
if (len == 0)
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Read a single UTF-8 character starting at @s,
|
|
Packit |
bfcc33 |
* returning the length, in bytes, of the character read.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* This function assumes input is valid UTF-8,
|
|
Packit |
bfcc33 |
* and that there are enough characters in front of @s.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
static int utf8_read_char(const char *s, uint32_t *out)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const unsigned char *c = (const unsigned char*) s;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
assert(utf8_validate_cz(s));
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (c[0] <= 0x7F) {
|
|
Packit |
bfcc33 |
/* 00..7F */
|
|
Packit |
bfcc33 |
*out = c[0];
|
|
Packit |
bfcc33 |
return 1;
|
|
Packit |
bfcc33 |
} else if (c[0] <= 0xDF) {
|
|
Packit |
bfcc33 |
/* C2..DF (unless input is invalid) */
|
|
Packit |
bfcc33 |
*out = ((uint32_t)c[0] & 0x1F) << 6 |
|
|
Packit |
bfcc33 |
((uint32_t)c[1] & 0x3F);
|
|
Packit |
bfcc33 |
return 2;
|
|
Packit |
bfcc33 |
} else if (c[0] <= 0xEF) {
|
|
Packit |
bfcc33 |
/* E0..EF */
|
|
Packit |
bfcc33 |
*out = ((uint32_t)c[0] & 0xF) << 12 |
|
|
Packit |
bfcc33 |
((uint32_t)c[1] & 0x3F) << 6 |
|
|
Packit |
bfcc33 |
((uint32_t)c[2] & 0x3F);
|
|
Packit |
bfcc33 |
return 3;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
/* F0..F4 (unless input is invalid) */
|
|
Packit |
bfcc33 |
*out = ((uint32_t)c[0] & 0x7) << 18 |
|
|
Packit |
bfcc33 |
((uint32_t)c[1] & 0x3F) << 12 |
|
|
Packit |
bfcc33 |
((uint32_t)c[2] & 0x3F) << 6 |
|
|
Packit |
bfcc33 |
((uint32_t)c[3] & 0x3F);
|
|
Packit |
bfcc33 |
return 4;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Write a single UTF-8 character to @s,
|
|
Packit |
bfcc33 |
* returning the length, in bytes, of the character written.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* @unicode must be U+0000..U+10FFFF, but not U+D800..U+DFFF.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* This function will write up to 4 bytes to @out.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
static int utf8_write_char(uint32_t unicode, char *out)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
unsigned char *o = (unsigned char*) out;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF));
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (unicode <= 0x7F) {
|
|
Packit |
bfcc33 |
/* U+0000..U+007F */
|
|
Packit |
bfcc33 |
*o++ = unicode;
|
|
Packit |
bfcc33 |
return 1;
|
|
Packit |
bfcc33 |
} else if (unicode <= 0x7FF) {
|
|
Packit |
bfcc33 |
/* U+0080..U+07FF */
|
|
Packit |
bfcc33 |
*o++ = 0xC0 | unicode >> 6;
|
|
Packit |
bfcc33 |
*o++ = 0x80 | (unicode & 0x3F);
|
|
Packit |
bfcc33 |
return 2;
|
|
Packit |
bfcc33 |
} else if (unicode <= 0xFFFF) {
|
|
Packit |
bfcc33 |
/* U+0800..U+FFFF */
|
|
Packit |
bfcc33 |
*o++ = 0xE0 | unicode >> 12;
|
|
Packit |
bfcc33 |
*o++ = 0x80 | (unicode >> 6 & 0x3F);
|
|
Packit |
bfcc33 |
*o++ = 0x80 | (unicode & 0x3F);
|
|
Packit |
bfcc33 |
return 3;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
/* U+10000..U+10FFFF */
|
|
Packit |
bfcc33 |
*o++ = 0xF0 | unicode >> 18;
|
|
Packit |
bfcc33 |
*o++ = 0x80 | (unicode >> 12 & 0x3F);
|
|
Packit |
bfcc33 |
*o++ = 0x80 | (unicode >> 6 & 0x3F);
|
|
Packit |
bfcc33 |
*o++ = 0x80 | (unicode & 0x3F);
|
|
Packit |
bfcc33 |
return 4;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Compute the Unicode codepoint of a UTF-16 surrogate pair.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF.
|
|
Packit |
bfcc33 |
* If they aren't, this function returns false.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uint32_t *unicode)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) {
|
|
Packit |
bfcc33 |
*unicode = 0x10000 + ((((uint32_t)uc & 0x3FF) << 10) | (lc & 0x3FF));
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Construct a UTF-16 surrogate pair given a Unicode codepoint.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* @unicode must be U+10000..U+10FFFF.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
static void to_surrogate_pair(uint32_t unicode, uint16_t *uc, uint16_t *lc)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
uint32_t n;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
assert(unicode >= 0x10000 && unicode <= 0x10FFFF);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
n = unicode - 0x10000;
|
|
Packit |
bfcc33 |
*uc = ((n >> 10) & 0x3FF) | 0xD800;
|
|
Packit |
bfcc33 |
*lc = (n & 0x3FF) | 0xDC00;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static bool is_space (const char *c);
|
|
Packit |
bfcc33 |
static bool is_digit (const char *c);
|
|
Packit |
bfcc33 |
static bool parse_value (const char **sp, JsonNode **out);
|
|
Packit |
bfcc33 |
static bool parse_string (const char **sp, char **out);
|
|
Packit |
bfcc33 |
static bool parse_number (const char **sp, double *out);
|
|
Packit |
bfcc33 |
static bool parse_array (const char **sp, JsonNode **out);
|
|
Packit |
bfcc33 |
static bool parse_object (const char **sp, JsonNode **out);
|
|
Packit |
bfcc33 |
static bool parse_hex16 (const char **sp, uint16_t *out);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static bool expect_literal (const char **sp, const char *str);
|
|
Packit |
bfcc33 |
static void skip_space (const char **sp);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void emit_value (SB *out, const JsonNode *node);
|
|
Packit |
bfcc33 |
static void emit_value_indented (SB *out, const JsonNode *node, const char *space, int indent_level);
|
|
Packit |
bfcc33 |
static void emit_string (SB *out, const char *str);
|
|
Packit |
bfcc33 |
static void emit_number (SB *out, double num);
|
|
Packit |
bfcc33 |
static void emit_array (SB *out, const JsonNode *array);
|
|
Packit |
bfcc33 |
static void emit_array_indented (SB *out, const JsonNode *array, const char *space, int indent_level);
|
|
Packit |
bfcc33 |
static void emit_object (SB *out, const JsonNode *object);
|
|
Packit |
bfcc33 |
static void emit_object_indented (SB *out, const JsonNode *object, const char *space, int indent_level);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static int write_hex16(char *out, uint16_t val);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static JsonNode *mknode(JsonTag tag);
|
|
Packit |
bfcc33 |
static void append_node(JsonNode *parent, JsonNode *child);
|
|
Packit |
bfcc33 |
static void prepend_node(JsonNode *parent, JsonNode *child);
|
|
Packit |
bfcc33 |
static void append_member(JsonNode *object, char *key, JsonNode *value);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Assertion-friendly validity checks */
|
|
Packit |
bfcc33 |
static bool tag_is_valid(unsigned int tag);
|
|
Packit |
bfcc33 |
static bool number_is_valid(const char *num);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_decode(const char *json)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = json;
|
|
Packit |
bfcc33 |
JsonNode *ret;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
if (!parse_value(&s, &ret))
|
|
Packit |
bfcc33 |
return NULL;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
if (*s != 0) {
|
|
Packit |
bfcc33 |
json_delete(ret);
|
|
Packit |
bfcc33 |
return NULL;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return ret;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
char *json_encode(const JsonNode *node)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return json_stringify(node, NULL);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
char *json_encode_string(const char *str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
SB sb;
|
|
Packit |
bfcc33 |
sb_init(&sb);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
try {
|
|
Packit |
bfcc33 |
emit_string(&sb, str);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
catch (std::exception) {
|
|
Packit |
bfcc33 |
sb_free(&sb);
|
|
Packit |
bfcc33 |
throw;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return sb_finish(&sb);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
char *json_stringify(const JsonNode *node, const char *space)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
SB sb;
|
|
Packit |
bfcc33 |
sb_init(&sb);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
try {
|
|
Packit |
bfcc33 |
if (space != NULL)
|
|
Packit |
bfcc33 |
emit_value_indented(&sb, node, space, 0);
|
|
Packit |
bfcc33 |
else
|
|
Packit |
bfcc33 |
emit_value(&sb, node);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
catch (std::exception) {
|
|
Packit |
bfcc33 |
sb_free(&sb);
|
|
Packit |
bfcc33 |
throw;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return sb_finish(&sb);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
void json_delete(JsonNode *node)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (node != NULL) {
|
|
Packit |
bfcc33 |
json_remove_from_parent(node);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
switch (node->tag) {
|
|
Packit |
bfcc33 |
case JSON_STRING:
|
|
Packit |
bfcc33 |
free(node->string_);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_ARRAY:
|
|
Packit |
bfcc33 |
case JSON_OBJECT:
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
JsonNode *child, *next;
|
|
Packit |
bfcc33 |
for (child = node->children.head; child != NULL; child = next) {
|
|
Packit |
bfcc33 |
next = child->next;
|
|
Packit |
bfcc33 |
json_delete(child);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
default:;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
free(node);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool json_validate(const char *json)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = json;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
if (!parse_value(&s, NULL))
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
if (*s != 0)
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_find_element(JsonNode *array, int index)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
JsonNode *element;
|
|
Packit |
bfcc33 |
int i = 0;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (array == NULL || array->tag != JSON_ARRAY)
|
|
Packit |
bfcc33 |
return NULL;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
json_foreach(element, array) {
|
|
Packit |
bfcc33 |
if (i == index)
|
|
Packit |
bfcc33 |
return element;
|
|
Packit |
bfcc33 |
i++;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return NULL;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_find_member(JsonNode *object, const char *name)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
JsonNode *member;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (object == NULL || object->tag != JSON_OBJECT)
|
|
Packit |
bfcc33 |
return NULL;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
json_foreach(member, object)
|
|
Packit |
bfcc33 |
if (strcmp(member->key, name) == 0)
|
|
Packit |
bfcc33 |
return member;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return NULL;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_first_child(const JsonNode *node)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (node != NULL && (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT))
|
|
Packit |
bfcc33 |
return node->children.head;
|
|
Packit |
bfcc33 |
return NULL;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static JsonNode *mknode(JsonTag tag)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
JsonNode *ret = (JsonNode*) calloc(1, sizeof(JsonNode));
|
|
Packit |
bfcc33 |
if (ret == NULL)
|
|
Packit |
bfcc33 |
out_of_memory();
|
|
Packit |
bfcc33 |
ret->tag = tag;
|
|
Packit |
bfcc33 |
return ret;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_mknull(void)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return mknode(JSON_NULL);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_mkbool(bool b)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
JsonNode *ret = mknode(JSON_BOOL);
|
|
Packit |
bfcc33 |
ret->bool_ = b;
|
|
Packit |
bfcc33 |
return ret;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static JsonNode *mkstring(char *s)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
JsonNode *ret = mknode(JSON_STRING);
|
|
Packit |
bfcc33 |
ret->string_ = s;
|
|
Packit |
bfcc33 |
return ret;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_mkstring(const char *s)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return mkstring(json_strdup(s));
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_mknumber(double n)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
JsonNode *node = mknode(JSON_NUMBER);
|
|
Packit |
bfcc33 |
node->number_ = n;
|
|
Packit |
bfcc33 |
return node;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_mkarray(void)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return mknode(JSON_ARRAY);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
JsonNode *json_mkobject(void)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return mknode(JSON_OBJECT);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void append_node(JsonNode *parent, JsonNode *child)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (child != NULL && parent != NULL) {
|
|
Packit |
bfcc33 |
child->parent = parent;
|
|
Packit |
bfcc33 |
child->prev = parent->children.tail;
|
|
Packit |
bfcc33 |
child->next = NULL;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (parent->children.tail != NULL)
|
|
Packit |
bfcc33 |
parent->children.tail->next = child;
|
|
Packit |
bfcc33 |
else
|
|
Packit |
bfcc33 |
parent->children.head = child;
|
|
Packit |
bfcc33 |
parent->children.tail = child;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void prepend_node(JsonNode *parent, JsonNode *child)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (child != NULL && parent != NULL) {
|
|
Packit |
bfcc33 |
child->parent = parent;
|
|
Packit |
bfcc33 |
child->prev = NULL;
|
|
Packit |
bfcc33 |
child->next = parent->children.head;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (parent->children.head != NULL)
|
|
Packit |
bfcc33 |
parent->children.head->prev = child;
|
|
Packit |
bfcc33 |
else
|
|
Packit |
bfcc33 |
parent->children.tail = child;
|
|
Packit |
bfcc33 |
parent->children.head = child;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void append_member(JsonNode *object, char *key, JsonNode *value)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (value != NULL && object != NULL) {
|
|
Packit |
bfcc33 |
value->key = key;
|
|
Packit |
bfcc33 |
append_node(object, value);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
void json_append_element(JsonNode *array, JsonNode *element)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (array != NULL && element !=NULL) {
|
|
Packit |
bfcc33 |
assert(array->tag == JSON_ARRAY);
|
|
Packit |
bfcc33 |
assert(element->parent == NULL);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
append_node(array, element);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
void json_prepend_element(JsonNode *array, JsonNode *element)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
assert(array->tag == JSON_ARRAY);
|
|
Packit |
bfcc33 |
assert(element->parent == NULL);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
prepend_node(array, element);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
void json_append_member(JsonNode *object, const char *key, JsonNode *value)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (object != NULL && key != NULL && value != NULL) {
|
|
Packit |
bfcc33 |
assert(object->tag == JSON_OBJECT);
|
|
Packit |
bfcc33 |
assert(value->parent == NULL);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
append_member(object, json_strdup(key), value);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
void json_prepend_member(JsonNode *object, const char *key, JsonNode *value)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (object != NULL && key != NULL && value != NULL) {
|
|
Packit |
bfcc33 |
assert(object->tag == JSON_OBJECT);
|
|
Packit |
bfcc33 |
assert(value->parent == NULL);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
value->key = json_strdup(key);
|
|
Packit |
bfcc33 |
prepend_node(object, value);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
void json_remove_from_parent(JsonNode *node)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (node != NULL) {
|
|
Packit |
bfcc33 |
JsonNode *parent = node->parent;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (parent != NULL) {
|
|
Packit |
bfcc33 |
if (node->prev != NULL)
|
|
Packit |
bfcc33 |
node->prev->next = node->next;
|
|
Packit |
bfcc33 |
else
|
|
Packit |
bfcc33 |
parent->children.head = node->next;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (node->next != NULL)
|
|
Packit |
bfcc33 |
node->next->prev = node->prev;
|
|
Packit |
bfcc33 |
else
|
|
Packit |
bfcc33 |
parent->children.tail = node->prev;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
free(node->key);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
node->parent = NULL;
|
|
Packit |
bfcc33 |
node->prev = node->next = NULL;
|
|
Packit |
bfcc33 |
node->key = NULL;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static bool parse_value(const char **sp, JsonNode **out)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = *sp;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
switch (*s) {
|
|
Packit |
bfcc33 |
case 'n':
|
|
Packit |
bfcc33 |
if (expect_literal(&s, "null")) {
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = json_mknull();
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
case 'f':
|
|
Packit |
bfcc33 |
if (expect_literal(&s, "false")) {
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = json_mkbool(false);
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
case 't':
|
|
Packit |
bfcc33 |
if (expect_literal(&s, "true")) {
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = json_mkbool(true);
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
case '"': {
|
|
Packit |
bfcc33 |
char *str = NULL;
|
|
Packit |
bfcc33 |
if (parse_string(&s, out ? &str : NULL)) {
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = mkstring(str);
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
case '[':
|
|
Packit |
bfcc33 |
if (parse_array(&s, out)) {
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
case '{':
|
|
Packit |
bfcc33 |
if (parse_object(&s, out)) {
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
default: {
|
|
Packit |
bfcc33 |
double num;
|
|
Packit |
bfcc33 |
if (parse_number(&s, out ? &num : NULL)) {
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = json_mknumber(num);
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static bool parse_array(const char **sp, JsonNode **out)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = *sp;
|
|
Packit |
bfcc33 |
JsonNode *ret = out ? json_mkarray() : NULL;
|
|
Packit |
bfcc33 |
JsonNode *element = NULL;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s++ != '[')
|
|
Packit |
bfcc33 |
goto failure;
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s == ']') {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
goto success;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
for (;;) {
|
|
Packit |
bfcc33 |
if (!parse_value(&s, out ? &element : NULL))
|
|
Packit |
bfcc33 |
goto failure;
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
json_append_element(ret, element);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s == ']') {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
goto success;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s++ != ',')
|
|
Packit |
bfcc33 |
goto failure;
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
success:
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = ret;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
failure:
|
|
Packit |
bfcc33 |
json_delete(ret);
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static bool parse_object(const char **sp, JsonNode **out)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = *sp;
|
|
Packit |
bfcc33 |
JsonNode *ret = out ? json_mkobject() : NULL;
|
|
Packit |
bfcc33 |
char *key = NULL;
|
|
Packit |
bfcc33 |
JsonNode *value = NULL;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s++ != '{')
|
|
Packit |
bfcc33 |
goto failure;
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s == '}') {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
goto success;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
for (;;) {
|
|
Packit |
bfcc33 |
if (!parse_string(&s, out ? &key : NULL))
|
|
Packit |
bfcc33 |
goto failure;
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s++ != ':')
|
|
Packit |
bfcc33 |
goto failure_free_key;
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (!parse_value(&s, out ? &value : NULL))
|
|
Packit |
bfcc33 |
goto failure_free_key;
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
append_member(ret, key, value);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s == '}') {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
goto success;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s++ != ',')
|
|
Packit |
bfcc33 |
goto failure;
|
|
Packit |
bfcc33 |
skip_space(&s);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
success:
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = ret;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
failure_free_key:
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
free(key);
|
|
Packit |
bfcc33 |
failure:
|
|
Packit |
bfcc33 |
json_delete(ret);
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool parse_string(const char **sp, char **out)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = *sp;
|
|
Packit |
bfcc33 |
SB sb = { 0, 0, 0 };
|
|
Packit |
bfcc33 |
char throwaway_buffer[4];
|
|
Packit |
bfcc33 |
/* enough space for a UTF-8 character */
|
|
Packit |
bfcc33 |
char *b;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*s++ != '"')
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (out) {
|
|
Packit |
bfcc33 |
sb_init(&sb);
|
|
Packit |
bfcc33 |
sb_need(&sb, 4);
|
|
Packit |
bfcc33 |
b = sb.cur;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
b = throwaway_buffer;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
while (*s != '"') {
|
|
Packit |
bfcc33 |
unsigned char c = *s++;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Parse next character, and write it to b. */
|
|
Packit |
bfcc33 |
if (c == '\\') {
|
|
Packit |
bfcc33 |
c = *s++;
|
|
Packit |
bfcc33 |
switch (c) {
|
|
Packit |
bfcc33 |
case '"':
|
|
Packit |
bfcc33 |
case '\\':
|
|
Packit |
bfcc33 |
case '/':
|
|
Packit |
bfcc33 |
*b++ = c;
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case 'b':
|
|
Packit |
bfcc33 |
*b++ = '\b';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case 'f':
|
|
Packit |
bfcc33 |
*b++ = '\f';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case 'n':
|
|
Packit |
bfcc33 |
*b++ = '\n';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case 'r':
|
|
Packit |
bfcc33 |
*b++ = '\r';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case 't':
|
|
Packit |
bfcc33 |
*b++ = '\t';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case 'u':
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
uint16_t uc, lc;
|
|
Packit |
bfcc33 |
uint32_t unicode;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (!parse_hex16(&s, &uc))
|
|
Packit |
bfcc33 |
goto failed;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (uc >= 0xD800 && uc <= 0xDFFF) {
|
|
Packit |
bfcc33 |
/* Handle UTF-16 surrogate pair. */
|
|
Packit |
bfcc33 |
if (*s++ != '\\' || *s++ != 'u' || !parse_hex16(&s, &lc))
|
|
Packit |
bfcc33 |
goto failed; /* Incomplete surrogate pair. */
|
|
Packit |
bfcc33 |
if (!from_surrogate_pair(uc, lc, &unicode))
|
|
Packit |
bfcc33 |
goto failed; /* Invalid surrogate pair. */
|
|
Packit |
bfcc33 |
} else if (uc == 0) {
|
|
Packit |
bfcc33 |
/* Disallow "\u0000". */
|
|
Packit |
bfcc33 |
goto failed;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
unicode = uc;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
b += utf8_write_char(unicode, b);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
default:
|
|
Packit |
bfcc33 |
/* Invalid escape */
|
|
Packit |
bfcc33 |
goto failed;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
} else if (c <= 0x1F) {
|
|
Packit |
bfcc33 |
/* Control characters are not allowed in string literals. */
|
|
Packit |
bfcc33 |
goto failed;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
/* Validate and echo a UTF-8 character. */
|
|
Packit |
bfcc33 |
int len;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
s--;
|
|
Packit |
bfcc33 |
len = utf8_validate_cz(s);
|
|
Packit |
bfcc33 |
if (len == 0)
|
|
Packit |
bfcc33 |
goto failed; /* Invalid UTF-8 character. */
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
while (len--)
|
|
Packit |
bfcc33 |
*b++ = *s++;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Update sb to know about the new bytes,
|
|
Packit |
bfcc33 |
* and set up b to write another character.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
if (out) {
|
|
Packit |
bfcc33 |
sb.cur = b;
|
|
Packit |
bfcc33 |
sb_need(&sb, 4);
|
|
Packit |
bfcc33 |
b = sb.cur;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
b = throwaway_buffer;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = sb_finish(&sb);
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
failed:
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
sb_free(&sb);
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool is_space(const char *c) {
|
|
Packit |
bfcc33 |
return ((*c) == '\t' || (*c) == '\n' || (*c) == '\r' || (*c) == ' ');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool is_digit(const char *c){
|
|
Packit |
bfcc33 |
return ((*c) >= '0' && (*c) <= '9');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* The JSON spec says that a number shall follow this precise pattern
|
|
Packit |
bfcc33 |
* (spaces and quotes added for readability):
|
|
Packit |
bfcc33 |
* '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)?
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* However, some JSON parsers are more liberal. For instance, PHP accepts
|
|
Packit |
bfcc33 |
* '.5' and '1.'. JSON.parse accepts '+3'.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* This function takes the strict approach.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
bool parse_number(const char **sp, double *out)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = *sp;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* '-'? */
|
|
Packit |
bfcc33 |
if (*s == '-')
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* (0 | [1-9][0-9]*) */
|
|
Packit |
bfcc33 |
if (*s == '0') {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
if (!is_digit(s))
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
do {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
} while (is_digit(s));
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* ('.' [0-9]+)? */
|
|
Packit |
bfcc33 |
if (*s == '.') {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
if (!is_digit(s))
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
do {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
} while (is_digit(s));
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* ([Ee] [+-]? [0-9]+)? */
|
|
Packit |
bfcc33 |
if (*s == 'E' || *s == 'e') {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
if (*s == '+' || *s == '-')
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
if (!is_digit(s))
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
do {
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
} while (is_digit(s));
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = strtod(*sp, NULL);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void skip_space(const char **sp)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = *sp;
|
|
Packit |
bfcc33 |
while (is_space(s))
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void emit_value(SB *out, const JsonNode *node)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
assert(tag_is_valid(node->tag));
|
|
Packit |
bfcc33 |
switch (node->tag) {
|
|
Packit |
bfcc33 |
case JSON_NULL:
|
|
Packit |
bfcc33 |
sb_puts(out, "null");
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_BOOL:
|
|
Packit |
bfcc33 |
sb_puts(out, node->bool_ ? "true" : "false");
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_STRING:
|
|
Packit |
bfcc33 |
emit_string(out, node->string_);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_NUMBER:
|
|
Packit |
bfcc33 |
emit_number(out, node->number_);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_ARRAY:
|
|
Packit |
bfcc33 |
emit_array(out, node);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_OBJECT:
|
|
Packit |
bfcc33 |
emit_object(out, node);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
default:
|
|
Packit |
bfcc33 |
assert(false);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
void emit_value_indented(SB *out, const JsonNode *node, const char *space, int indent_level)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
assert(tag_is_valid(node->tag));
|
|
Packit |
bfcc33 |
switch (node->tag) {
|
|
Packit |
bfcc33 |
case JSON_NULL:
|
|
Packit |
bfcc33 |
sb_puts(out, "null");
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_BOOL:
|
|
Packit |
bfcc33 |
sb_puts(out, node->bool_ ? "true" : "false");
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_STRING:
|
|
Packit |
bfcc33 |
emit_string(out, node->string_);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_NUMBER:
|
|
Packit |
bfcc33 |
emit_number(out, node->number_);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_ARRAY:
|
|
Packit |
bfcc33 |
emit_array_indented(out, node, space, indent_level);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case JSON_OBJECT:
|
|
Packit |
bfcc33 |
emit_object_indented(out, node, space, indent_level);
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
default:
|
|
Packit |
bfcc33 |
assert(false);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void emit_array(SB *out, const JsonNode *array)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const JsonNode *element;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
sb_putc(out, '[');
|
|
Packit |
bfcc33 |
json_foreach(element, array) {
|
|
Packit |
bfcc33 |
emit_value(out, element);
|
|
Packit |
bfcc33 |
if (element->next != NULL)
|
|
Packit |
bfcc33 |
sb_putc(out, ',');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
sb_putc(out, ']');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void emit_array_indented(SB *out, const JsonNode *array, const char *space, int indent_level)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const JsonNode *element = array->children.head;
|
|
Packit |
bfcc33 |
int i;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (element == NULL) {
|
|
Packit |
bfcc33 |
sb_puts(out, "[]");
|
|
Packit |
bfcc33 |
return;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
sb_puts(out, "[\n");
|
|
Packit |
bfcc33 |
while (element != NULL) {
|
|
Packit |
bfcc33 |
for (i = 0; i < indent_level + 1; i++)
|
|
Packit |
bfcc33 |
sb_puts(out, space);
|
|
Packit |
bfcc33 |
emit_value_indented(out, element, space, indent_level + 1);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
element = element->next;
|
|
Packit |
bfcc33 |
sb_puts(out, element != NULL ? ",\n" : "\n");
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
for (i = 0; i < indent_level; i++)
|
|
Packit |
bfcc33 |
sb_puts(out, space);
|
|
Packit |
bfcc33 |
sb_putc(out, ']');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void emit_object(SB *out, const JsonNode *object)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const JsonNode *member;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
sb_putc(out, '{');
|
|
Packit |
bfcc33 |
json_foreach(member, object) {
|
|
Packit |
bfcc33 |
emit_string(out, member->key);
|
|
Packit |
bfcc33 |
sb_putc(out, ':');
|
|
Packit |
bfcc33 |
emit_value(out, member);
|
|
Packit |
bfcc33 |
if (member->next != NULL)
|
|
Packit |
bfcc33 |
sb_putc(out, ',');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
sb_putc(out, '}');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void emit_object_indented(SB *out, const JsonNode *object, const char *space, int indent_level)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const JsonNode *member = object->children.head;
|
|
Packit |
bfcc33 |
int i;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (member == NULL) {
|
|
Packit |
bfcc33 |
sb_puts(out, "{}");
|
|
Packit |
bfcc33 |
return;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
sb_puts(out, "{\n");
|
|
Packit |
bfcc33 |
while (member != NULL) {
|
|
Packit |
bfcc33 |
for (i = 0; i < indent_level + 1; i++)
|
|
Packit |
bfcc33 |
sb_puts(out, space);
|
|
Packit |
bfcc33 |
emit_string(out, member->key);
|
|
Packit |
bfcc33 |
sb_puts(out, ": ");
|
|
Packit |
bfcc33 |
emit_value_indented(out, member, space, indent_level + 1);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
member = member->next;
|
|
Packit |
bfcc33 |
sb_puts(out, member != NULL ? ",\n" : "\n");
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
for (i = 0; i < indent_level; i++)
|
|
Packit |
bfcc33 |
sb_puts(out, space);
|
|
Packit |
bfcc33 |
sb_putc(out, '}');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
void emit_string(SB *out, const char *str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
bool escape_unicode = false;
|
|
Packit |
bfcc33 |
const char *s = str;
|
|
Packit |
bfcc33 |
char *b;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// make assertion catchable
|
|
Packit |
bfcc33 |
#ifndef NDEBUG
|
|
Packit |
bfcc33 |
if (!utf8_validate(str)) {
|
|
Packit |
bfcc33 |
throw utf8::invalid_utf8(0);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
#endif
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
assert(utf8_validate(str));
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* 14 bytes is enough space to write up to two
|
|
Packit |
bfcc33 |
* \uXXXX escapes and two quotation marks.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
sb_need(out, 14);
|
|
Packit |
bfcc33 |
b = out->cur;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
*b++ = '"';
|
|
Packit |
bfcc33 |
while (*s != 0) {
|
|
Packit |
bfcc33 |
unsigned char c = *s++;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Encode the next character, and write it to b. */
|
|
Packit |
bfcc33 |
switch (c) {
|
|
Packit |
bfcc33 |
case '"':
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = '"';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case '\\':
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case '\b':
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = 'b';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case '\f':
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = 'f';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case '\n':
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = 'n';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case '\r':
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = 'r';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
case '\t':
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = 't';
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
default: {
|
|
Packit |
bfcc33 |
int len;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
s--;
|
|
Packit |
bfcc33 |
len = utf8_validate_cz(s);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (len == 0) {
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Handle invalid UTF-8 character gracefully in production
|
|
Packit |
bfcc33 |
* by writing a replacement character (U+FFFD)
|
|
Packit |
bfcc33 |
* and skipping a single byte.
|
|
Packit |
bfcc33 |
*
|
|
Packit |
bfcc33 |
* This should never happen when assertions are enabled
|
|
Packit |
bfcc33 |
* due to the assertion at the beginning of this function.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
assert(false);
|
|
Packit |
bfcc33 |
if (escape_unicode) {
|
|
Packit |
bfcc33 |
strcpy(b, "\\uFFFD");
|
|
Packit |
bfcc33 |
b += 6;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
*b++ = 0xEFu;
|
|
Packit |
bfcc33 |
*b++ = 0xBFu;
|
|
Packit |
bfcc33 |
*b++ = 0xBDu;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
s++;
|
|
Packit |
bfcc33 |
} else if (c < 0x1F || (c >= 0x80 && escape_unicode)) {
|
|
Packit |
bfcc33 |
/* Encode using \u.... */
|
|
Packit |
bfcc33 |
uint32_t unicode;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
s += utf8_read_char(s, &unicode);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (unicode <= 0xFFFF) {
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = 'u';
|
|
Packit |
bfcc33 |
b += write_hex16(b, unicode);
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
/* Produce a surrogate pair. */
|
|
Packit |
bfcc33 |
uint16_t uc, lc;
|
|
Packit |
bfcc33 |
assert(unicode <= 0x10FFFF);
|
|
Packit |
bfcc33 |
to_surrogate_pair(unicode, &uc, &lc);
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = 'u';
|
|
Packit |
bfcc33 |
b += write_hex16(b, uc);
|
|
Packit |
bfcc33 |
*b++ = '\\';
|
|
Packit |
bfcc33 |
*b++ = 'u';
|
|
Packit |
bfcc33 |
b += write_hex16(b, lc);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
/* Write the character directly. */
|
|
Packit |
bfcc33 |
while (len--)
|
|
Packit |
bfcc33 |
*b++ = *s++;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
break;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Update *out to know about the new bytes,
|
|
Packit |
bfcc33 |
* and set up b to write another encoded character.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
out->cur = b;
|
|
Packit |
bfcc33 |
sb_need(out, 14);
|
|
Packit |
bfcc33 |
b = out->cur;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
*b++ = '"';
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
out->cur = b;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static void emit_number(SB *out, double num)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* This isn't exactly how JavaScript renders numbers,
|
|
Packit |
bfcc33 |
* but it should produce valid JSON for reasonable numbers
|
|
Packit |
bfcc33 |
* preserve precision well enough, and avoid some oddities
|
|
Packit |
bfcc33 |
* like 0.3 -> 0.299999999999999988898 .
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
char buf[64];
|
|
Packit |
bfcc33 |
sprintf(buf, "%.16g", num);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (number_is_valid(buf))
|
|
Packit |
bfcc33 |
sb_puts(out, buf);
|
|
Packit |
bfcc33 |
else
|
|
Packit |
bfcc33 |
sb_puts(out, "null");
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static bool tag_is_valid(unsigned int tag)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return (/* tag >= JSON_NULL && */ tag <= JSON_OBJECT);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static bool number_is_valid(const char *num)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return (parse_number(&num, NULL) && *num == '\0');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
static bool expect_literal(const char **sp, const char *str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = *sp;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
while (*str != '\0')
|
|
Packit |
bfcc33 |
if (*s++ != *str++)
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Parses exactly 4 hex characters (capital or lowercase).
|
|
Packit |
bfcc33 |
* Fails if any input chars are not [0-9A-Fa-f].
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
static bool parse_hex16(const char **sp, uint16_t *out)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *s = *sp;
|
|
Packit |
bfcc33 |
uint16_t ret = 0;
|
|
Packit |
bfcc33 |
uint16_t i;
|
|
Packit |
bfcc33 |
uint16_t tmp;
|
|
Packit |
bfcc33 |
char c;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
for (i = 0; i < 4; i++) {
|
|
Packit |
bfcc33 |
c = *s++;
|
|
Packit |
bfcc33 |
if (c >= '0' && c <= '9')
|
|
Packit |
bfcc33 |
tmp = c - '0';
|
|
Packit |
bfcc33 |
else if (c >= 'A' && c <= 'F')
|
|
Packit |
bfcc33 |
tmp = c - 'A' + 10;
|
|
Packit |
bfcc33 |
else if (c >= 'a' && c <= 'f')
|
|
Packit |
bfcc33 |
tmp = c - 'a' + 10;
|
|
Packit |
bfcc33 |
else
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
ret <<= 4;
|
|
Packit |
bfcc33 |
ret += tmp;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (out)
|
|
Packit |
bfcc33 |
*out = ret;
|
|
Packit |
bfcc33 |
*sp = s;
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/*
|
|
Packit |
bfcc33 |
* Encodes a 16-bit number into hexadecimal,
|
|
Packit |
bfcc33 |
* writing exactly 4 hex chars.
|
|
Packit |
bfcc33 |
*/
|
|
Packit |
bfcc33 |
static int write_hex16(char *out, uint16_t val)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
const char *hex = "0123456789ABCDEF";
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
*out++ = hex[(val >> 12) & 0xF];
|
|
Packit |
bfcc33 |
*out++ = hex[(val >> 8) & 0xF];
|
|
Packit |
bfcc33 |
*out++ = hex[(val >> 4) & 0xF];
|
|
Packit |
bfcc33 |
*out++ = hex[ val & 0xF];
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return 4;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool json_check(const JsonNode *node, char errmsg[256])
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
#define problem(...) do { \
|
|
Packit |
bfcc33 |
if (errmsg != NULL) \
|
|
Packit |
bfcc33 |
snprintf(errmsg, 256, __VA_ARGS__); \
|
|
Packit |
bfcc33 |
return false; \
|
|
Packit |
bfcc33 |
} while (0)
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (node->key != NULL && !utf8_validate(node->key))
|
|
Packit |
bfcc33 |
problem("key contains invalid UTF-8");
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (!tag_is_valid(node->tag))
|
|
Packit |
bfcc33 |
problem("tag is invalid (%u)", node->tag);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (node->tag == JSON_BOOL) {
|
|
Packit |
bfcc33 |
if (node->bool_ != false && node->bool_ != true)
|
|
Packit |
bfcc33 |
problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true);
|
|
Packit |
bfcc33 |
} else if (node->tag == JSON_STRING) {
|
|
Packit |
bfcc33 |
if (node->string_ == NULL)
|
|
Packit |
bfcc33 |
problem("string_ is NULL");
|
|
Packit |
bfcc33 |
if (!utf8_validate(node->string_))
|
|
Packit |
bfcc33 |
problem("string_ contains invalid UTF-8");
|
|
Packit |
bfcc33 |
} else if (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT) {
|
|
Packit |
bfcc33 |
JsonNode *head = node->children.head;
|
|
Packit |
bfcc33 |
JsonNode *tail = node->children.tail;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (head == NULL || tail == NULL) {
|
|
Packit |
bfcc33 |
if (head != NULL)
|
|
Packit |
bfcc33 |
problem("tail is NULL, but head is not");
|
|
Packit |
bfcc33 |
if (tail != NULL)
|
|
Packit |
bfcc33 |
problem("head is NULL, but tail is not");
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
JsonNode *child;
|
|
Packit |
bfcc33 |
JsonNode *last = NULL;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (head->prev != NULL)
|
|
Packit |
bfcc33 |
problem("First child's prev pointer is not NULL");
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
for (child = head; child != NULL; last = child, child = child->next) {
|
|
Packit |
bfcc33 |
if (child == node)
|
|
Packit |
bfcc33 |
problem("node is its own child");
|
|
Packit |
bfcc33 |
if (child->next == child)
|
|
Packit |
bfcc33 |
problem("child->next == child (cycle)");
|
|
Packit |
bfcc33 |
if (child->next == head)
|
|
Packit |
bfcc33 |
problem("child->next == head (cycle)");
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (child->parent != node)
|
|
Packit |
bfcc33 |
problem("child does not point back to parent");
|
|
Packit |
bfcc33 |
if (child->next != NULL && child->next->prev != child)
|
|
Packit |
bfcc33 |
problem("child->next does not point back to child");
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (node->tag == JSON_ARRAY && child->key != NULL)
|
|
Packit |
bfcc33 |
problem("Array element's key is not NULL");
|
|
Packit |
bfcc33 |
if (node->tag == JSON_OBJECT && child->key == NULL)
|
|
Packit |
bfcc33 |
problem("Object member's key is NULL");
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (!json_check(child, errmsg))
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (last != tail)
|
|
Packit |
bfcc33 |
problem("tail does not match pointer found by starting at head and following next links");
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
#undef problem
|
|
Packit |
bfcc33 |
}
|