git/json-parser.c

184 строки
4.0 KiB
C

#include "git-compat-util.h"
#include "hex.h"
#include "json-parser.h"
static int reset_iterator(struct json_iterator *it)
{
it->p = it->begin = it->json;
strbuf_release(&it->key);
strbuf_release(&it->string_value);
it->type = JSON_NULL;
return -1;
}
static int parse_json_string(struct json_iterator *it, struct strbuf *out)
{
const char *begin = it->p;
if (*(it->p)++ != '"')
return error("expected double quote: '%.*s'", 5, begin),
reset_iterator(it);
strbuf_reset(&it->string_value);
#define APPEND(c) strbuf_addch(out, c)
while (*it->p != '"') {
switch (*it->p) {
case '\0':
return error("incomplete string: '%s'", begin),
reset_iterator(it);
case '\\':
it->p++;
if (*it->p == '\\' || *it->p == '"')
APPEND(*it->p);
else if (*it->p == 'b')
APPEND(8);
else if (*it->p == 't')
APPEND(9);
else if (*it->p == 'n')
APPEND(10);
else if (*it->p == 'f')
APPEND(12);
else if (*it->p == 'r')
APPEND(13);
else if (*it->p == 'u') {
unsigned char binary[2];
int i;
if (hex_to_bytes(binary, it->p + 1, 2) < 0)
return error("invalid: '%.*s'",
6, it->p - 1),
reset_iterator(it);
it->p += 4;
i = (binary[0] << 8) | binary[1];
if (i < 0x80)
APPEND(i);
else if (i < 0x0800) {
APPEND(0xc0 | ((i >> 6) & 0x1f));
APPEND(0x80 | (i & 0x3f));
} else if (i < 0x10000) {
APPEND(0xe0 | ((i >> 12) & 0x0f));
APPEND(0x80 | ((i >> 6) & 0x3f));
APPEND(0x80 | (i & 0x3f));
} else {
APPEND(0xf0 | ((i >> 18) & 0x07));
APPEND(0x80 | ((i >> 12) & 0x3f));
APPEND(0x80 | ((i >> 6) & 0x3f));
APPEND(0x80 | (i & 0x3f));
}
}
break;
default:
APPEND(*it->p);
}
it->p++;
}
it->end = it->p++;
return 0;
}
static void skip_whitespace(struct json_iterator *it)
{
while (isspace(*it->p))
it->p++;
}
int iterate_json(struct json_iterator *it)
{
skip_whitespace(it);
it->begin = it->p;
switch (*it->p) {
case '\0':
return reset_iterator(it), 0;
case 'n':
if (!starts_with(it->p, "null"))
return error("unexpected value: %.*s", 4, it->p),
reset_iterator(it);
it->type = JSON_NULL;
it->end = it->p = it->begin + 4;
break;
case 't':
if (!starts_with(it->p, "true"))
return error("unexpected value: %.*s", 4, it->p),
reset_iterator(it);
it->type = JSON_TRUE;
it->end = it->p = it->begin + 4;
break;
case 'f':
if (!starts_with(it->p, "false"))
return error("unexpected value: %.*s", 5, it->p),
reset_iterator(it);
it->type = JSON_FALSE;
it->end = it->p = it->begin + 5;
break;
case '-': case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
it->type = JSON_NUMBER;
it->end = it->p = it->begin + strspn(it->p, "-.0123456789");
break;
case '"':
it->type = JSON_STRING;
if (parse_json_string(it, &it->string_value) < 0)
return -1;
break;
case '[': {
const char *save = it->begin;
size_t key_offset = it->key.len;
int i = 0, res;
for (it->p++, skip_whitespace(it); *it->p != ']'; i++) {
strbuf_addf(&it->key, "[%d]", i);
if ((res = iterate_json(it)))
return reset_iterator(it), res;
strbuf_setlen(&it->key, key_offset);
skip_whitespace(it);
if (*it->p == ',')
it->p++;
}
it->type = JSON_ARRAY;
it->begin = save;
it->end = it->p;
it->p++;
break;
}
case '{': {
const char *save = it->begin;
size_t key_offset = it->key.len;
int res;
strbuf_addch(&it->key, '.');
for (it->p++, skip_whitespace(it); *it->p != '}'; ) {
strbuf_setlen(&it->key, key_offset + 1);
if (parse_json_string(it, &it->key) < 0)
return -1;
skip_whitespace(it);
if (*(it->p)++ != ':')
return error("expected colon: %.*s", 5, it->p),
reset_iterator(it);
if ((res = iterate_json(it)))
return res;
skip_whitespace(it);
if (*it->p == ',')
it->p++;
}
strbuf_setlen(&it->key, key_offset);
it->type = JSON_OBJECT;
it->begin = save;
it->end = it->p;
it->p++;
break;
}
}
return it->fn(it);
}