blob: 6a85cec16cbd5505bb327d21e03f3d7b2b8b90f1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* This module provides support for JSON - a computer data interchange format.
* It is a text-based, human-readable format for representing simple data structures and
* associative arrays (called objects). The JSON format is specified in RFC 4627 by Douglas Crockford.
* JSON is TCF preffered marshaling format.
*/
#include "json.h"
#include "assert.h"
#include "myalloc.h"
#include "exceptions.h"
static char * buf = NULL;
static unsigned buf_size = 0;
void json_write_ulong(OutputStream * out, unsigned long n) {
if (n >= 10) {
json_write_ulong(out, n / 10);
n = n % 10;
}
out->write(out, n + '0');
}
void json_write_long(OutputStream * out, long n) {
if (n < 0) {
out->write(out, '-');
n = -n;
}
json_write_ulong(out, (unsigned long)n);
}
void json_write_int64(OutputStream * out, int64 n) {
if (n < 0) {
out->write(out, '-');
n = -n;
if (n < 0) exception(EINVAL);
}
if (n >= 10) {
json_write_int64(out, n / 10);
n = n % 10;
}
out->write(out, (int)n + '0');
}
void json_write_boolean(OutputStream * out, int b) {
if (b) write_string(out, "true");
else write_string(out, "false");
}
static char hex_digit(unsigned n) {
n &= 0xf;
if (n < 10) return '0' + n;
return 'A' + (n - 10);
}
void json_write_char(OutputStream * out, char ch) {
unsigned n = ch & 0xff;
if (n < ' ') {
out->write(out, '\\');
out->write(out, 'u');
out->write(out, '0');
out->write(out, '0');
out->write(out, hex_digit(n >> 4));
out->write(out, hex_digit(n));
}
else {
if (n == '"' || n == '\\') out->write(out, '\\');
out->write(out, n);
}
}
void json_write_string(OutputStream * out, const char * str) {
if (str == NULL) {
write_string(out, "null");
}
else {
out->write(out, '"');
while (*str) json_write_char(out, *str++);
out->write(out, '"');
}
}
void json_write_string_len(OutputStream * out, const char * str, size_t len) {
if (str == NULL) {
write_string(out, "null");
}
else {
out->write(out, '"');
while (len > 0) {
json_write_char(out, *str++);
len--;
}
out->write(out, '"');
}
}
static int readHex(InputStream * inp) {
int ch = inp->read(inp);
if (ch >= '0' && ch <= '9') return ch - '0';
if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
exception(ERR_JSON_SYNTAX);
return 0;
}
static int readHexChar(InputStream * inp) {
int n = readHex(inp) << 12;
n |= readHex(inp) << 8;
n |= readHex(inp) << 4;
n |= readHex(inp);
return n;
}
static int read_esc_char(InputStream * inp) {
int ch = inp->read(inp);
switch (ch) {
case '"': break;
case '\\': break;
case '/': break;
case 'b': ch = '\b'; break;
case 'f': ch = '\f'; break;
case 'n': ch = '\n'; break;
case 'r': ch = '\r'; break;
case 't': ch = '\t'; break;
case 'u': ch = readHexChar(inp); break;
default: exception(ERR_JSON_SYNTAX);
}
return ch;
}
int json_read_string(InputStream * inp, char * str, size_t size) {
unsigned i = 0;
int ch = inp->read(inp);
if (ch == 'n') {
if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
str[0] = 0;
return -1;
}
if (ch != '"') exception(ERR_JSON_SYNTAX);
for (;;) {
ch = inp->read(inp);
if (ch == '"') break;
if (ch == '\\') ch = read_esc_char(inp);
if (i < size - 1) str[i] = (char)ch;
i++;
}
if (i < size) str[i] = 0;
else str[size - 1] = 0;
return i;
}
char * json_read_alloc_string(InputStream * inp) {
char * str = NULL;
unsigned i = 0;
int ch = inp->read(inp);
if (ch == 'n') {
if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
return NULL;
}
if (ch != '"') exception(ERR_JSON_SYNTAX);
for (;;) {
ch = inp->read(inp);
if (ch == '"') break;
if (ch == '\\') ch = read_esc_char(inp);
if (i >= buf_size) {
int new_size = buf_size == 0 ? 0x1000 : buf_size * 2;
char * tmp = (char *)loc_alloc(new_size);
if (i > 0) memcpy(tmp, buf, i);
if (buf != NULL) loc_free(buf);
buf = tmp;
buf_size = new_size;
}
buf[i++] = (char)ch;
}
str = (char *)loc_alloc(i + 1);
memcpy(str, buf, i);
str[i] = 0;
return str;
}
int json_read_boolean(InputStream * inp) {
int ch = inp->read(inp);
if (ch == 'f') {
if (inp->read(inp) != 'a') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 's') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'e') exception(ERR_JSON_SYNTAX);
return 0;
}
if (ch == 't') {
if (inp->read(inp) != 'r') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'e') exception(ERR_JSON_SYNTAX);
return 1;
}
exception(ERR_JSON_SYNTAX);
return 0;
}
long json_read_long(InputStream * inp) {
long res = 0;
int neg = 0;
int ch = inp->read(inp);
if (ch == '-') {
neg = 1;
ch = inp->read(inp);
}
if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX);
res = ch - '0';
while (1) {
ch = inp->peek(inp);
if (ch < '0' || ch > '9') break;
inp->read(inp);
res = res * 10 + (ch - '0');
}
if (neg) return -res;
return res;
}
unsigned long json_read_ulong(InputStream * inp) {
unsigned long res = 0;
int neg = 0;
int ch = inp->read(inp);
if (ch == '-') {
neg = 1;
ch = inp->read(inp);
}
if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX);
res = ch - '0';
while (1) {
ch = inp->peek(inp);
if (ch < '0' || ch > '9') break;
inp->read(inp);
res = res * 10 + (ch - '0');
}
if (neg) return ~res + 1;
return res;
}
int64 json_read_int64(InputStream * inp) {
int64 res = 0;
int neg = 0;
int ch = inp->read(inp);
if (ch == '-') {
neg = 1;
ch = inp->read(inp);
}
if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX);
res = ch - '0';
while (1) {
ch = inp->peek(inp);
if (ch < '0' || ch > '9') break;
inp->read(inp);
res = res * 10 + (ch - '0');
}
if (neg) return -res;
return res;
}
int json_read_struct(InputStream * inp, struct_call_back call_back, void * arg) {
int ch = inp->read(inp);
if (ch == 'n') {
if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
return 0;
}
if (ch == '{') {
ch = inp->read(inp);
if (ch != '}') {
for (;;) {
int nm_len = 0;
char nm[256];
if (ch != '"') exception(ERR_JSON_SYNTAX);
for (;;) {
ch = inp->read(inp);
if (ch == '"') break;
if (ch == '\\') {
ch = inp->read(inp);
switch (ch) {
case '"': break;
case '\\': break;
case '/': break;
case 'b': ch = '\b'; break;
case 'f': ch = '\f'; break;
case 'n': ch = '\n'; break;
case 'r': ch = '\r'; break;
case 't': ch = '\t'; break;
case 'u': ch = readHexChar(inp); break;
default: exception(ERR_JSON_SYNTAX);
}
}
if (nm_len < sizeof(nm) - 1) {
nm[nm_len] = (char)ch;
nm_len++;
}
}
nm[nm_len] = 0;
ch = inp->read(inp);
if (ch != ':') exception(ERR_JSON_SYNTAX);
call_back(inp, nm, arg);
ch = inp->read(inp);
if (ch == '}') break;
if (ch != ',') exception(ERR_JSON_SYNTAX);
ch = inp->read(inp);
}
}
return 1;
}
exception(ERR_JSON_SYNTAX);
return 0;
}
char ** json_read_alloc_string_array(InputStream * inp, int * pos) {
int ch = inp->read(inp);
*pos = 0;
if (ch == 'n') {
if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
return NULL;
}
else if (ch != '[') {
exception(ERR_PROTOCOL);
return NULL;
}
else {
static int * len_buf = NULL;
static int len_buf_size = 0;
int buf_pos = 0;
int len_pos = 0;
int i, j;
char * str = NULL;
char ** arr = NULL;
if (inp->peek(inp) == ']') {
inp->read(inp);
}
else {
while (1) {
int ch;
int len;
if (buf == NULL) {
buf_size = 0x1000;
buf = (char *)loc_alloc(buf_size);
}
else if (buf_size - buf_pos < 0x200) {
char * tmp = (char *)loc_alloc(buf_size * 2);
memcpy(tmp, buf, buf_pos);
loc_free(buf);
buf = tmp;
buf_size *= 2;
}
if (len_pos >= len_buf_size) {
len_buf_size = len_buf_size == 0 ? 0x100 : len_buf_size * 2;
len_buf = (int *)loc_realloc(len_buf, len_buf_size * sizeof(int));
}
len = json_read_string(inp, buf + buf_pos, buf_size - buf_pos);
if (len >= (int)(buf_size - buf_pos)) exception(ERR_BUFFER_OVERFLOW);
len_buf[len_pos++] = len;
buf_pos += len + 1;
ch = inp->read(inp);
if (ch == ',') continue;
if (ch == ']') break;
exception(ERR_JSON_SYNTAX);
}
}
arr = (char **)loc_alloc((len_pos + 1) * sizeof(char *) + buf_pos);
str = (char *)(arr + len_pos + 1);
memcpy(str, buf, buf_pos);
j = 0;
for (i = 0; i < len_pos; i++) {
arr[i] = str + j;
j += len_buf[i] + 1;
}
arr[len_pos] = NULL;
*pos = len_pos;
return arr;
}
}
void json_skip_object(InputStream * inp) {
int ch = inp->read(inp);
if (ch == 'n') {
if (inp->read(inp) != 'u') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
if (inp->read(inp) != 'l') exception(ERR_JSON_SYNTAX);
return;
}
if (ch == '"') {
for (;;) {
ch = inp->read(inp);
if (ch == '"') break;
if (ch == '\\') {
if (inp->read(inp) == 'u') readHexChar(inp);
}
}
return;
}
if (ch == '-' || ch >= '0' && ch <= '9') {
while (1) {
ch = inp->peek(inp);
if (ch < '0' || ch > '9') break;
inp->read(inp);
}
return;
}
if (ch == '[') {
if (inp->peek(inp) == ']') {
inp->read(inp);
}
else {
while (1) {
int ch;
json_skip_object(inp);
ch = inp->read(inp);
if (ch == ',') continue;
if (ch == ']') break;
exception(ERR_JSON_SYNTAX);
}
}
return;
}
if (ch == '{') {
if (inp->peek(inp) == '}') {
inp->read(inp);
}
else {
while (1) {
int ch;
json_skip_object(inp);
if (inp->read(inp) != ':') exception(ERR_JSON_SYNTAX);
json_skip_object(inp);
ch = inp->read(inp);
if (ch == ',') continue;
if (ch == '}') break;
exception(ERR_JSON_SYNTAX);
}
}
}
exception(ERR_JSON_SYNTAX);
}
void write_errno(OutputStream * out, int err) {
char * msg = NULL;
json_write_long(out, err);
out->write(out, 0);
if (err != 0) msg = errno_to_str(err);
json_write_string(out, msg);
out->write(out, 0);
}
void write_err_msg(OutputStream * out, int err, char * msg) {
json_write_long(out, err);
out->write(out, 0);
if (err == 0) {
write_string(out, "null");
}
else {
char * str = errno_to_str(err);
out->write(out, '"');
while (*str) json_write_char(out, *str++);
if (msg != NULL) {
out->write(out, ':');
out->write(out, ' ');
while (*msg) json_write_char(out, *msg++);
}
out->write(out, '"');
}
out->write(out, 0);
}