blob: e18ea8d9e69a136e307c87a2f870af1dd44ca040 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/slab.h>
#include "cjson.h"
static const char *ep;
const char *cjson_geterrorptr(void)
{
return ep;
}
unsigned long mypow(unsigned long num, unsigned long n)
{
unsigned long value = 1;
int i = 1;
if (n == 0) {
value = 1;
} else {
while (i++ <= n)
value *= num;
}
return value;
}
int mytolower(int c)
{
if (c >= 'A' && c <= 'Z')
return c + 32;
else
return c;
}
static int cjson_strcasecmp(const char *s1, const char *s2)
{
if (!s1)
return (s1 == s2) ? 0 : 1;
if (!s2)
return 1;
for (; mytolower(*s1) == mytolower(*s2); ++s1, ++s2)
if (*s1 == 0)
return 0;
return mytolower(*(const unsigned char *)s1) - mytolower(*(const unsigned char *)s2);
}
void *cjson_malloc(size_t sz)
{
void *alloc = NULL;
alloc = kzalloc(sz, GFP_KERNEL);
return alloc;
}
void cjson_free(void *ptr)
{
kfree(ptr);
ptr = NULL;
}
/* Internal constructor. */
static struct cjson *cjson_new_item(void)
{
struct cjson *node = (struct cjson *)cjson_malloc(sizeof(struct cjson));
if (node)
memset(node, 0, sizeof(struct cjson));
return node;
}
/* Delete a cjson structure. */
void cjson_delete(struct cjson *c)
{
struct cjson *next;
if (c && c->pure_json)
cjson_free(c->pure_json);
while (c) {
next = c->next;
if (!(c->type & cjson_isreference) && c->child)
cjson_delete(c->child);
if (!(c->type & cjson_isreference) && c->valuestring)
cjson_free(c->valuestring);
if (!(c->type & cjson_stringisconst) && c->string)
cjson_free(c->string);
cjson_free(c);
c = next;
}
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(struct cjson *item, const char *num)
{
unsigned long n = 0, sign = 1, scale = 0;
int subscale = 0, signsubscale = 1;
if (*num == '-') {
sign = -1;
num++;
}
if (*num == '0')
num++;
if (*num >= '1' && *num <= '9') {
do {
n = (n * 10) + (*num++ - '0');
} while (*num >= '0' && *num <= '9');
}
if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
num++;
do {
n = (n * 10) + (*num++ - '0');
scale--;
} while (*num >= '0' && *num <= '9');
}
if (*num == 'e' || *num == 'E') {
num++;
if (*num == '+') {
num++;
} else if (*num == '-') {
signsubscale = -1;
num++;
}
while (*num >= '0' && *num <= '9')
subscale = (subscale * 10) + (*num++ - '0');
}
n = sign * n * mypow(10, (scale + subscale * signsubscale));
//item->valuedouble = (double)n;
item->valueint = (int)n;
item->type = cjson_number;
return num;
}
static unsigned int parse_hex4(const char *str)
{
unsigned int h = 0;
if (*str >= '0' && *str <= '9')
h += (*str) - '0';
else if (*str >= 'A' && *str <= 'F')
h += 10 + (*str) - 'A';
else if (*str >= 'a' && *str <= 'f')
h += 10 + (*str) - 'a';
else
return 0;
h = h << 4;
str++;
if (*str >= '0' && *str <= '9')
h += (*str) - '0';
else if (*str >= 'A' && *str <= 'F')
h += 10 + (*str) - 'A';
else if (*str >= 'a' && *str <= 'f')
h += 10 + (*str) - 'a';
else
return 0;
h = h << 4;
str++;
if (*str >= '0' && *str <= '9')
h += (*str) - '0';
else if (*str >= 'A' && *str <= 'F')
h += 10 + (*str) - 'A';
else if (*str >= 'a' && *str <= 'f')
h += 10 + (*str) - 'a';
else
return 0;
h = h << 4;
str++;
if (*str >= '0' && *str <= '9')
h += (*str) - '0';
else if (*str >= 'A' && *str <= 'F')
h += 10 + (*str) - 'A';
else if (*str >= 'a' && *str <= 'f')
h += 10 + (*str) - 'a';
else
return 0;
return h;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(struct cjson *item, const char *str)
{
const char *ptr = str + 1;
char *ptr2;
char *out;
int len = 0;
unsigned int uc, uc2;
if (*str != '\"') {
ep = str;
return 0;
}
while (*ptr != '\"' && *ptr && ++len)
if (*ptr++ == '\\')
ptr++;
out = (char *)cjson_malloc(len + 1);
if (!out)
return 0;
ptr = str + 1;
ptr2 = out;
while (*ptr != '\"' && *ptr) {
if (*ptr != '\\') {
*ptr2++ = *ptr++;
} else {
ptr++;
switch (*ptr) {
case 'b':
*ptr2++ = '\b';
break;
case 'f':
*ptr2++ = '\f';
break;
case 'n':
*ptr2++ = '\n';
break;
case 'r':
*ptr2++ = '\r';
break;
case 't':
*ptr2++ = '\t';
break;
case 'u':
uc = parse_hex4(ptr + 1);
ptr += 4;
if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)
break;
if (uc >= 0xD800 && uc <= 0xDBFF) {
if (ptr[1] != '\\' || ptr[2] != 'u')
break;
uc2 = parse_hex4(ptr + 3);
ptr += 6;
if (uc2 < 0xDC00 || uc2 > 0xDFFF)
break;
uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
}
len = 4;
if (uc < 0x80)
len = 1;
else if (uc < 0x800)
len = 2;
else if (uc < 0x10000)
len = 3;
ptr2 += len;
switch (len) {
case 4:
case 3:
case 2:
*--ptr2 = ((uc | 0x80) & 0xBF);
uc = uc >> 6;
break;
case 1:
*--ptr2 = (uc | firstByteMark[len]);
break;
default:
break;
}
ptr2 += len;
break;
default:
*ptr2++ = *ptr;
break;
}
ptr++;
}
}
*ptr2 = 0;
if (*ptr == '\"')
ptr++;
item->valuestring = out;
item->type = cjson_string;
return ptr;
}
static const char *parse_value(struct cjson *item, const char *value);
static const char *parse_array(struct cjson *item, const char *value);
static const char *parse_object(struct cjson *item, const char *value);
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in)
{
while (in && *in && (unsigned char)*in <= 32)
in++;
return in;
}
static const char *get_pure_json_str(const char *in)
{
int i = 0;
int j = 0;
char cur_char = 0;
char pre_char = 0;
char is_in_str = 0;
const char *str;
int len = 0;
if (!in)
return in;
str = in;
while (*(str + i) != 0) {
len++;
i++;
}
char *json_pure = (char *)cjson_malloc(len);
if (!json_pure)
return 0;
for (i = 0, j = 0; i < len; i++) {
cur_char = *(in + i);
if (cur_char == 0) {
pr_debug("skip\n");
} else if (cur_char == ' ' || cur_char == '\n' || cur_char == '\r') {
if (is_in_str) {
//don't remove space in strings
*(json_pure + j++) = cur_char;
}
} else {
*(json_pure + j++) = cur_char;
if (cur_char == '"')
if (pre_char != '\\')
is_in_str = !is_in_str;
}
pre_char = cur_char;
}
return json_pure;
}
/* Parse an object - create a new root, and populate. */
struct cjson *cjson_parsewithopts(const char *value, const char **return_parse_end,
int require_null_terminated)
{
const char *end = 0;
char *json_pure = NULL;
struct cjson *c = cjson_new_item();
ep = 0;
if (!c)
return 0;
json_pure = (char *)get_pure_json_str(value);
c->pure_json = json_pure;
end = parse_value(c, skip(json_pure));
if (!end) {
cjson_delete(c);
return 0;
}
/* parse failure. ep is set.*/
/*skip and then check for a null terminator*/
if (require_null_terminated) {
end = skip(end);
if (*end) {
cjson_delete(c);
ep = end;
return 0;
}
}
if (return_parse_end)
*return_parse_end = end;
return c;
}
/* Default options for cjson_parse */
struct cjson *cjson_parse(const char *value)
{
return cjson_parsewithopts(value, 0, 0);
}
/* Render a cjson item/entity/structure to text. */
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(struct cjson *item, const char *value)
{
if (!value)
return 0;
if (!strncmp(value, "null", 4)) {
item->type = cjson_null;
return value + 4;
}
if (!strncmp(value, "false", 5)) {
item->type = cjson_false;
return value + 5;
}
if (!strncmp(value, "true", 4)) {
item->type = cjson_true;
item->valueint = 1;
return value + 4;
}
if (*value == '\"')
return parse_string(item, value);
if (*value == '-' || (*value >= '0' && *value <= '9'))
return parse_number(item, value);
if (*value == '[')
return parse_array(item, value);
if (*value == '{')
return parse_object(item, value);
ep = value;
return 0;
}
/* Build an array from input text. */
static const char *parse_array(struct cjson *item, const char *value)
{
struct cjson *child;
if (*value != '[') {
ep = value;
return 0;
}
item->type = cjson_array;
value = skip(value + 1);
if (*value == ']')
return value + 1;
child = cjson_new_item();
item->child = child;
if (!item->child)
return 0;
value = skip(parse_value(child, skip(value)));
// skip any spacing, get the value.
if (!value)
return 0;
while (*value == ',') {
struct cjson *new_item;
new_item = cjson_new_item();
if (!new_item)
return 0;
child->next = new_item;
new_item->prev = child;
child = new_item;
value = skip(parse_value(child, skip(value + 1)));
if (!value)
return 0;
//JSON array
}
if (*value == ']')
return value + 1;
ep = value;
return 0;
}
/* Build an object from the text. */
static const char *parse_object(struct cjson *item, const char *value)
{
struct cjson *child;
if (*value != '{') {
ep = value;
return 0;
}
item->type = cjson_object;
value = skip(value + 1);
if (*value == '}')
return value + 1;
child = cjson_new_item();
item->child = child;
if (!item->child)
return 0;
value = skip(parse_string(child, skip(value)));
if (!value)
return 0;
child->string = child->valuestring;
child->valuestring = 0;
if (*value != ':') {
ep = value;
return 0;
}
value = skip(parse_value(child, skip(value + 1)));
if (!value)
return 0;
while (*value == ',') {
struct cjson *new_item;
new_item = cjson_new_item();
if (!new_item)
return 0;
child->next = new_item;
new_item->prev = child;
child = new_item;
value = skip(parse_string(child, skip(value + 1)));
if (!value)
return 0;
child->string = child->valuestring;
child->valuestring = 0;
if (*value != ':') {
ep = value;
return 0;
}
value = skip(parse_value(child, skip(value + 1)));
if (!value)
return 0;
}
if (*value == '}')
return value + 1;
ep = value;
return 0;
}
/* Get Array size/item / object item. */
int cjson_getarraysize(struct cjson *array)
{
struct cjson *c = array->child;
int i = 0;
while (c)
i++, c = c->next;
return i;
}
struct cjson *cjson_getarrayitem(struct cjson *array, int item)
{
struct cjson *c = array->child;
while (c && item > 0)
item--, c = c->next;
return c;
}
struct cjson *cjson_getobjectitem(struct cjson *object, const char *string)
{
struct cjson *c = object->child;
while (c && cjson_strcasecmp(c->string, string))
c = c->next;
return c;
}