| /******************************************************************************* |
| * Copyright (c) 2007, 2010 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 |
| * and Eclipse Distribution License v1.0 which accompany this distribution. |
| * The Eclipse Public License is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * Contributors: |
| * Wind River Systems - initial API and implementation |
| * Michael Sills-Lavoie(École Polytechnique de Montréal) - ZeroCopy support |
| * * * - json_splice_binary |
| *******************************************************************************/ |
| |
| /* |
| * 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 <config.h> |
| #ifdef ENABLE_STREAM_MACROS |
| #undef ENABLE_STREAM_MACROS |
| #endif |
| #define ENABLE_STREAM_MACROS 1 |
| |
| #include <stdio.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <framework/json.h> |
| #include <framework/myalloc.h> |
| #include <framework/exceptions.h> |
| #include <framework/base64.h> |
| |
| #define ENCODING_BINARY 0 |
| #define ENCODING_BASE64 1 |
| |
| static char * buf = NULL; |
| static size_t buf_pos = 0; |
| static size_t buf_size = 0; |
| |
| static void realloc_buf(void) { |
| if (buf == NULL) { |
| buf_size = 0x1000; |
| buf = (char *)loc_alloc(buf_size); |
| } |
| else { |
| buf_size *= 2; |
| buf = (char *)loc_realloc(buf, buf_size); |
| } |
| } |
| |
| #define buf_add(ch) { if (buf_pos >= buf_size) realloc_buf(); buf[buf_pos++] = (char)(ch); } |
| |
| void json_write_ulong(OutputStream * out, unsigned long n) { |
| if (n >= 10) { |
| json_write_ulong(out, n / 10); |
| n = n % 10; |
| } |
| write_stream(out, n + '0'); |
| } |
| |
| void json_write_long(OutputStream * out, long n) { |
| if (n < 0) { |
| write_stream(out, '-'); |
| n = -n; |
| } |
| json_write_ulong(out, (unsigned long)n); |
| } |
| |
| void json_write_uint64(OutputStream * out, uint64_t n) { |
| if (n >= 10) { |
| json_write_uint64(out, n / 10); |
| n = n % 10; |
| } |
| write_stream(out, (int)n + '0'); |
| } |
| |
| void json_write_int64(OutputStream * out, int64_t n) { |
| if (n < 0) { |
| write_stream(out, '-'); |
| n = -n; |
| } |
| json_write_uint64(out, (uint64_t)n); |
| } |
| |
| void json_write_double(OutputStream * out, double n) { |
| char buf[256]; |
| snprintf(buf, sizeof(buf), "%.18g", n); |
| write_string(out, buf); |
| } |
| |
| void json_write_boolean(OutputStream * out, int b) { |
| if (b) write_string(out, "true"); |
| else write_string(out, "false"); |
| } |
| |
| static int 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 < ' ') { |
| write_stream(out, '\\'); |
| write_stream(out, 'u'); |
| write_stream(out, '0'); |
| write_stream(out, '0'); |
| write_stream(out, hex_digit(n >> 4)); |
| write_stream(out, hex_digit(n)); |
| } |
| else { |
| if (n == '"' || n == '\\') write_stream(out, '\\'); |
| write_stream(out, n); |
| } |
| } |
| |
| void json_write_string(OutputStream * out, const char * str) { |
| if (str == NULL) { |
| write_string(out, "null"); |
| } |
| else { |
| write_stream(out, '"'); |
| for (;;) { |
| unsigned char ch = (unsigned char)*str++; |
| while (ch >= ' ') { |
| if (ch == '"' || ch == '\\') write_stream(out, '\\'); |
| write_stream(out, ch); |
| ch = (unsigned char)*str++; |
| } |
| if (ch == 0) break; |
| json_write_char(out, ch); |
| } |
| write_stream(out, '"'); |
| } |
| } |
| |
| void json_write_string_len(OutputStream * out, const char * str, size_t len) { |
| if (str == NULL) { |
| write_string(out, "null"); |
| } |
| else { |
| write_stream(out, '"'); |
| while (len > 0) { |
| json_write_char(out, *str++); |
| len--; |
| } |
| write_stream(out, '"'); |
| } |
| } |
| |
| static int readHex(InputStream * inp) { |
| int ch = read_stream(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 unsigned read_esc_char(InputStream * inp, char * utf8) { |
| int ch = read_stream(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); |
| } |
| /* 'ch' can be wide character - convert it to UTF-8 sequence */ |
| if (ch < 0x80) { |
| utf8[0] = (char)ch; |
| return 1; |
| } |
| if (ch < 0x800) { |
| utf8[0] = (char)((ch >> 6) | 0xc0); |
| utf8[1] = (char)((ch & 0x3f) | 0x80); |
| return 2; |
| } |
| utf8[0] = (char)((ch >> 12) | 0xe0); |
| utf8[1] = (char)(((ch >> 6) & 0x3f) | 0x80); |
| utf8[2] = (char)((ch & 0x3f) | 0x80); |
| return 3; |
| } |
| |
| int json_read_string(InputStream * inp, char * str, size_t size) { |
| unsigned i = 0; |
| int ch = read_stream(inp); |
| if (ch == 'n') { |
| if (read_stream(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| str[0] = 0; |
| return -1; |
| } |
| if (ch != '"') exception(ERR_JSON_SYNTAX); |
| for (;;) { |
| ch = read_stream(inp); |
| if (ch < 0) exception(ERR_JSON_SYNTAX); |
| if (ch == '"') break; |
| if (ch == '\\') { |
| char utf8[4]; |
| unsigned l = read_esc_char(inp, utf8); |
| unsigned n; |
| for (n = 0; n < l; n++, i++) { |
| if (i < size - 1) str[i] = utf8[n]; |
| } |
| } |
| else { |
| 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; |
| int ch = read_stream(inp); |
| if (ch == 'n') { |
| if (read_stream(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| return NULL; |
| } |
| buf_pos = 0; |
| if (ch != '"') exception(ERR_JSON_SYNTAX); |
| for (;;) { |
| ch = read_stream(inp); |
| if (ch < 0) exception(ERR_JSON_SYNTAX); |
| if (ch == '"') break; |
| if (ch == '\\') { |
| char utf8[4]; |
| unsigned l = read_esc_char(inp, utf8); |
| unsigned n; |
| for (n = 0; n < l; n++) buf_add(utf8[n]); |
| } |
| else { |
| buf_add(ch); |
| } |
| } |
| buf_add(0); |
| str = (char *)loc_alloc(buf_pos); |
| memcpy(str, buf, buf_pos); |
| return str; |
| } |
| |
| int json_read_boolean(InputStream * inp) { |
| int ch = read_stream(inp); |
| if (ch == 'f') { |
| if (read_stream(inp) != 'a') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 's') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'e') exception(ERR_JSON_SYNTAX); |
| return 0; |
| } |
| if (ch == 't') { |
| if (read_stream(inp) != 'r') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (read_stream(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 = read_stream(inp); |
| if (ch == '-') { |
| neg = 1; |
| ch = read_stream(inp); |
| } |
| if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX); |
| res = ch - '0'; |
| for (;;) { |
| ch = peek_stream(inp); |
| if (ch < '0' || ch > '9') break; |
| read_stream(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 = read_stream(inp); |
| if (ch == '-') { |
| neg = 1; |
| ch = read_stream(inp); |
| } |
| if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX); |
| res = ch - '0'; |
| for (;;) { |
| ch = peek_stream(inp); |
| if (ch < '0' || ch > '9') break; |
| read_stream(inp); |
| res = res * 10 + (ch - '0'); |
| } |
| if (neg) return ~res + 1; |
| return res; |
| } |
| |
| int64_t json_read_int64(InputStream * inp) { |
| int64_t res = 0; |
| int neg = 0; |
| int ch = read_stream(inp); |
| if (ch == '-') { |
| neg = 1; |
| ch = read_stream(inp); |
| } |
| if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX); |
| res = ch - '0'; |
| for (;;) { |
| ch = peek_stream(inp); |
| if (ch < '0' || ch > '9') break; |
| read_stream(inp); |
| res = res * 10 + (ch - '0'); |
| } |
| if (neg) return -res; |
| return res; |
| } |
| |
| uint64_t json_read_uint64(InputStream * inp) { |
| uint64_t res = 0; |
| int neg = 0; |
| int ch = read_stream(inp); |
| if (ch == '-') { |
| neg = 1; |
| ch = read_stream(inp); |
| } |
| if (ch < '0' || ch > '9') exception(ERR_JSON_SYNTAX); |
| res = ch - '0'; |
| for (;;) { |
| ch = peek_stream(inp); |
| if (ch < '0' || ch > '9') break; |
| read_stream(inp); |
| res = res * 10 + (ch - '0'); |
| } |
| if (neg) return ~res + 1; |
| return res; |
| } |
| |
| double json_read_double(InputStream * inp) { |
| char buf[256]; |
| int pos = 0; |
| double n = 0; |
| char * end = buf; |
| |
| for (;;) { |
| int ch = peek_stream(inp); |
| switch (ch) { |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| case '-': |
| case '+': |
| case 'e': |
| case 'E': |
| case '.': |
| if (pos >= (int)sizeof(buf) - 1) exception(ERR_BUFFER_OVERFLOW); |
| buf[pos++] = (char)read_stream(inp); |
| continue; |
| } |
| break; |
| } |
| if (pos == 0) exception(ERR_JSON_SYNTAX); |
| buf[pos++] = 0; |
| n = strtod(buf, &end); |
| if (*end != 0) exception(ERR_JSON_SYNTAX); |
| return n; |
| } |
| |
| int json_read_struct(InputStream * inp, JsonStructCallBack * call_back, void * arg) { |
| int ch = read_stream(inp); |
| if (ch == 'n') { |
| if (read_stream(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| return 0; |
| } |
| if (ch == '{') { |
| ch = peek_stream(inp); |
| if (ch != '}') { |
| for (;;) { |
| char nm[256]; |
| json_read_string(inp, nm, sizeof(nm)); |
| ch = read_stream(inp); |
| if (ch != ':') exception(ERR_JSON_SYNTAX); |
| call_back(inp, nm, arg); |
| ch = read_stream(inp); |
| if (ch == '}') break; |
| if (ch != ',') exception(ERR_JSON_SYNTAX); |
| } |
| } |
| else { |
| read_stream(inp); |
| } |
| return 1; |
| } |
| exception(ERR_JSON_SYNTAX); |
| return 0; |
| } |
| |
| char ** json_read_alloc_string_array(InputStream * inp, int * cnt) { |
| int ch = read_stream(inp); |
| if (ch == 'n') { |
| if (read_stream(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (cnt) *cnt = 0; |
| return NULL; |
| } |
| else if (ch != '[') { |
| exception(ERR_PROTOCOL); |
| return NULL; |
| } |
| else { |
| static size_t * len_buf = NULL; |
| static unsigned len_buf_size = 0; |
| unsigned len_pos = 0; |
| |
| unsigned i; |
| size_t j; |
| char * str = NULL; |
| char ** arr = NULL; |
| |
| buf_pos = 0; |
| |
| if (peek_stream(inp) == ']') { |
| read_stream(inp); |
| } |
| else { |
| for (;;) { |
| int ch = read_stream(inp); |
| size_t len = 0; |
| if (len_pos >= len_buf_size) { |
| len_buf_size = len_buf_size == 0 ? 0x100 : len_buf_size * 2; |
| len_buf = (size_t *)loc_realloc(len_buf, len_buf_size * sizeof(size_t)); |
| } |
| if (ch == 'n') { |
| if (read_stream(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| } |
| else { |
| size_t buf_pos0 = buf_pos; |
| if (ch != '"') exception(ERR_JSON_SYNTAX); |
| for (;;) { |
| ch = read_stream(inp); |
| if (ch < 0) exception(ERR_JSON_SYNTAX); |
| if (ch == '"') break; |
| if (ch == '\\') { |
| char utf8[4]; |
| unsigned l = read_esc_char(inp, utf8); |
| unsigned n; |
| for (n = 0; n < l; n++) buf_add(utf8[n]); |
| } |
| else { |
| buf_add(ch); |
| } |
| } |
| len = buf_pos - buf_pos0; |
| } |
| buf_add(0); |
| len_buf[len_pos++] = len; |
| ch = read_stream(inp); |
| if (ch == ',') continue; |
| if (ch == ']') break; |
| exception(ERR_JSON_SYNTAX); |
| } |
| } |
| buf_add(0); |
| 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; |
| if (cnt) *cnt = len_pos; |
| return arr; |
| } |
| } |
| |
| /* |
| * json_read_array - generic read array function |
| * |
| * This function will call the call_back with inp and arg as |
| * arguments for each element of the list. |
| * Return 0 if null, 1 otherwise |
| */ |
| int json_read_array(InputStream * inp, JsonArrayCallBack * call_back, void * arg) { |
| int ch = read_stream(inp); |
| if (ch == 'n') { |
| if (read_stream(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| return 0; |
| } |
| if (ch != '[') { |
| exception(ERR_PROTOCOL); |
| return 1; |
| } |
| if (peek_stream(inp) == ']') { |
| read_stream(inp); |
| return 1; |
| } |
| for (;;) { |
| call_back(inp, arg); |
| ch = read_stream(inp); |
| if (ch == ',') continue; |
| if (ch == ']') break; |
| exception(ERR_JSON_SYNTAX); |
| } |
| return 1; |
| } |
| |
| void json_read_binary_start(JsonReadBinaryState * state, InputStream * inp) { |
| int ch = read_stream(inp); |
| state->inp = inp; |
| state->rem = 0; |
| state->size_start = 0; |
| state->size_done = 0; |
| if (ch == '(') { |
| state->encoding = ENCODING_BINARY; |
| state->size_start = json_read_ulong(inp); |
| if (read_stream(inp) != ')') exception(ERR_JSON_SYNTAX); |
| } |
| else if (ch == '"') { |
| state->encoding = ENCODING_BASE64; |
| } |
| else if (ch == 'n') { |
| if (read_stream(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| state->encoding = ENCODING_BINARY; |
| state->size_start = 0; |
| } |
| else { |
| exception(ERR_JSON_SYNTAX); |
| } |
| } |
| |
| size_t json_read_binary_data(JsonReadBinaryState * state, void * buf, size_t len) { |
| size_t res = 0; |
| uint8_t * ptr = (uint8_t *)buf; |
| if (state->encoding == ENCODING_BINARY) { |
| if (len > (size_t)(state->size_start - state->size_done)) len = state->size_start - state->size_done; |
| while (res < len) ptr[res++] = (uint8_t)read_stream(state->inp); |
| } |
| else { |
| while (len > 0) { |
| if (state->rem > 0) { |
| unsigned i = 0; |
| while (i < state->rem && i < len) *ptr++ = state->buf[i++]; |
| len -= i; |
| res += i; |
| if (i < state->rem) { |
| int j = 0; |
| while (i < state->rem) state->buf[j++] = state->buf[i++]; |
| state->rem = j; |
| return res; |
| } |
| state->rem = 0; |
| } |
| if (len >= 3) { |
| size_t i = read_base64(state->inp, (char *)ptr, len); |
| if (i == 0) break; |
| ptr += i; |
| len -= i; |
| res += i; |
| } |
| else { |
| state->rem = read_base64(state->inp, state->buf, 3); |
| if (state->rem == 0) break; |
| } |
| } |
| } |
| state->size_done += res; |
| return res; |
| } |
| |
| void json_read_binary_end(JsonReadBinaryState * state) { |
| if (state->rem != 0) exception(ERR_JSON_SYNTAX); |
| if (state->encoding == ENCODING_BINARY) { |
| assert(state->size_start == state->size_done); |
| } |
| else { |
| if (read_stream(state->inp) != '"') exception(ERR_JSON_SYNTAX); |
| } |
| } |
| |
| char * json_read_alloc_binary(InputStream * inp, size_t * size) { |
| char * data = NULL; |
| int ch = peek_stream(inp); |
| *size = 0; |
| if (ch == 'n') { |
| read_stream(inp); |
| if (read_stream(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (read_stream(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| } |
| else { |
| JsonReadBinaryState state; |
| |
| json_read_binary_start(&state, inp); |
| |
| buf_pos = 0; |
| for (;;) { |
| size_t rd; |
| if (buf_pos >= buf_size) realloc_buf(); |
| rd = json_read_binary_data(&state, buf + buf_pos, buf_size - buf_pos); |
| if (rd == 0) break; |
| buf_pos += rd; |
| } |
| |
| assert(state.size_start <= 0 || buf_pos == state.size_start); |
| |
| json_read_binary_end(&state); |
| data = (char *)loc_alloc(buf_pos); |
| memcpy(data, buf, buf_pos); |
| *size = buf_pos; |
| } |
| return data; |
| } |
| |
| void json_write_binary_start(JsonWriteBinaryState * state, OutputStream * out, size_t size) { |
| state->out = out; |
| state->rem = 0; |
| state->encoding = out->supports_zero_copy && size > 0 ? ENCODING_BINARY : ENCODING_BASE64; |
| state->size_start = size; |
| state->size_done = 0; |
| if (state->encoding == ENCODING_BINARY) { |
| write_stream(state->out, '('); |
| json_write_ulong(out, size); |
| write_stream(state->out, ')'); |
| } |
| else { |
| write_stream(state->out, '"'); |
| } |
| } |
| |
| void json_write_binary_data(JsonWriteBinaryState * state, const void * data, size_t len) { |
| if (len <= 0) return; |
| if (state->encoding == (int)ENCODING_BINARY) { |
| write_block_stream(state->out, (const char *)data, len); |
| } |
| else { |
| const uint8_t * ptr = (uint8_t *)data; |
| size_t rem = state->rem; |
| |
| if (rem > 0) { |
| while (rem < 3 && len > 0) { |
| state->buf[rem++] = *ptr++; |
| len--; |
| } |
| assert(rem <= 3); |
| if (rem >= 3) { |
| write_base64(state->out, state->buf, rem); |
| rem = 0; |
| } |
| } |
| if (len > 0) { |
| assert(rem == 0); |
| rem = len % 3; |
| len -= rem; |
| write_base64(state->out, (char *)ptr, len); |
| if (rem > 0) memcpy(state->buf, ptr + len, rem); |
| } |
| state->rem = rem; |
| } |
| state->size_done += len; |
| } |
| |
| void json_write_binary_end(JsonWriteBinaryState * state) { |
| if (state->encoding == ENCODING_BINARY) { |
| assert(state->size_start == state->size_done); |
| } |
| else { |
| size_t rem; |
| |
| if ((rem = state->rem) > 0) { |
| write_base64(state->out, state->buf, rem); |
| } |
| write_stream(state->out, '"'); |
| } |
| } |
| |
| void json_write_binary(OutputStream * out, const void * data, size_t size) { |
| if (data == NULL) { |
| write_string(out, "null"); |
| } |
| else { |
| JsonWriteBinaryState state; |
| json_write_binary_start(&state, out, size); |
| json_write_binary_data(&state, data, size); |
| json_write_binary_end(&state); |
| } |
| } |
| |
| void json_splice_binary(OutputStream * out, int fd, size_t size) { |
| json_splice_binary_offset(out, fd, size, NULL); |
| } |
| |
| void json_splice_binary_offset(OutputStream * out, int fd, size_t size, off_t * offset) { |
| if (out->supports_zero_copy && size > 0) { |
| write_stream(out, '('); |
| json_write_ulong(out, size); |
| write_stream(out, ')'); |
| while (size > 0) { |
| ssize_t rd = splice_block_stream(out, fd, size, offset); |
| if (rd < 0) exception(errno); |
| if (rd == 0) exception(ERR_EOF); |
| size -= rd; |
| } |
| } |
| else { |
| char buffer[0x1000]; |
| JsonWriteBinaryState state; |
| json_write_binary_start(&state, out, size); |
| |
| while (size > 0) { |
| ssize_t rd = 0; |
| if (offset != NULL) { |
| rd = pread(fd, buffer, size < sizeof(buffer) ? size : sizeof(buffer), *offset); |
| if (rd > 0) *offset += rd; |
| } |
| else { |
| rd = read(fd, buffer, size < sizeof(buffer) ? size : sizeof(buffer)); |
| } |
| if (rd < 0) exception(errno); |
| if (rd == 0) exception(ERR_EOF); |
| json_write_binary_data(&state, buffer, rd); |
| size -= rd; |
| } |
| json_write_binary_end(&state); |
| } |
| } |
| |
| static int skip_char(InputStream * inp) { |
| int ch = read_stream(inp); |
| buf_add(ch); |
| return ch; |
| } |
| |
| static void skip_object(InputStream * inp) { |
| int ch = skip_char(inp); |
| if (ch == 'n') { |
| if (skip_char(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (skip_char(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (skip_char(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| return; |
| } |
| if (ch == 'f') { |
| if (skip_char(inp) != 'a') exception(ERR_JSON_SYNTAX); |
| if (skip_char(inp) != 'l') exception(ERR_JSON_SYNTAX); |
| if (skip_char(inp) != 's') exception(ERR_JSON_SYNTAX); |
| if (skip_char(inp) != 'e') exception(ERR_JSON_SYNTAX); |
| return; |
| } |
| if (ch == 't') { |
| if (skip_char(inp) != 'r') exception(ERR_JSON_SYNTAX); |
| if (skip_char(inp) != 'u') exception(ERR_JSON_SYNTAX); |
| if (skip_char(inp) != 'e') exception(ERR_JSON_SYNTAX); |
| return; |
| } |
| if (ch == '"') { |
| for (;;) { |
| ch = skip_char(inp); |
| if (ch == '"') break; |
| if (ch == '\\') skip_char(inp); |
| } |
| return; |
| } |
| if (ch == '-' || (ch >= '0' && ch <= '9')) { |
| for (;;) { |
| ch = peek_stream(inp); |
| if ((ch < '0' || ch > '9') && ch != '.' |
| && ch != 'e' && ch != 'E' && ch != '-' && ch != '+') break; |
| skip_char(inp); |
| } |
| return; |
| } |
| if (ch == '[') { |
| if (peek_stream(inp) == ']') { |
| skip_char(inp); |
| } |
| else { |
| for (;;) { |
| int ch; |
| skip_object(inp); |
| ch = skip_char(inp); |
| if (ch == ',') continue; |
| if (ch == ']') break; |
| exception(ERR_JSON_SYNTAX); |
| } |
| } |
| return; |
| } |
| if (ch == '{') { |
| if (peek_stream(inp) == '}') { |
| skip_char(inp); |
| } |
| else { |
| for (;;) { |
| int ch; |
| skip_object(inp); |
| if (skip_char(inp) != ':') exception(ERR_JSON_SYNTAX); |
| skip_object(inp); |
| ch = skip_char(inp); |
| if (ch == ',') continue; |
| if (ch == '}') break; |
| exception(ERR_JSON_SYNTAX); |
| } |
| } |
| return; |
| } |
| exception(ERR_JSON_SYNTAX); |
| } |
| |
| char * json_read_object(InputStream * inp) { |
| char * str = NULL; |
| buf_pos = 0; |
| skip_object(inp); |
| buf_add(0); |
| str = (char *)loc_alloc(buf_pos); |
| memcpy(str, buf, buf_pos); |
| return str; |
| } |
| |
| void json_skip_object(InputStream * inp) { |
| buf_pos = 0; |
| skip_object(inp); |
| } |
| |
| static void read_errno_param(InputStream * inp, void * x) { |
| ErrorReport * err = (ErrorReport *)x; |
| if (err->param_cnt >= err->param_max) { |
| err->param_max += 4; |
| err->params = (char **)loc_realloc(err->params, err->param_max * sizeof(char *)); |
| } |
| err->params[err->param_cnt++] = json_read_object(inp); |
| } |
| |
| int read_errno(InputStream * inp) { |
| int no = 0; |
| ErrorReport * err = NULL; |
| int ch = read_stream(inp); |
| if (ch == 0) return 0; |
| if (ch != '{') exception(ERR_JSON_SYNTAX); |
| if (peek_stream(inp) == '}') { |
| read_stream(inp); |
| } |
| else { |
| for (;;) { |
| char name[256]; |
| json_read_string(inp, name, sizeof(name)); |
| if (read_stream(inp) != ':') exception(ERR_JSON_SYNTAX); |
| if (err == NULL) err = create_error_report(); |
| if (strcmp(name, "Code") == 0) { |
| err->code = json_read_long(inp); |
| } |
| else if (strcmp(name, "Time") == 0) { |
| err->time_stamp = json_read_uint64(inp); |
| } |
| else if (strcmp(name, "Format") == 0) { |
| err->format = json_read_alloc_string(inp); |
| } |
| else if (strcmp(name, "Params") == 0) { |
| json_read_array(inp, read_errno_param, err); |
| } |
| else { |
| ErrorReportItem * i = (ErrorReportItem *)loc_alloc_zero(sizeof(ErrorReportItem)); |
| i->name = loc_strdup(name); |
| i->value = json_read_object(inp); |
| i->next = err->props; |
| err->props = i; |
| } |
| ch = read_stream(inp); |
| if (ch == ',') continue; |
| if (ch == '}') break; |
| exception(ERR_JSON_SYNTAX); |
| } |
| } |
| if (read_stream(inp) != 0) exception(ERR_JSON_SYNTAX); |
| if (err == NULL) return 0; |
| if (err->code != 0) no = set_error_report_errno(err); |
| release_error_report(err); |
| return no; |
| } |
| |
| static void write_error_props(OutputStream * out, ErrorReport * rep) { |
| ErrorReportItem * i = rep->props; |
| |
| if (rep->time_stamp != 0) { |
| write_stream(out, ','); |
| json_write_string(out, "Time"); |
| write_stream(out, ':'); |
| json_write_uint64(out, rep->time_stamp); |
| } |
| |
| if (rep->format != NULL) { |
| write_stream(out, ','); |
| json_write_string(out, "Format"); |
| write_stream(out, ':'); |
| json_write_string(out, rep->format); |
| } |
| |
| if (rep->param_cnt > 0) { |
| int n = 0; |
| write_stream(out, ','); |
| json_write_string(out, "Params"); |
| write_stream(out, ':'); |
| write_stream(out, '['); |
| for (n = 0; n < rep->param_cnt; n++) { |
| if (n > 0) write_stream(out, ','); |
| write_string(out, rep->params[n]); |
| } |
| write_stream(out, ']'); |
| } |
| |
| while (i != NULL) { |
| write_stream(out, ','); |
| json_write_string(out, i->name); |
| write_stream(out, ':'); |
| write_string(out, i->value); |
| i = i->next; |
| } |
| } |
| |
| void write_error_object(OutputStream * out, int err) { |
| ErrorReport * rep = get_error_report(err); |
| if (rep == NULL) { |
| write_string(out, "null"); |
| } |
| else { |
| write_stream(out, '{'); |
| |
| json_write_string(out, "Code"); |
| write_stream(out, ':'); |
| json_write_long(out, rep->code); |
| |
| write_error_props(out, rep); |
| release_error_report(rep); |
| |
| write_stream(out, '}'); |
| } |
| } |
| |
| void write_errno(OutputStream * out, int err) { |
| if (err != 0) write_error_object(out, err); |
| write_stream(out, 0); |
| } |
| |
| void write_service_error(OutputStream * out, int err, const char * service_name, int service_error) { |
| ErrorReport * rep = get_error_report(err); |
| if (rep != NULL) { |
| write_stream(out, '{'); |
| |
| json_write_string(out, "Service"); |
| write_stream(out, ':'); |
| json_write_string(out, service_name); |
| |
| write_stream(out, ','); |
| |
| json_write_string(out, "Code"); |
| write_stream(out, ':'); |
| json_write_long(out, service_error); |
| |
| write_error_props(out, rep); |
| release_error_report(rep); |
| |
| write_stream(out, '}'); |
| } |
| write_stream(out, 0); |
| } |