|
Packit |
fd8b60 |
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
Packit |
fd8b60 |
/* util/support/json.c - JSON parser and unparser */
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* Copyright (c) 2010 Kungliga Tekniska Högskolan
|
|
Packit |
fd8b60 |
* (Royal Institute of Technology, Stockholm, Sweden).
|
|
Packit |
fd8b60 |
* All rights reserved.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* Redistribution and use in source and binary forms, with or without
|
|
Packit |
fd8b60 |
* modification, are permitted provided that the following conditions
|
|
Packit |
fd8b60 |
* are met:
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* 1. Redistributions of source code must retain the above copyright
|
|
Packit |
fd8b60 |
* notice, this list of conditions and the following disclaimer.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* 2. Redistributions in binary form must reproduce the above copyright
|
|
Packit |
fd8b60 |
* notice, this list of conditions and the following disclaimer in the
|
|
Packit |
fd8b60 |
* documentation and/or other materials provided with the distribution.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* 3. Neither the name of the Institute nor the names of its contributors
|
|
Packit |
fd8b60 |
* may be used to endorse or promote products derived from this software
|
|
Packit |
fd8b60 |
* without specific prior written permission.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
Packit |
fd8b60 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
Packit |
fd8b60 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
Packit |
fd8b60 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
Packit |
fd8b60 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
Packit |
fd8b60 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
Packit |
fd8b60 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
Packit |
fd8b60 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
Packit |
fd8b60 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
Packit |
fd8b60 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
Packit |
fd8b60 |
* SUCH DAMAGE.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* Copyright (C) 2012 by the Massachusetts Institute of Technology.
|
|
Packit |
fd8b60 |
* All rights reserved.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* Redistribution and use in source and binary forms, with or without
|
|
Packit |
fd8b60 |
* modification, are permitted provided that the following conditions
|
|
Packit |
fd8b60 |
* are met:
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* * Redistributions of source code must retain the above copyright
|
|
Packit |
fd8b60 |
* notice, this list of conditions and the following disclaimer.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* * Redistributions in binary form must reproduce the above copyright
|
|
Packit |
fd8b60 |
* notice, this list of conditions and the following disclaimer in
|
|
Packit |
fd8b60 |
* the documentation and/or other materials provided with the
|
|
Packit |
fd8b60 |
* distribution.
|
|
Packit |
fd8b60 |
*
|
|
Packit |
fd8b60 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
Packit |
fd8b60 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
Packit |
fd8b60 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
Packit |
fd8b60 |
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
Packit |
fd8b60 |
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
Packit |
fd8b60 |
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
Packit |
fd8b60 |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
Packit |
fd8b60 |
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
Packit |
fd8b60 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
Packit |
fd8b60 |
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
Packit |
fd8b60 |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
Packit |
fd8b60 |
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*
|
|
Packit |
fd8b60 |
* This file implements a minimal dynamic type system for JSON values and a
|
|
Packit |
fd8b60 |
* JSON encoder and decoder. It is loosely based on the heimbase code from
|
|
Packit |
fd8b60 |
* Heimdal.
|
|
Packit |
fd8b60 |
*/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
#include <k5-platform.h>
|
|
Packit |
fd8b60 |
#include <k5-base64.h>
|
|
Packit |
fd8b60 |
#include <k5-json.h>
|
|
Packit |
fd8b60 |
#include <k5-buf.h>
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
#define MAX_DECODE_DEPTH 64
|
|
Packit |
fd8b60 |
#define MIN_ALLOC_SLOT 16
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
typedef void (*type_dealloc_fn)(void *val);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
typedef struct json_type_st {
|
|
Packit |
fd8b60 |
k5_json_tid tid;
|
|
Packit |
fd8b60 |
const char *name;
|
|
Packit |
fd8b60 |
type_dealloc_fn dealloc;
|
|
Packit |
fd8b60 |
} *json_type;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
struct value_base {
|
|
Packit |
fd8b60 |
json_type isa;
|
|
Packit |
fd8b60 |
unsigned int ref_cnt;
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
#define PTR2BASE(ptr) (((struct value_base *)ptr) - 1)
|
|
Packit |
fd8b60 |
#define BASE2PTR(ptr) ((void *)(((struct value_base *)ptr) + 1))
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
k5_json_value
|
|
Packit |
fd8b60 |
k5_json_retain(k5_json_value val)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct value_base *p;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (val == NULL)
|
|
Packit |
fd8b60 |
return val;
|
|
Packit |
fd8b60 |
p = PTR2BASE(val);
|
|
Packit |
fd8b60 |
assert(p->ref_cnt != 0);
|
|
Packit |
fd8b60 |
p->ref_cnt++;
|
|
Packit |
fd8b60 |
return val;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
void
|
|
Packit |
fd8b60 |
k5_json_release(k5_json_value val)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct value_base *p;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (val == NULL)
|
|
Packit |
fd8b60 |
return;
|
|
Packit |
fd8b60 |
p = PTR2BASE(val);
|
|
Packit |
fd8b60 |
assert(p->ref_cnt != 0);
|
|
Packit |
fd8b60 |
p->ref_cnt--;
|
|
Packit |
fd8b60 |
if (p->ref_cnt == 0) {
|
|
Packit |
fd8b60 |
if (p->isa->dealloc != NULL)
|
|
Packit |
fd8b60 |
p->isa->dealloc(val);
|
|
Packit |
fd8b60 |
free(p);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Get the type description of a k5_json_value. */
|
|
Packit |
fd8b60 |
static json_type
|
|
Packit |
fd8b60 |
get_isa(k5_json_value val)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct value_base *p = PTR2BASE(val);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return p->isa;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
k5_json_tid
|
|
Packit |
fd8b60 |
k5_json_get_tid(k5_json_value val)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
json_type isa = get_isa(val);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return isa->tid;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static k5_json_value
|
|
Packit |
fd8b60 |
alloc_value(json_type type, size_t size)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct value_base *p = calloc(1, size + sizeof(*p));
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (p == NULL)
|
|
Packit |
fd8b60 |
return NULL;
|
|
Packit |
fd8b60 |
p->isa = type;
|
|
Packit |
fd8b60 |
p->ref_cnt = 1;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return BASE2PTR(p);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*** Null type ***/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static struct json_type_st null_type = { K5_JSON_TID_NULL, "null", NULL };
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_null_create(k5_json_null *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
*val_out = alloc_value(&null_type, 0);
|
|
Packit |
fd8b60 |
return (*val_out == NULL) ? ENOMEM : 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_null_create_val(k5_json_value *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
*val_out = alloc_value(&null_type, 0);
|
|
Packit |
fd8b60 |
return (*val_out == NULL) ? ENOMEM : 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*** Boolean type ***/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static struct json_type_st bool_type = { K5_JSON_TID_BOOL, "bool", NULL };
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_bool_create(int truth, k5_json_bool *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_bool b;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*val_out = NULL;
|
|
Packit |
fd8b60 |
b = alloc_value(&bool_type, 1);
|
|
Packit |
fd8b60 |
if (b == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
*(unsigned char *)b = !!truth;
|
|
Packit |
fd8b60 |
*val_out = b;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_bool_value(k5_json_bool bval)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
return *(unsigned char *)bval;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*** Array type ***/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
struct k5_json_array_st {
|
|
Packit |
fd8b60 |
k5_json_value *values;
|
|
Packit |
fd8b60 |
size_t len;
|
|
Packit |
fd8b60 |
size_t allocated;
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
array_dealloc(void *ptr)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_array array = ptr;
|
|
Packit |
fd8b60 |
size_t i;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
for (i = 0; i < array->len; i++)
|
|
Packit |
fd8b60 |
k5_json_release(array->values[i]);
|
|
Packit |
fd8b60 |
free(array->values);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static struct json_type_st array_type = {
|
|
Packit |
fd8b60 |
K5_JSON_TID_ARRAY, "array", array_dealloc
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_array_create(k5_json_array *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
*val_out = alloc_value(&array_type, sizeof(struct k5_json_array_st));
|
|
Packit |
fd8b60 |
return (*val_out == NULL) ? ENOMEM : 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_array_add(k5_json_array array, k5_json_value val)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_value *ptr;
|
|
Packit |
fd8b60 |
size_t new_alloc;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (array->len >= array->allocated) {
|
|
Packit |
fd8b60 |
/* Increase the number of slots by 50% (MIN_ALLOC_SLOT minimum). */
|
|
Packit |
fd8b60 |
new_alloc = array->len + 1 + (array->len >> 1);
|
|
Packit |
fd8b60 |
if (new_alloc < MIN_ALLOC_SLOT)
|
|
Packit |
fd8b60 |
new_alloc = MIN_ALLOC_SLOT;
|
|
Packit |
fd8b60 |
ptr = realloc(array->values, new_alloc * sizeof(*array->values));
|
|
Packit |
fd8b60 |
if (ptr == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
array->values = ptr;
|
|
Packit |
fd8b60 |
array->allocated = new_alloc;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
array->values[array->len++] = k5_json_retain(val);
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
size_t
|
|
Packit |
fd8b60 |
k5_json_array_length(k5_json_array array)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
return array->len;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
k5_json_value
|
|
Packit |
fd8b60 |
k5_json_array_get(k5_json_array array, size_t idx)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
if (idx >= array->len)
|
|
Packit |
fd8b60 |
abort();
|
|
Packit |
fd8b60 |
return array->values[idx];
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
void
|
|
Packit |
fd8b60 |
k5_json_array_set(k5_json_array array, size_t idx, k5_json_value val)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
if (idx >= array->len)
|
|
Packit |
fd8b60 |
abort();
|
|
Packit |
fd8b60 |
k5_json_release(array->values[idx]);
|
|
Packit |
fd8b60 |
array->values[idx] = k5_json_retain(val);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_array_fmt(k5_json_array *array_out, const char *template, ...)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
const char *p;
|
|
Packit |
fd8b60 |
va_list ap;
|
|
Packit |
fd8b60 |
const char *cstring;
|
|
Packit |
fd8b60 |
unsigned char *data;
|
|
Packit |
fd8b60 |
size_t len;
|
|
Packit |
fd8b60 |
long long nval;
|
|
Packit |
fd8b60 |
k5_json_array array;
|
|
Packit |
fd8b60 |
k5_json_value val;
|
|
Packit |
fd8b60 |
k5_json_number num;
|
|
Packit |
fd8b60 |
k5_json_string str;
|
|
Packit |
fd8b60 |
k5_json_bool b;
|
|
Packit |
fd8b60 |
k5_json_null null;
|
|
Packit |
fd8b60 |
int truth, ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*array_out = NULL;
|
|
Packit |
fd8b60 |
if (k5_json_array_create(&array))
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
va_start(ap, template);
|
|
Packit |
fd8b60 |
for (p = template; *p != '\0'; p++) {
|
|
Packit |
fd8b60 |
switch (*p) {
|
|
Packit |
fd8b60 |
case 'v':
|
|
Packit |
fd8b60 |
val = k5_json_retain(va_arg(ap, k5_json_value));
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
case 'n':
|
|
Packit |
fd8b60 |
if (k5_json_null_create(&null))
|
|
Packit |
fd8b60 |
goto err;
|
|
Packit |
fd8b60 |
val = null;
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
case 'b':
|
|
Packit |
fd8b60 |
truth = va_arg(ap, int);
|
|
Packit |
fd8b60 |
if (k5_json_bool_create(truth, &b))
|
|
Packit |
fd8b60 |
goto err;
|
|
Packit |
fd8b60 |
val = b;
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
case 'i':
|
|
Packit |
fd8b60 |
nval = va_arg(ap, int);
|
|
Packit |
fd8b60 |
if (k5_json_number_create(nval, &num))
|
|
Packit |
fd8b60 |
goto err;
|
|
Packit |
fd8b60 |
val = num;
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
case 'L':
|
|
Packit |
fd8b60 |
nval = va_arg(ap, long long);
|
|
Packit |
fd8b60 |
if (k5_json_number_create(nval, &num))
|
|
Packit |
fd8b60 |
goto err;
|
|
Packit |
fd8b60 |
val = num;
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
case 's':
|
|
Packit |
fd8b60 |
cstring = va_arg(ap, const char *);
|
|
Packit |
fd8b60 |
if (cstring == NULL) {
|
|
Packit |
fd8b60 |
if (k5_json_null_create(&null))
|
|
Packit |
fd8b60 |
goto err;
|
|
Packit |
fd8b60 |
val = null;
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
if (k5_json_string_create(cstring, &str))
|
|
Packit |
fd8b60 |
goto err;
|
|
Packit |
fd8b60 |
val = str;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
case 'B':
|
|
Packit |
fd8b60 |
data = va_arg(ap, unsigned char *);
|
|
Packit |
fd8b60 |
len = va_arg(ap, size_t);
|
|
Packit |
fd8b60 |
if (k5_json_string_create_base64(data, len, &str))
|
|
Packit |
fd8b60 |
goto err;
|
|
Packit |
fd8b60 |
val = str;
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
default:
|
|
Packit |
fd8b60 |
goto err;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
ret = k5_json_array_add(array, val);
|
|
Packit |
fd8b60 |
k5_json_release(val);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
goto err;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
va_end(ap);
|
|
Packit |
fd8b60 |
*array_out = array;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
err:
|
|
Packit |
fd8b60 |
va_end(ap);
|
|
Packit |
fd8b60 |
k5_json_release(array);
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*** Object type (string:value mapping) ***/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
struct entry {
|
|
Packit |
fd8b60 |
char *key;
|
|
Packit |
fd8b60 |
k5_json_value value;
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
struct k5_json_object_st {
|
|
Packit |
fd8b60 |
struct entry *entries;
|
|
Packit |
fd8b60 |
size_t len;
|
|
Packit |
fd8b60 |
size_t allocated;
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
object_dealloc(void *ptr)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_object obj = ptr;
|
|
Packit |
fd8b60 |
size_t i;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
for (i = 0; i < obj->len; i++) {
|
|
Packit |
fd8b60 |
free(obj->entries[i].key);
|
|
Packit |
fd8b60 |
k5_json_release(obj->entries[i].value);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
free(obj->entries);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static struct json_type_st object_type = {
|
|
Packit |
fd8b60 |
K5_JSON_TID_OBJECT, "object", object_dealloc
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_object_create(k5_json_object *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
*val_out = alloc_value(&object_type, sizeof(struct k5_json_object_st));
|
|
Packit |
fd8b60 |
return (*val_out == NULL) ? ENOMEM : 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
size_t
|
|
Packit |
fd8b60 |
k5_json_object_count(k5_json_object obj)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
return obj->len;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Return the entry for key within obj, or NULL if none exists. */
|
|
Packit |
fd8b60 |
static struct entry *
|
|
Packit |
fd8b60 |
object_search(k5_json_object obj, const char *key)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
size_t i;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
for (i = 0; i < obj->len; i++) {
|
|
Packit |
fd8b60 |
if (strcmp(key, obj->entries[i].key) == 0)
|
|
Packit |
fd8b60 |
return &obj->entries[i];
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
return NULL;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
k5_json_value
|
|
Packit |
fd8b60 |
k5_json_object_get(k5_json_object obj, const char *key)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct entry *ent;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
ent = object_search(obj, key);
|
|
Packit |
fd8b60 |
if (ent == NULL)
|
|
Packit |
fd8b60 |
return NULL;
|
|
Packit |
fd8b60 |
return ent->value;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_object_set(k5_json_object obj, const char *key, k5_json_value val)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct entry *ent, *ptr;
|
|
Packit |
fd8b60 |
size_t new_alloc, i;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
ent = object_search(obj, key);
|
|
Packit |
fd8b60 |
if (ent != NULL) {
|
|
Packit |
fd8b60 |
k5_json_release(ent->value);
|
|
Packit |
fd8b60 |
if (val == NULL) {
|
|
Packit |
fd8b60 |
/* Remove this key. */
|
|
Packit |
fd8b60 |
free(ent->key);
|
|
Packit |
fd8b60 |
for (i = ent - obj->entries; i < obj->len - 1; i++)
|
|
Packit |
fd8b60 |
obj->entries[i] = obj->entries[i + 1];
|
|
Packit |
fd8b60 |
obj->len--;
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
/* Overwrite this key's value with the new one. */
|
|
Packit |
fd8b60 |
ent->value = k5_json_retain(val);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* If didn't find a key the caller asked to remove, do nothing. */
|
|
Packit |
fd8b60 |
if (val == NULL)
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (obj->len >= obj->allocated) {
|
|
Packit |
fd8b60 |
/* Increase the number of slots by 50% (MIN_ALLOC_SLOT minimum). */
|
|
Packit |
fd8b60 |
new_alloc = obj->len + 1 + (obj->len >> 1);
|
|
Packit |
fd8b60 |
if (new_alloc < MIN_ALLOC_SLOT)
|
|
Packit |
fd8b60 |
new_alloc = MIN_ALLOC_SLOT;
|
|
Packit |
fd8b60 |
ptr = realloc(obj->entries, new_alloc * sizeof(*obj->entries));
|
|
Packit |
fd8b60 |
if (ptr == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
obj->entries = ptr;
|
|
Packit |
fd8b60 |
obj->allocated = new_alloc;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
obj->entries[obj->len].key = strdup(key);
|
|
Packit |
fd8b60 |
if (obj->entries[obj->len].key == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
obj->entries[obj->len].value = k5_json_retain(val);
|
|
Packit |
fd8b60 |
obj->len++;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
void
|
|
Packit |
fd8b60 |
k5_json_object_iterate(k5_json_object obj, k5_json_object_iterator_fn func,
|
|
Packit |
fd8b60 |
void *arg)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
size_t i;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
for (i = 0; i < obj->len; i++)
|
|
Packit |
fd8b60 |
func(arg, obj->entries[i].key, obj->entries[i].value);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*** String type ***/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static struct json_type_st string_type = {
|
|
Packit |
fd8b60 |
K5_JSON_TID_STRING, "string", NULL
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_string_create(const char *cstring, k5_json_string *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
return k5_json_string_create_len(cstring, strlen(cstring), val_out);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_string_create_len(const void *data, size_t len,
|
|
Packit |
fd8b60 |
k5_json_string *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
char *s;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*val_out = NULL;
|
|
Packit |
fd8b60 |
s = alloc_value(&string_type, len + 1);
|
|
Packit |
fd8b60 |
if (s == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
if (len > 0)
|
|
Packit |
fd8b60 |
memcpy(s, data, len);
|
|
Packit |
fd8b60 |
s[len] = '\0';
|
|
Packit |
fd8b60 |
*val_out = (k5_json_string)s;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_string_create_base64(const void *data, size_t len,
|
|
Packit |
fd8b60 |
k5_json_string *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
char *base64;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*val_out = NULL;
|
|
Packit |
fd8b60 |
base64 = k5_base64_encode(data, len);
|
|
Packit |
fd8b60 |
if (base64 == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
ret = k5_json_string_create(base64, val_out);
|
|
Packit |
fd8b60 |
free(base64);
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
const char *
|
|
Packit |
fd8b60 |
k5_json_string_utf8(k5_json_string string)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
return (const char *)string;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_string_unbase64(k5_json_string string, unsigned char **data_out,
|
|
Packit |
fd8b60 |
size_t *len_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
unsigned char *data;
|
|
Packit |
fd8b60 |
size_t len;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*data_out = NULL;
|
|
Packit |
fd8b60 |
*len_out = 0;
|
|
Packit |
fd8b60 |
data = k5_base64_decode((const char *)string, &len;;
|
|
Packit |
fd8b60 |
if (data == NULL)
|
|
Packit |
fd8b60 |
return (len == 0) ? ENOMEM : EINVAL;
|
|
Packit |
fd8b60 |
*data_out = data;
|
|
Packit |
fd8b60 |
*len_out = len;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*** Number type ***/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static struct json_type_st number_type = {
|
|
Packit |
fd8b60 |
K5_JSON_TID_NUMBER, "number", NULL
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_number_create(long long number, k5_json_number *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_number n;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*val_out = NULL;
|
|
Packit |
fd8b60 |
n = alloc_value(&number_type, sizeof(long long));
|
|
Packit |
fd8b60 |
if (n == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
*((long long *)n) = number;
|
|
Packit |
fd8b60 |
*val_out = n;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
long long
|
|
Packit |
fd8b60 |
k5_json_number_value(k5_json_number number)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
return *(long long *)number;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*** JSON encoding ***/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static const char quotemap_json[] = "\"\\/bfnrt";
|
|
Packit |
fd8b60 |
static const char quotemap_c[] = "\"\\/\b\f\n\r\t";
|
|
Packit |
fd8b60 |
static const char needs_quote[] = "\\\"\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
|
|
Packit |
fd8b60 |
"\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37";
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static int encode_value(struct k5buf *buf, k5_json_value val);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
encode_string(struct k5buf *buf, const char *str)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
size_t n;
|
|
Packit |
fd8b60 |
const char *p;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
k5_buf_add(buf, "\"");
|
|
Packit |
fd8b60 |
while (*str != '\0') {
|
|
Packit |
fd8b60 |
n = strcspn(str, needs_quote);
|
|
Packit |
fd8b60 |
k5_buf_add_len(buf, str, n);
|
|
Packit |
fd8b60 |
str += n;
|
|
Packit |
fd8b60 |
if (*str == '\0')
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
k5_buf_add(buf, "\\");
|
|
Packit |
fd8b60 |
p = strchr(quotemap_c, *str);
|
|
Packit |
fd8b60 |
if (p != NULL)
|
|
Packit |
fd8b60 |
k5_buf_add_len(buf, quotemap_json + (p - quotemap_c), 1);
|
|
Packit |
fd8b60 |
else
|
|
Packit |
fd8b60 |
k5_buf_add_fmt(buf, "u00%02X", (unsigned int)*str);
|
|
Packit |
fd8b60 |
str++;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
k5_buf_add(buf, "\"");
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
struct obj_ctx {
|
|
Packit |
fd8b60 |
struct k5buf *buf;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
int first;
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static void
|
|
Packit |
fd8b60 |
encode_obj_entry(void *ctx, const char *key, k5_json_value value)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct obj_ctx *j = ctx;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (j->ret)
|
|
Packit |
fd8b60 |
return;
|
|
Packit |
fd8b60 |
if (j->first)
|
|
Packit |
fd8b60 |
j->first = 0;
|
|
Packit |
fd8b60 |
else
|
|
Packit |
fd8b60 |
k5_buf_add(j->buf, ",");
|
|
Packit |
fd8b60 |
encode_string(j->buf, key);
|
|
Packit |
fd8b60 |
k5_buf_add(j->buf, ":");
|
|
Packit |
fd8b60 |
j->ret = encode_value(j->buf, value);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static int
|
|
Packit |
fd8b60 |
encode_value(struct k5buf *buf, k5_json_value val)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_tid type;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
size_t i, len;
|
|
Packit |
fd8b60 |
struct obj_ctx ctx;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (val == NULL)
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
type = k5_json_get_tid(val);
|
|
Packit |
fd8b60 |
switch (type) {
|
|
Packit |
fd8b60 |
case K5_JSON_TID_ARRAY:
|
|
Packit |
fd8b60 |
k5_buf_add(buf, "[");
|
|
Packit |
fd8b60 |
len = k5_json_array_length(val);
|
|
Packit |
fd8b60 |
for (i = 0; i < len; i++) {
|
|
Packit |
fd8b60 |
if (i != 0)
|
|
Packit |
fd8b60 |
k5_buf_add(buf, ",");
|
|
Packit |
fd8b60 |
ret = encode_value(buf, k5_json_array_get(val, i));
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
k5_buf_add(buf, "]");
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
case K5_JSON_TID_OBJECT:
|
|
Packit |
fd8b60 |
k5_buf_add(buf, "{");
|
|
Packit |
fd8b60 |
ctx.buf = buf;
|
|
Packit |
fd8b60 |
ctx.ret = 0;
|
|
Packit |
fd8b60 |
ctx.first = 1;
|
|
Packit |
fd8b60 |
k5_json_object_iterate(val, encode_obj_entry, &ctx;;
|
|
Packit |
fd8b60 |
k5_buf_add(buf, "}");
|
|
Packit |
fd8b60 |
return ctx.ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
case K5_JSON_TID_STRING:
|
|
Packit |
fd8b60 |
encode_string(buf, k5_json_string_utf8(val));
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
case K5_JSON_TID_NUMBER:
|
|
Packit |
fd8b60 |
k5_buf_add_fmt(buf, "%lld", k5_json_number_value(val));
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
case K5_JSON_TID_NULL:
|
|
Packit |
fd8b60 |
k5_buf_add(buf, "null");
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
case K5_JSON_TID_BOOL:
|
|
Packit |
fd8b60 |
k5_buf_add(buf, k5_json_bool_value(val) ? "true" : "false");
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
default:
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_encode(k5_json_value val, char **json_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct k5buf buf;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*json_out = NULL;
|
|
Packit |
fd8b60 |
k5_buf_init_dynamic(&buf;;
|
|
Packit |
fd8b60 |
ret = encode_value(&buf, val);
|
|
Packit |
fd8b60 |
if (ret) {
|
|
Packit |
fd8b60 |
k5_buf_free(&buf;;
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
if (k5_buf_status(&buf) != 0)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
*json_out = buf.data;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/*** JSON decoding ***/
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
struct decode_ctx {
|
|
Packit |
fd8b60 |
const unsigned char *p;
|
|
Packit |
fd8b60 |
size_t depth;
|
|
Packit |
fd8b60 |
};
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
static int parse_value(struct decode_ctx *ctx, k5_json_value *val_out);
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Consume whitespace. Return 0 if there is anything left to parse after the
|
|
Packit |
fd8b60 |
* whitespace, -1 if not. */
|
|
Packit |
fd8b60 |
static int
|
|
Packit |
fd8b60 |
white_spaces(struct decode_ctx *ctx)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
unsigned char c;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
for (; *ctx->p != '\0'; ctx->p++) {
|
|
Packit |
fd8b60 |
c = *ctx->p;
|
|
Packit |
fd8b60 |
if (c != ' ' && c != '\t' && c != '\r' && c != '\n')
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
return -1;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Return true if c is a decimal digit. */
|
|
Packit |
fd8b60 |
static inline int
|
|
Packit |
fd8b60 |
is_digit(unsigned char c)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
return ('0' <= c && c <= '9');
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Return true if c is a hexadecimal digit (per RFC 5234 HEXDIG). */
|
|
Packit |
fd8b60 |
static inline int
|
|
Packit |
fd8b60 |
is_hex_digit(unsigned char c)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
return is_digit(c) || ('A' <= c && c <= 'F');
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Return the numeric value of a hex digit; aborts if c is not a hex digit. */
|
|
Packit |
fd8b60 |
static inline unsigned int
|
|
Packit |
fd8b60 |
hexval(unsigned char c)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
if (is_digit(c))
|
|
Packit |
fd8b60 |
return c - '0';
|
|
Packit |
fd8b60 |
else if ('A' <= c && c <= 'F')
|
|
Packit |
fd8b60 |
return c - 'A' + 10;
|
|
Packit |
fd8b60 |
abort();
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse a JSON number (which must be an integer in the signed 64-bit range; we
|
|
Packit |
fd8b60 |
* do not allow floating-point numbers). */
|
|
Packit |
fd8b60 |
static int
|
|
Packit |
fd8b60 |
parse_number(struct decode_ctx *ctx, k5_json_number *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
const unsigned long long umax = ~0ULL, smax = (1ULL << 63) - 1;
|
|
Packit |
fd8b60 |
unsigned long long number = 0;
|
|
Packit |
fd8b60 |
int neg = 1;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*val_out = NULL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (*ctx->p == '-') {
|
|
Packit |
fd8b60 |
neg = -1;
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (!is_digit(*ctx->p))
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Read the number into an unsigned 64-bit container, ensuring that we
|
|
Packit |
fd8b60 |
* don't overflow it. */
|
|
Packit |
fd8b60 |
while (is_digit(*ctx->p)) {
|
|
Packit |
fd8b60 |
if (number + 1 > umax / 10)
|
|
Packit |
fd8b60 |
return EOVERFLOW;
|
|
Packit |
fd8b60 |
number = (number * 10) + (*ctx->p - '0');
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Make sure the unsigned 64-bit value fits in the signed 64-bit range. */
|
|
Packit |
fd8b60 |
if (number > smax + 1 || (number > smax && neg == 1))
|
|
Packit |
fd8b60 |
return EOVERFLOW;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return k5_json_number_create(number * neg, val_out);
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse a JSON string (which must not quote Unicode code points above 256). */
|
|
Packit |
fd8b60 |
static int
|
|
Packit |
fd8b60 |
parse_string(struct decode_ctx *ctx, char **str_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
const unsigned char *p, *start, *end = NULL;
|
|
Packit |
fd8b60 |
const char *q;
|
|
Packit |
fd8b60 |
char *buf, *pos;
|
|
Packit |
fd8b60 |
unsigned int code;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*str_out = NULL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Find the start and end of the string. */
|
|
Packit |
fd8b60 |
if (*ctx->p != '"')
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
start = ++ctx->p;
|
|
Packit |
fd8b60 |
for (; *ctx->p != '\0'; ctx->p++) {
|
|
Packit |
fd8b60 |
if (*ctx->p == '\\') {
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
if (*ctx->p == '\0')
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
} else if (*ctx->p == '"') {
|
|
Packit |
fd8b60 |
end = ctx->p++;
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
if (end == NULL)
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
pos = buf = malloc(end - start + 1);
|
|
Packit |
fd8b60 |
if (buf == NULL)
|
|
Packit |
fd8b60 |
return ENOMEM;
|
|
Packit |
fd8b60 |
for (p = start; p < end;) {
|
|
Packit |
fd8b60 |
if (*p == '\\') {
|
|
Packit |
fd8b60 |
p++;
|
|
Packit |
fd8b60 |
if (*p == 'u' && is_hex_digit(p[1]) && is_hex_digit(p[2]) &&
|
|
Packit |
fd8b60 |
is_hex_digit(p[3]) && is_hex_digit(p[4])) {
|
|
Packit |
fd8b60 |
code = (hexval(p[1]) << 12) | (hexval(p[2]) << 8) |
|
|
Packit |
fd8b60 |
(hexval(p[3]) << 4) | hexval(p[4]);
|
|
Packit |
fd8b60 |
if (code <= 0xff) {
|
|
Packit |
fd8b60 |
*pos++ = code;
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
/* Code points above 0xff don't need to be quoted, so we
|
|
Packit |
fd8b60 |
* don't implement translating those into UTF-8. */
|
|
Packit |
fd8b60 |
free(buf);
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
p += 5;
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
q = strchr(quotemap_json, *p);
|
|
Packit |
fd8b60 |
if (q != NULL) {
|
|
Packit |
fd8b60 |
*pos++ = quotemap_c[q - quotemap_json];
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
free(buf);
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
p++;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
*pos++ = *p++;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
*pos = '\0';
|
|
Packit |
fd8b60 |
*str_out = buf;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse an object association and place it into obj. */
|
|
Packit |
fd8b60 |
static int
|
|
Packit |
fd8b60 |
parse_object_association(k5_json_object obj, struct decode_ctx *ctx)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
char *key = NULL;
|
|
Packit |
fd8b60 |
k5_json_value val;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse the key and value. */
|
|
Packit |
fd8b60 |
ret = parse_string(ctx, &key);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
if (white_spaces(ctx))
|
|
Packit |
fd8b60 |
goto invalid;
|
|
Packit |
fd8b60 |
if (*ctx->p != ':')
|
|
Packit |
fd8b60 |
goto invalid;
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
if (white_spaces(ctx))
|
|
Packit |
fd8b60 |
goto invalid;
|
|
Packit |
fd8b60 |
ret = parse_value(ctx, &val;;
|
|
Packit |
fd8b60 |
if (ret) {
|
|
Packit |
fd8b60 |
free(key);
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Add the key and value to obj. */
|
|
Packit |
fd8b60 |
ret = k5_json_object_set(obj, key, val);
|
|
Packit |
fd8b60 |
free(key);
|
|
Packit |
fd8b60 |
k5_json_release(val);
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
invalid:
|
|
Packit |
fd8b60 |
free(key);
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse a JSON object. */
|
|
Packit |
fd8b60 |
static int
|
|
Packit |
fd8b60 |
parse_object(struct decode_ctx *ctx, k5_json_object *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_object obj = NULL;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*val_out = NULL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse past the opening brace. */
|
|
Packit |
fd8b60 |
if (*ctx->p != '{')
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
if (white_spaces(ctx))
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
ret = k5_json_object_create(&obj);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Pairs associations until we reach the terminating brace. */
|
|
Packit |
fd8b60 |
if (*ctx->p != '}') {
|
|
Packit |
fd8b60 |
while (1) {
|
|
Packit |
fd8b60 |
ret = parse_object_association(obj, ctx);
|
|
Packit |
fd8b60 |
if (ret) {
|
|
Packit |
fd8b60 |
k5_json_release(obj);
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
if (white_spaces(ctx))
|
|
Packit |
fd8b60 |
goto invalid;
|
|
Packit |
fd8b60 |
if (*ctx->p == '}')
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
if (*ctx->p != ',')
|
|
Packit |
fd8b60 |
goto invalid;
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
if (white_spaces(ctx))
|
|
Packit |
fd8b60 |
goto invalid;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
*val_out = obj;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
invalid:
|
|
Packit |
fd8b60 |
k5_json_release(obj);
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse an value and place it into array. */
|
|
Packit |
fd8b60 |
static int
|
|
Packit |
fd8b60 |
parse_array_item(k5_json_array array, struct decode_ctx *ctx)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_value val;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
ret = parse_value(ctx, &val;;
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
ret = k5_json_array_add(array, val);
|
|
Packit |
fd8b60 |
k5_json_release(val);
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse a JSON array. */
|
|
Packit |
fd8b60 |
static int
|
|
Packit |
fd8b60 |
parse_array(struct decode_ctx *ctx, k5_json_array *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_array array = NULL;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*val_out = NULL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse past the opening bracket. */
|
|
Packit |
fd8b60 |
if (*ctx->p != '[')
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
if (white_spaces(ctx))
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
ret = k5_json_array_create(&array);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Pairs values until we reach the terminating bracket. */
|
|
Packit |
fd8b60 |
if (*ctx->p != ']') {
|
|
Packit |
fd8b60 |
while (1) {
|
|
Packit |
fd8b60 |
ret = parse_array_item(array, ctx);
|
|
Packit |
fd8b60 |
if (ret) {
|
|
Packit |
fd8b60 |
k5_json_release(array);
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
if (white_spaces(ctx))
|
|
Packit |
fd8b60 |
goto invalid;
|
|
Packit |
fd8b60 |
if (*ctx->p == ']')
|
|
Packit |
fd8b60 |
break;
|
|
Packit |
fd8b60 |
if (*ctx->p != ',')
|
|
Packit |
fd8b60 |
goto invalid;
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
if (white_spaces(ctx))
|
|
Packit |
fd8b60 |
goto invalid;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
ctx->p++;
|
|
Packit |
fd8b60 |
*val_out = array;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
invalid:
|
|
Packit |
fd8b60 |
k5_json_release(array);
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
/* Parse a JSON value of any type. */
|
|
Packit |
fd8b60 |
static int
|
|
Packit |
fd8b60 |
parse_value(struct decode_ctx *ctx, k5_json_value *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
k5_json_null null;
|
|
Packit |
fd8b60 |
k5_json_bool bval;
|
|
Packit |
fd8b60 |
k5_json_number num;
|
|
Packit |
fd8b60 |
k5_json_string str;
|
|
Packit |
fd8b60 |
k5_json_object obj;
|
|
Packit |
fd8b60 |
k5_json_array array;
|
|
Packit |
fd8b60 |
char *cstring;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*val_out = NULL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (white_spaces(ctx))
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
if (*ctx->p == '"') {
|
|
Packit |
fd8b60 |
ret = parse_string(ctx, &cstring);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
ret = k5_json_string_create(cstring, &str);
|
|
Packit |
fd8b60 |
free(cstring);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
*val_out = str;
|
|
Packit |
fd8b60 |
} else if (*ctx->p == '{') {
|
|
Packit |
fd8b60 |
if (ctx->depth-- == 1)
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
ret = parse_object(ctx, &obj);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
ctx->depth++;
|
|
Packit |
fd8b60 |
*val_out = obj;
|
|
Packit |
fd8b60 |
} else if (*ctx->p == '[') {
|
|
Packit |
fd8b60 |
if (ctx->depth-- == 1)
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
ret = parse_array(ctx, &array);
|
|
Packit |
fd8b60 |
ctx->depth++;
|
|
Packit |
fd8b60 |
*val_out = array;
|
|
Packit |
fd8b60 |
} else if (is_digit(*ctx->p) || *ctx->p == '-') {
|
|
Packit |
fd8b60 |
ret = parse_number(ctx, &num);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
*val_out = num;
|
|
Packit |
fd8b60 |
} else if (strncmp((char *)ctx->p, "null", 4) == 0) {
|
|
Packit |
fd8b60 |
ctx->p += 4;
|
|
Packit |
fd8b60 |
ret = k5_json_null_create(&null);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
*val_out = null;
|
|
Packit |
fd8b60 |
} else if (strncmp((char *)ctx->p, "true", 4) == 0) {
|
|
Packit |
fd8b60 |
ctx->p += 4;
|
|
Packit |
fd8b60 |
ret = k5_json_bool_create(1, &bval);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
*val_out = bval;
|
|
Packit |
fd8b60 |
} else if (strncmp((char *)ctx->p, "false", 5) == 0) {
|
|
Packit |
fd8b60 |
ctx->p += 5;
|
|
Packit |
fd8b60 |
ret = k5_json_bool_create(0, &bval);
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
*val_out = bval;
|
|
Packit |
fd8b60 |
} else {
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
int
|
|
Packit |
fd8b60 |
k5_json_decode(const char *string, k5_json_value *val_out)
|
|
Packit |
fd8b60 |
{
|
|
Packit |
fd8b60 |
struct decode_ctx ctx;
|
|
Packit |
fd8b60 |
k5_json_value val;
|
|
Packit |
fd8b60 |
int ret;
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
*val_out = NULL;
|
|
Packit |
fd8b60 |
ctx.p = (unsigned char *)string;
|
|
Packit |
fd8b60 |
ctx.depth = MAX_DECODE_DEPTH;
|
|
Packit |
fd8b60 |
ret = parse_value(&ctx, &val;;
|
|
Packit |
fd8b60 |
if (ret)
|
|
Packit |
fd8b60 |
return ret;
|
|
Packit |
fd8b60 |
if (white_spaces(&ctx) == 0) {
|
|
Packit |
fd8b60 |
k5_json_release(val);
|
|
Packit |
fd8b60 |
return EINVAL;
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
*val_out = val;
|
|
Packit |
fd8b60 |
return 0;
|
|
Packit |
fd8b60 |
}
|