blob: b99ba076bf60cdfe3019b68885c33428e9ac2de5 [file] [log] [blame]
// Copyright 2012 Intel Corporation
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include <wchar.h>
#include <assert.h>
#include "json.h"
#define JSON_TRUE L"true"
#define JSON_FALSE L"false"
#define JSON_NULL L"null"
struct _JsonItem {
// Common fields
JsonValueType value_type;
union {
wchar_t *as_string;
int as_int;
INT64 as_int64;
double as_double;
BOOL as_bool;
JsonArray as_array;
JsonObject as_object;
} value;
size_t value_as_string_len;
struct _JsonItem *next;
};
struct _JsonObjectItem {
// Common fields
JsonValueType value_type;
union {
wchar_t *as_string;
int as_int;
double as_double;
JsonArray as_array;
JsonObject as_object;
} value;
size_t value_as_string_len;
struct _JsonItem *next;
// Specific fields
wchar_t *tag;
size_t tag_len;
};
struct _JsonCursor {
wchar_t *buf;
size_t max;
size_t pos;
};
typedef struct _JsonCursor JsonCursor;
static BOOL json_parse_array(JsonCursor *cursor, JsonArray array);
static BOOL json_parse_object(JsonCursor *cursor, JsonObject object);
static void json_init_cursor(wchar_t * buf, JsonCursor *cursor)
{
memset((void *)cursor, 0, sizeof(JsonCursor));
cursor->buf = buf;
cursor->max = wcslen(buf);
}
static BOOL json_parse_string(JsonCursor *cursor, JsonItem item)
{
wchar_t *buf = cursor->buf;
BOOL escape;
cursor->pos++; // Initial '"'
item->value.as_string = buf + cursor->pos;
while (cursor->pos < cursor->max) {
if (buf[cursor->pos] == '"' && !escape) {
cursor->pos++; // Trailing '"'
break;
}
if (buf[cursor->pos] == '\\' && !escape)
escape = TRUE;
else
escape = FALSE;
cursor->pos++;
item->value_as_string_len++;
}
return (cursor->pos == cursor->max) ? FALSE : TRUE;
}
static BOOL json_parse_number(JsonCursor *cursor, JsonItem item)
{
wchar_t *buf = cursor->buf;
wchar_t *value = buf + cursor->pos;
size_t value_len = 0;
BOOL has_dot = (buf[cursor->pos] == '.') ? TRUE : FALSE;
INT64 val64;
cursor->pos++;
value_len++;
while (cursor->pos < cursor->max) {
if (buf[cursor->pos] == '.') {
if (has_dot)
return FALSE;
else
has_dot = TRUE;
} else if (!iswdigit(buf[cursor->pos]))
break;
cursor->pos++;
value_len++;
}
if (cursor->pos == cursor->max)
return FALSE;
item->value_type = (has_dot) ? JSON_VALUE_DOUBLE : JSON_VALUE_INT;
if (item->value_type == JSON_VALUE_INT) {
if (_snwscanf_s(value, value_len, L"%I64d", &val64) != 1)
return FALSE;
if (val64 > MAXINT) {
item->value.as_int64 = val64;
item->value_type = JSON_VALUE_INT64;
} else
item->value.as_int = (int) val64;
item->value.as_int64 = val64;
} else {
if (_snwscanf_s(value, value_len, L"%f", &item->value.as_double) != 1)
return FALSE;
}
return TRUE;
}
static BOOL json_parse_value(JsonCursor *cursor, JsonItem item)
{
wchar_t *buf = cursor->buf;
if (buf[cursor->pos] == '\"') {
item->value_type = JSON_VALUE_STRING;
return json_parse_string(cursor, item);
} else if (iswdigit(buf[cursor->pos]) || buf[cursor->pos] == '-' || buf[cursor->pos] == '.') {
return json_parse_number(cursor, item);
} else if (buf[cursor->pos] == '[') {
item->value_type = JSON_VALUE_ARRAY;
item->value.as_array = (JsonArray) calloc(1, sizeof(struct _JsonItem));
return json_parse_array(cursor, item->value.as_array);
} else if (buf[cursor->pos] == '{') {
item->value_type = JSON_VALUE_OBJECT;
item->value.as_object = (JsonObject) calloc(1, sizeof(struct _JsonObjectItem));
return json_parse_object(cursor, item->value.as_object);
} else if ((cursor->pos + wcslen(JSON_TRUE)) < cursor->max && !wcsncmp(buf + cursor->pos, JSON_TRUE, wcslen(JSON_TRUE))) {
item->value_type = JSON_VALUE_BOOL;
item->value.as_bool = TRUE;
cursor->pos += wcslen(JSON_TRUE);
return TRUE;
} else if ((cursor->pos + wcslen(JSON_FALSE)) < cursor->max && !wcsncmp(buf + cursor->pos, JSON_FALSE, wcslen(JSON_FALSE))) {
item->value_type = JSON_VALUE_BOOL;
item->value.as_bool = FALSE;
cursor->pos += wcslen(JSON_FALSE);
return TRUE;
} else if ((cursor->pos + wcslen(JSON_NULL)) < cursor->max && !wcsncmp(buf + cursor->pos, JSON_NULL, wcslen(JSON_NULL))) {
item->value_type = JSON_VALUE_NULL;
item->value.as_string = buf + cursor->pos;
item->value_as_string_len = wcslen(JSON_NULL);
cursor->pos += item->value_as_string_len;
return TRUE;
}
return FALSE;
}
static BOOL json_parse_object(JsonCursor *cursor, JsonObject object)
{
wchar_t *buf = cursor->buf;
struct _JsonObjectItem *item = (struct _JsonObjectItem *) object;
cursor->pos++;
if (buf[cursor->pos] == '}') {
cursor->pos++;
item->value_type = JSON_VALUE_EMPTY;
return TRUE;
}
while (cursor->pos < cursor->max) {
if (buf[cursor->pos] != '\"')
return FALSE;
// Tag
cursor->pos++;
item->tag = buf + cursor->pos;
while (cursor->pos < cursor->max && buf[cursor->pos] != '\"') {
cursor->pos++;
item->tag_len++;
}
if (cursor->pos == cursor->max)
return FALSE;
// Seprator
cursor->pos++;
if (buf[cursor->pos] != ':')
return FALSE;
// Value
cursor->pos++;
if(!json_parse_value(cursor, (JsonItem) item))
return FALSE;
// Next
if (buf[cursor->pos] == '}') {
cursor->pos++;
break;
}
if (buf[cursor->pos] != ',')
return FALSE;
cursor->pos++;
item->next = (JsonItem) calloc(1, sizeof(struct _JsonObjectItem));
item = (JsonObjectItem) item->next;
}
return TRUE;
}
static BOOL json_parse_array(JsonCursor *cursor, JsonArray item)
{
wchar_t *buf = cursor->buf;
if (buf[cursor->pos] != '[')
return FALSE;
cursor->pos++;
if (buf[cursor->pos] == ']') {
cursor->pos++;
item->value_type = JSON_VALUE_EMPTY;
return TRUE;
}
while (cursor->pos < cursor->max) {
if (!json_parse_value(cursor, (JsonItem) item))
return FALSE;
// Next
if (buf[cursor->pos] == ']') {
cursor->pos++;
break;
}
if (buf[cursor->pos] != ',')
return FALSE;
cursor->pos++;
item->next = (JsonItem) calloc(1, sizeof(struct _JsonItem));
item = (JsonItem) item->next;
}
return TRUE;
}
BOOL json_parse_args(wchar_t * buf, JsonArray *item)
{
JsonCursor cursor;
BOOL success;
json_init_cursor(buf, &cursor);
*item = (JsonArray) calloc(1, sizeof(struct _JsonItem));
success = json_parse_array(&cursor, *item);
if (success && cursor.pos == cursor.max)
return TRUE;
return FALSE;
}
static void json_free_item(JsonItem item)
{
JsonItem current;
while (item != NULL) {
current = item;
switch (item->value_type) {
case JSON_VALUE_ARRAY:
json_free_item((JsonItem) current->value.as_array);
break;
case JSON_VALUE_OBJECT:
json_free_item((JsonItem) current->value.as_object);
break;
default:
break;
}
item = item->next;
free(current);
}
}
void json_free_args(JsonArray item)
{
json_free_item((JsonItem) item);
}
JsonItem json_array_get_next(JsonItem item)
{
return item->next;
}
JsonValueType json_get_value_type(JsonItem item)
{
return item->value_type;
}
int json_get_int_value(JsonItem item)
{
return item->value.as_int;
}
INT64 json_get_int64_value(JsonItem item)
{
return item->value.as_int64;
}
BOOL json_get_bool_value(JsonItem item)
{
return item->value.as_bool;
}
double json_get_double_value(JsonItem item)
{
return item->value.as_double;
}
static wchar_t decode_unicode_char(const wchar_t *text)
{
wchar_t val = 0;
int i;
const BYTE *buf = (const BYTE *) text;
for(i = 1; i <= 4; i++) {
BYTE c = buf[i];
val <<= 4;
if(isdigit(c))
val += c - '0';
else if(c >= 'a' && c <= 'f')
val += c - 'a' + 10;
else if(c >= 'A' && c <= 'F')
val += c - 'A' + 10;
else
return 0;
}
return val;
}
wchar_t *json_get_string_value(JsonItem item)
{
size_t src_index = 0;
size_t val_index = 0;
const wchar_t *text = item->value.as_string;
wchar_t *val = (wchar_t*) malloc(sizeof(wchar_t) * (item->value_as_string_len + 1));
while (src_index < item->value_as_string_len) {
if (text[src_index] == '\\') {
src_index++;
if (src_index == item->value_as_string_len)
break;
switch(text[src_index]) {
case 'u':
if ((item->value_as_string_len - src_index) > 3) {
wchar_t unicode_val = decode_unicode_char(text + src_index);
if (val) {
val[val_index] = unicode_val;
src_index += 3;
} else
val[val_index] = text[src_index];
} else
val[val_index] = text[src_index];
break;
case '"':
case '\\':
case '/':
val[val_index] = text[src_index];
break;
case 'b':
val[val_index] = '\b';
break;
case 'f':
val[val_index] = '\f';
break;
case 'n':
val[val_index] = '\n';
break;
case 'r':
val[val_index] = '\r';
break;
case 't':
val[val_index] = '\t';
break;
default:
val[val_index] = text[src_index];
}
val_index++;
src_index++;
} else
val[val_index++] = text[src_index++];
}
val[val_index] = 0;
return val;
}
JsonObject json_get_object_value(JsonItem item)
{
return item->value.as_object;
}
JsonArray json_get_array_value(JsonItem item)
{
return item->value.as_array;
}
static int json_container_item_count(JsonItem item)
{
int count = 0;
while (item != NULL) {
item = item->next;
count++;
}
return count;
}
static JsonItem json_container_item_at(JsonItem item, int position)
{
while (item != NULL && position) {
item = item->next;
position--;
}
return item;
}
int json_array_item_count(JsonArray array)
{
return json_container_item_count((JsonItem) array);
}
JsonItem json_array_item_at(JsonArray array, int position)
{
return json_container_item_at((JsonItem) array, position);
}
static BOOL is_type_compatible(JsonValueType current, JsonValueType expected)
{
if (expected & JSON_VALUE_INT64)
return ((expected & current) || (JSON_VALUE_INT & current));
return (expected & current);
}
static BOOL internal_json_array_validate_contents(JsonArray array, JsonValueType type, va_list args)
{
while(type != JSON_VALUE_INVALID) {
if (!array)
return FALSE;
if (!is_type_compatible(array->value_type, type))
return FALSE;
array = array->next;
type = va_arg(args, JsonValueType);
}
return TRUE;
}
BOOL json_array_validate_contents(JsonArray array, JsonValueType type, ...)
{
va_list args;
va_start(args, type);
return internal_json_array_validate_contents(array, type, args);
}
BOOL json_parse_and_validate_args(wchar_t * buf, JsonArray *array, JsonValueType type, ...)
{
va_list args;
if (!json_parse_args(buf, array))
return FALSE;
va_start(args, type);
return internal_json_array_validate_contents(*array, type, args);
}
int json_object_prop_count(JsonObject object)
{
return json_container_item_count((JsonItem) object);
}
JsonObjectItem json_object_prop_at(JsonObject object, int position)
{
return (JsonObjectItem) json_container_item_at((JsonItem) object, position);
}
JsonObjectItem json_object_find_prop(JsonObject object, const wchar_t *id, JsonValueType type)
{
while (object != NULL) {
if (!wcsncmp(id, (wchar_t *) object->tag, wcslen(id)) &&
wcslen(id) == object->tag_len && is_type_compatible(object->value_type, type))
return (JsonObjectItem) object;
object = (struct _JsonObjectItem *) object->next;
}
return NULL;
}
wchar_t *json_object_get_prop_id(JsonObject object)
{
wchar_t *id = (wchar_t*) malloc(sizeof(wchar_t) * (object->tag_len + 1));
wcsncpy_s(id, object->tag_len + 1, object->tag, object->tag_len);
return id;
}
JsonObjectItem json_object_get_next(JsonObjectItem item)
{
return (JsonObjectItem) item->next;
}