CSS-Sass
view release on metacpan or search on metacpan
libsass/json.cpp view on Meta::CPAN
return 0;
}
}
/* Validate a null-terminated UTF-8 string. */
static bool utf8_validate(const char *s)
{
int len;
for (; *s != 0; s += len) {
len = utf8_validate_cz(s);
if (len == 0)
return false;
}
return true;
}
/*
* Read a single UTF-8 character starting at @s,
* returning the length, in bytes, of the character read.
*
* This function assumes input is valid UTF-8,
* and that there are enough characters in front of @s.
*/
static int utf8_read_char(const char *s, uint32_t *out)
{
const unsigned char *c = (const unsigned char*) s;
assert(utf8_validate_cz(s));
if (c[0] <= 0x7F) {
/* 00..7F */
*out = c[0];
return 1;
} else if (c[0] <= 0xDF) {
/* C2..DF (unless input is invalid) */
*out = ((uint32_t)c[0] & 0x1F) << 6 |
((uint32_t)c[1] & 0x3F);
return 2;
} else if (c[0] <= 0xEF) {
/* E0..EF */
*out = ((uint32_t)c[0] & 0xF) << 12 |
((uint32_t)c[1] & 0x3F) << 6 |
((uint32_t)c[2] & 0x3F);
return 3;
} else {
/* F0..F4 (unless input is invalid) */
*out = ((uint32_t)c[0] & 0x7) << 18 |
((uint32_t)c[1] & 0x3F) << 12 |
((uint32_t)c[2] & 0x3F) << 6 |
((uint32_t)c[3] & 0x3F);
return 4;
}
}
/*
* Write a single UTF-8 character to @s,
* returning the length, in bytes, of the character written.
*
* @unicode must be U+0000..U+10FFFF, but not U+D800..U+DFFF.
*
* This function will write up to 4 bytes to @out.
*/
static int utf8_write_char(uint32_t unicode, char *out)
{
unsigned char *o = (unsigned char*) out;
assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF));
if (unicode <= 0x7F) {
/* U+0000..U+007F */
*o++ = unicode;
return 1;
} else if (unicode <= 0x7FF) {
/* U+0080..U+07FF */
*o++ = 0xC0 | unicode >> 6;
*o++ = 0x80 | (unicode & 0x3F);
return 2;
} else if (unicode <= 0xFFFF) {
/* U+0800..U+FFFF */
*o++ = 0xE0 | unicode >> 12;
*o++ = 0x80 | (unicode >> 6 & 0x3F);
*o++ = 0x80 | (unicode & 0x3F);
return 3;
} else {
/* U+10000..U+10FFFF */
*o++ = 0xF0 | unicode >> 18;
*o++ = 0x80 | (unicode >> 12 & 0x3F);
*o++ = 0x80 | (unicode >> 6 & 0x3F);
*o++ = 0x80 | (unicode & 0x3F);
return 4;
}
}
/*
* Compute the Unicode codepoint of a UTF-16 surrogate pair.
*
* @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF.
* If they aren't, this function returns false.
*/
static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uint32_t *unicode)
{
if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) {
*unicode = 0x10000 + ((((uint32_t)uc & 0x3FF) << 10) | (lc & 0x3FF));
return true;
} else {
return false;
}
}
/*
* Construct a UTF-16 surrogate pair given a Unicode codepoint.
*
* @unicode must be U+10000..U+10FFFF.
*/
static void to_surrogate_pair(uint32_t unicode, uint16_t *uc, uint16_t *lc)
{
uint32_t n;
assert(unicode >= 0x10000 && unicode <= 0x10FFFF);
n = unicode - 0x10000;
*uc = ((n >> 10) & 0x3FF) | 0xD800;
*lc = (n & 0x3FF) | 0xDC00;
}
#define is_space(c) ((c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == ' ')
#define is_digit(c) ((c) >= '0' && (c) <= '9')
static bool parse_value (const char **sp, JsonNode **out);
static bool parse_string (const char **sp, char **out);
static bool parse_number (const char **sp, double *out);
static bool parse_array (const char **sp, JsonNode **out);
static bool parse_object (const char **sp, JsonNode **out);
static bool parse_hex16 (const char **sp, uint16_t *out);
static bool expect_literal (const char **sp, const char *str);
static void skip_space (const char **sp);
static void emit_value (SB *out, const JsonNode *node);
static void emit_value_indented (SB *out, const JsonNode *node, const char *space, int indent_level);
static void emit_string (SB *out, const char *str);
static void emit_number (SB *out, double num);
static void emit_array (SB *out, const JsonNode *array);
static void emit_array_indented (SB *out, const JsonNode *array, const char *space, int indent_level);
static void emit_object (SB *out, const JsonNode *object);
static void emit_object_indented (SB *out, const JsonNode *object, const char *space, int indent_level);
static int write_hex16(char *out, uint16_t val);
static JsonNode *mknode(JsonTag tag);
static void append_node(JsonNode *parent, JsonNode *child);
static void prepend_node(JsonNode *parent, JsonNode *child);
static void append_member(JsonNode *object, char *key, JsonNode *value);
/* Assertion-friendly validity checks */
static bool tag_is_valid(unsigned int tag);
static bool number_is_valid(const char *num);
JsonNode *json_decode(const char *json)
{
const char *s = json;
JsonNode *ret;
skip_space(&s);
if (!parse_value(&s, &ret))
return NULL;
skip_space(&s);
if (*s != 0) {
json_delete(ret);
return NULL;
}
return ret;
}
char *json_encode(const JsonNode *node)
{
return json_stringify(node, NULL);
}
libsass/json.cpp view on Meta::CPAN
*out = ret;
return true;
failure_free_key:
if (out)
free(key);
failure:
json_delete(ret);
return false;
}
bool parse_string(const char **sp, char **out)
{
const char *s = *sp;
SB sb;
char throwaway_buffer[4];
/* enough space for a UTF-8 character */
char *b;
if (*s++ != '"')
return false;
if (out) {
sb_init(&sb);
sb_need(&sb, 4);
b = sb.cur;
} else {
b = throwaway_buffer;
}
while (*s != '"') {
unsigned char c = *s++;
/* Parse next character, and write it to b. */
if (c == '\\') {
c = *s++;
switch (c) {
case '"':
case '\\':
case '/':
*b++ = c;
break;
case 'b':
*b++ = '\b';
break;
case 'f':
*b++ = '\f';
break;
case 'n':
*b++ = '\n';
break;
case 'r':
*b++ = '\r';
break;
case 't':
*b++ = '\t';
break;
case 'u':
{
uint16_t uc, lc;
uint32_t unicode;
if (!parse_hex16(&s, &uc))
goto failed;
if (uc >= 0xD800 && uc <= 0xDFFF) {
/* Handle UTF-16 surrogate pair. */
if (*s++ != '\\' || *s++ != 'u' || !parse_hex16(&s, &lc))
goto failed; /* Incomplete surrogate pair. */
if (!from_surrogate_pair(uc, lc, &unicode))
goto failed; /* Invalid surrogate pair. */
} else if (uc == 0) {
/* Disallow "\u0000". */
goto failed;
} else {
unicode = uc;
}
b += utf8_write_char(unicode, b);
break;
}
default:
/* Invalid escape */
goto failed;
}
} else if (c <= 0x1F) {
/* Control characters are not allowed in string literals. */
goto failed;
} else {
/* Validate and echo a UTF-8 character. */
int len;
s--;
len = utf8_validate_cz(s);
if (len == 0)
goto failed; /* Invalid UTF-8 character. */
while (len--)
*b++ = *s++;
}
/*
* Update sb to know about the new bytes,
* and set up b to write another character.
*/
if (out) {
sb.cur = b;
sb_need(&sb, 4);
b = sb.cur;
} else {
b = throwaway_buffer;
}
}
s++;
if (out)
*out = sb_finish(&sb);
*sp = s;
return true;
failed:
if (out)
sb_free(&sb);
return false;
}
/*
* The JSON spec says that a number shall follow this precise pattern
* (spaces and quotes added for readability):
* '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)?
*
* However, some JSON parsers are more liberal. For instance, PHP accepts
* '.5' and '1.'. JSON.parse accepts '+3'.
*
* This function takes the strict approach.
*/
bool parse_number(const char **sp, double *out)
{
const char *s = *sp;
libsass/json.cpp view on Meta::CPAN
return;
}
sb_puts(out, "[\n");
while (element != NULL) {
for (i = 0; i < indent_level + 1; i++)
sb_puts(out, space);
emit_value_indented(out, element, space, indent_level + 1);
element = element->next;
sb_puts(out, element != NULL ? ",\n" : "\n");
}
for (i = 0; i < indent_level; i++)
sb_puts(out, space);
sb_putc(out, ']');
}
static void emit_object(SB *out, const JsonNode *object)
{
const JsonNode *member;
sb_putc(out, '{');
json_foreach(member, object) {
emit_string(out, member->key);
sb_putc(out, ':');
emit_value(out, member);
if (member->next != NULL)
sb_putc(out, ',');
}
sb_putc(out, '}');
}
static void emit_object_indented(SB *out, const JsonNode *object, const char *space, int indent_level)
{
const JsonNode *member = object->children.head;
int i;
if (member == NULL) {
sb_puts(out, "{}");
return;
}
sb_puts(out, "{\n");
while (member != NULL) {
for (i = 0; i < indent_level + 1; i++)
sb_puts(out, space);
emit_string(out, member->key);
sb_puts(out, ": ");
emit_value_indented(out, member, space, indent_level + 1);
member = member->next;
sb_puts(out, member != NULL ? ",\n" : "\n");
}
for (i = 0; i < indent_level; i++)
sb_puts(out, space);
sb_putc(out, '}');
}
void emit_string(SB *out, const char *str)
{
bool escape_unicode = false;
const char *s = str;
char *b;
assert(utf8_validate(str));
/*
* 14 bytes is enough space to write up to two
* \uXXXX escapes and two quotation marks.
*/
sb_need(out, 14);
b = out->cur;
*b++ = '"';
while (*s != 0) {
unsigned char c = *s++;
/* Encode the next character, and write it to b. */
switch (c) {
case '"':
*b++ = '\\';
*b++ = '"';
break;
case '\\':
*b++ = '\\';
*b++ = '\\';
break;
case '\b':
*b++ = '\\';
*b++ = 'b';
break;
case '\f':
*b++ = '\\';
*b++ = 'f';
break;
case '\n':
*b++ = '\\';
*b++ = 'n';
break;
case '\r':
*b++ = '\\';
*b++ = 'r';
break;
case '\t':
*b++ = '\\';
*b++ = 't';
break;
default: {
int len;
s--;
len = utf8_validate_cz(s);
if (len == 0) {
/*
* Handle invalid UTF-8 character gracefully in production
* by writing a replacement character (U+FFFD)
* and skipping a single byte.
*
* This should never happen when assertions are enabled
* due to the assertion at the beginning of this function.
*/
assert(false);
if (escape_unicode) {
strcpy(b, "\\uFFFD");
b += 6;
} else {
*b++ = 0xEFu;
*b++ = 0xBFu;
*b++ = 0xBDu;
}
s++;
} else if (c < 0x1F || (c >= 0x80 && escape_unicode)) {
/* Encode using \u.... */
uint32_t unicode;
s += utf8_read_char(s, &unicode);
if (unicode <= 0xFFFF) {
*b++ = '\\';
*b++ = 'u';
b += write_hex16(b, unicode);
} else {
/* Produce a surrogate pair. */
uint16_t uc, lc;
assert(unicode <= 0x10FFFF);
to_surrogate_pair(unicode, &uc, &lc);
*b++ = '\\';
*b++ = 'u';
b += write_hex16(b, uc);
*b++ = '\\';
*b++ = 'u';
b += write_hex16(b, lc);
}
} else {
/* Write the character directly. */
while (len--)
*b++ = *s++;
}
break;
}
}
/*
* Update *out to know about the new bytes,
* and set up b to write another encoded character.
*/
out->cur = b;
sb_need(out, 14);
b = out->cur;
}
*b++ = '"';
out->cur = b;
}
static void emit_number(SB *out, double num)
{
/*
* This isn't exactly how JavaScript renders numbers,
* but it should produce valid JSON for reasonable numbers
* preserve precision well enough, and avoid some oddities
* like 0.3 -> 0.299999999999999988898 .
*/
char buf[64];
sprintf(buf, "%.16g", num);
if (number_is_valid(buf))
sb_puts(out, buf);
else
sb_puts(out, "null");
}
static bool tag_is_valid(unsigned int tag)
{
return (/* tag >= JSON_NULL && */ tag <= JSON_OBJECT);
}
static bool number_is_valid(const char *num)
{
return (parse_number(&num, NULL) && *num == '\0');
}
static bool expect_literal(const char **sp, const char *str)
{
const char *s = *sp;
( run in 0.572 second using v1.01-cache-2.11-cpan-39bf76dae61 )