Blame src/util/support/json.c

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
}