diff options
Diffstat (limited to 'comm/third_party/json-c/json_object.c')
-rw-r--r-- | comm/third_party/json-c/json_object.c | 1808 |
1 files changed, 1808 insertions, 0 deletions
diff --git a/comm/third_party/json-c/json_object.c b/comm/third_party/json-c/json_object.c new file mode 100644 index 0000000000..e52ca4071a --- /dev/null +++ b/comm/third_party/json-c/json_object.c @@ -0,0 +1,1808 @@ +/* + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark <michael@metaparadigm.com> + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include "strerror_override.h" + +#include <assert.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#include <math.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "arraylist.h" +#include "debug.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_util.h" +#include "linkhash.h" +#include "math_compat.h" +#include "printbuf.h" +#include "snprintf_compat.h" +#include "strdup_compat.h" + +/* Avoid ctype.h and locale overhead */ +#define is_plain_digit(c) ((c) >= '0' && (c) <= '9') + +#if SIZEOF_LONG_LONG != SIZEOF_INT64_T +#error The long long type is not 64-bits +#endif + +#ifndef SSIZE_T_MAX +#if SIZEOF_SSIZE_T == SIZEOF_INT +#define SSIZE_T_MAX INT_MAX +#elif SIZEOF_SSIZE_T == SIZEOF_LONG +#define SSIZE_T_MAX LONG_MAX +#elif SIZEOF_SSIZE_T == SIZEOF_LONG_LONG +#define SSIZE_T_MAX LLONG_MAX +#else +#error Unable to determine size of ssize_t +#endif +#endif + +const char *json_hex_chars = "0123456789abcdefABCDEF"; + +static void json_object_generic_delete(struct json_object *jso); + +#if defined(_MSC_VER) && (_MSC_VER <= 1800) +/* VS2013 doesn't know about "inline" */ +#define inline __inline +#elif defined(AIX_CC) +#define inline +#endif + +/* + * Helper functions to more safely cast to a particular type of json_object + */ +static inline struct json_object_object *JC_OBJECT(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_object *JC_OBJECT_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_array *JC_ARRAY(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_array *JC_ARRAY_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_boolean *JC_BOOL(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_boolean *JC_BOOL_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_double *JC_DOUBLE(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_double *JC_DOUBLE_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_int *JC_INT(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_int *JC_INT_C(const struct json_object *jso) +{ + return (const void *)jso; +} +static inline struct json_object_string *JC_STRING(struct json_object *jso) +{ + return (void *)jso; +} +static inline const struct json_object_string *JC_STRING_C(const struct json_object *jso) +{ + return (const void *)jso; +} + +#define JC_CONCAT(a, b) a##b +#define JC_CONCAT3(a, b, c) a##b##c + +#define JSON_OBJECT_NEW(jtype) \ + (struct JC_CONCAT(json_object_, jtype) *)json_object_new( \ + JC_CONCAT(json_type_, jtype), sizeof(struct JC_CONCAT(json_object_, jtype)), \ + &JC_CONCAT3(json_object_, jtype, _to_json_string)) + +static inline struct json_object *json_object_new(enum json_type o_type, size_t alloc_size, + json_object_to_json_string_fn *to_json_string); + +static void json_object_object_delete(struct json_object *jso_base); +static void json_object_string_delete(struct json_object *jso); +static void json_object_array_delete(struct json_object *jso); + +static json_object_to_json_string_fn json_object_object_to_json_string; +static json_object_to_json_string_fn json_object_boolean_to_json_string; +static json_object_to_json_string_fn json_object_double_to_json_string_default; +static json_object_to_json_string_fn json_object_int_to_json_string; +static json_object_to_json_string_fn json_object_string_to_json_string; +static json_object_to_json_string_fn json_object_array_to_json_string; +static json_object_to_json_string_fn _json_object_userdata_to_json_string; + +#ifndef JSON_NORETURN +#if defined(_MSC_VER) +#define JSON_NORETURN __declspec(noreturn) +#elif defined(__OS400__) +#define JSON_NORETURN +#else +/* 'cold' attribute is for optimization, telling the computer this code + * path is unlikely. + */ +#define JSON_NORETURN __attribute__((noreturn, cold)) +#endif +#endif +/** + * Abort and optionally print a message on standard error. + * This should be used rather than assert() for unconditional abortion + * (in particular for code paths which are never supposed to be run). + * */ +JSON_NORETURN static void json_abort(const char *message); + +/* helper for accessing the optimized string data component in json_object + */ +static inline char *get_string_component_mutable(struct json_object *jso) +{ + if (JC_STRING_C(jso)->len < 0) + { + /* Due to json_object_set_string(), we might have a pointer */ + return JC_STRING(jso)->c_string.pdata; + } + return JC_STRING(jso)->c_string.idata; +} +static inline const char *get_string_component(const struct json_object *jso) +{ + return get_string_component_mutable((void *)(uintptr_t)(const void *)jso); +} + +/* string escaping */ + +static int json_escape_str(struct printbuf *pb, const char *str, size_t len, int flags) +{ + size_t pos = 0, start_offset = 0; + unsigned char c; + while (len) + { + --len; + c = str[pos]; + switch (c) + { + case '\b': + case '\n': + case '\r': + case '\t': + case '\f': + case '"': + case '\\': + case '/': + if ((flags & JSON_C_TO_STRING_NOSLASHESCAPE) && c == '/') + { + pos++; + break; + } + + if (pos > start_offset) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + + if (c == '\b') + printbuf_memappend(pb, "\\b", 2); + else if (c == '\n') + printbuf_memappend(pb, "\\n", 2); + else if (c == '\r') + printbuf_memappend(pb, "\\r", 2); + else if (c == '\t') + printbuf_memappend(pb, "\\t", 2); + else if (c == '\f') + printbuf_memappend(pb, "\\f", 2); + else if (c == '"') + printbuf_memappend(pb, "\\\"", 2); + else if (c == '\\') + printbuf_memappend(pb, "\\\\", 2); + else if (c == '/') + printbuf_memappend(pb, "\\/", 2); + + start_offset = ++pos; + break; + default: + if (c < ' ') + { + char sbuf[7]; + if (pos > start_offset) + printbuf_memappend(pb, str + start_offset, + pos - start_offset); + snprintf(sbuf, sizeof(sbuf), "\\u00%c%c", json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + printbuf_memappend_fast(pb, sbuf, (int)sizeof(sbuf) - 1); + start_offset = ++pos; + } + else + pos++; + } + } + if (pos > start_offset) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} + +/* reference counting */ + +struct json_object *json_object_get(struct json_object *jso) +{ + if (!jso) + return jso; + + // Don't overflow the refcounter. + assert(jso->_ref_count < UINT32_MAX); + +#if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) + __sync_add_and_fetch(&jso->_ref_count, 1); +#else + ++jso->_ref_count; +#endif + + return jso; +} + +int json_object_put(struct json_object *jso) +{ + if (!jso) + return 0; + + /* Avoid invalid free and crash explicitly instead of (silently) + * segfaulting. + */ + assert(jso->_ref_count > 0); + +#if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) + /* Note: this only allow the refcount to remain correct + * when multiple threads are adjusting it. It is still an error + * for a thread to decrement the refcount if it doesn't "own" it, + * as that can result in the thread that loses the race to 0 + * operating on an already-freed object. + */ + if (__sync_sub_and_fetch(&jso->_ref_count, 1) > 0) + return 0; +#else + if (--jso->_ref_count > 0) + return 0; +#endif + + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + switch (jso->o_type) + { + case json_type_object: json_object_object_delete(jso); break; + case json_type_array: json_object_array_delete(jso); break; + case json_type_string: json_object_string_delete(jso); break; + default: json_object_generic_delete(jso); break; + } + return 1; +} + +/* generic object construction and destruction parts */ + +static void json_object_generic_delete(struct json_object *jso) +{ + printbuf_free(jso->_pb); + free(jso); +} + +static inline struct json_object *json_object_new(enum json_type o_type, size_t alloc_size, + json_object_to_json_string_fn *to_json_string) +{ + struct json_object *jso; + + jso = (struct json_object *)malloc(alloc_size); + if (!jso) + return NULL; + + jso->o_type = o_type; + jso->_ref_count = 1; + jso->_to_json_string = to_json_string; + jso->_pb = NULL; + jso->_user_delete = NULL; + jso->_userdata = NULL; + //jso->... // Type-specific fields must be set by caller + + return jso; +} + +/* type checking functions */ + +int json_object_is_type(const struct json_object *jso, enum json_type type) +{ + if (!jso) + return (type == json_type_null); + return (jso->o_type == type); +} + +enum json_type json_object_get_type(const struct json_object *jso) +{ + if (!jso) + return json_type_null; + return jso->o_type; +} + +void *json_object_get_userdata(json_object *jso) +{ + return jso ? jso->_userdata : NULL; +} + +void json_object_set_userdata(json_object *jso, void *userdata, json_object_delete_fn *user_delete) +{ + // Can't return failure, so abort if we can't perform the operation. + assert(jso != NULL); + + // First, clean up any previously existing user info + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + + jso->_userdata = userdata; + jso->_user_delete = user_delete; +} + +/* set a custom conversion to string */ + +void json_object_set_serializer(json_object *jso, json_object_to_json_string_fn *to_string_func, + void *userdata, json_object_delete_fn *user_delete) +{ + json_object_set_userdata(jso, userdata, user_delete); + + if (to_string_func == NULL) + { + // Reset to the standard serialization function + switch (jso->o_type) + { + case json_type_null: jso->_to_json_string = NULL; break; + case json_type_boolean: + jso->_to_json_string = &json_object_boolean_to_json_string; + break; + case json_type_double: + jso->_to_json_string = &json_object_double_to_json_string_default; + break; + case json_type_int: jso->_to_json_string = &json_object_int_to_json_string; break; + case json_type_object: + jso->_to_json_string = &json_object_object_to_json_string; + break; + case json_type_array: + jso->_to_json_string = &json_object_array_to_json_string; + break; + case json_type_string: + jso->_to_json_string = &json_object_string_to_json_string; + break; + } + return; + } + + jso->_to_json_string = to_string_func; +} + +/* extended conversion to string */ + +const char *json_object_to_json_string_length(struct json_object *jso, int flags, size_t *length) +{ + const char *r = NULL; + size_t s = 0; + + if (!jso) + { + s = 4; + r = "null"; + } + else if ((jso->_pb) || (jso->_pb = printbuf_new())) + { + printbuf_reset(jso->_pb); + + if (jso->_to_json_string(jso, jso->_pb, 0, flags) >= 0) + { + s = (size_t)jso->_pb->bpos; + r = jso->_pb->buf; + } + } + + if (length) + *length = s; + return r; +} + +const char *json_object_to_json_string_ext(struct json_object *jso, int flags) +{ + return json_object_to_json_string_length(jso, flags, NULL); +} + +/* backwards-compatible conversion to string */ + +const char *json_object_to_json_string(struct json_object *jso) +{ + return json_object_to_json_string_ext(jso, JSON_C_TO_STRING_SPACED); +} + +static void indent(struct printbuf *pb, int level, int flags) +{ + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (flags & JSON_C_TO_STRING_PRETTY_TAB) + { + printbuf_memset(pb, -1, '\t', level); + } + else + { + printbuf_memset(pb, -1, ' ', level * 2); + } + } +} + +/* json_object_object */ + +static int json_object_object_to_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + int had_children = 0; + struct json_object_iter iter; + + printbuf_strappend(pb, "{" /*}*/); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + json_object_object_foreachC(jso, iter) + { + if (had_children) + { + printbuf_strappend(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) + printbuf_strappend(pb, " "); + indent(pb, level + 1, flags); + printbuf_strappend(pb, "\""); + json_escape_str(pb, iter.key, strlen(iter.key), flags); + if (flags & JSON_C_TO_STRING_SPACED) + printbuf_strappend(pb, "\": "); + else + printbuf_strappend(pb, "\":"); + if (iter.val == NULL) + printbuf_strappend(pb, "null"); + else if (iter.val->_to_json_string(iter.val, pb, level + 1, flags) < 0) + return -1; + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + printbuf_strappend(pb, "\n"); + indent(pb, level, flags); + } + if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) + return printbuf_strappend(pb, /*{*/ " }"); + else + return printbuf_strappend(pb, /*{*/ "}"); +} + +static void json_object_lh_entry_free(struct lh_entry *ent) +{ + if (!lh_entry_k_is_constant(ent)) + free(lh_entry_k(ent)); + json_object_put((struct json_object *)lh_entry_v(ent)); +} + +static void json_object_object_delete(struct json_object *jso_base) +{ + lh_table_free(JC_OBJECT(jso_base)->c_object); + json_object_generic_delete(jso_base); +} + +struct json_object *json_object_new_object(void) +{ + struct json_object_object *jso = JSON_OBJECT_NEW(object); + if (!jso) + return NULL; + jso->c_object = + lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTRIES, &json_object_lh_entry_free); + if (!jso->c_object) + { + json_object_generic_delete(&jso->base); + errno = ENOMEM; + return NULL; + } + return &jso->base; +} + +struct lh_table *json_object_get_object(const struct json_object *jso) +{ + if (!jso) + return NULL; + switch (jso->o_type) + { + case json_type_object: return JC_OBJECT_C(jso)->c_object; + default: return NULL; + } +} + +int json_object_object_add_ex(struct json_object *jso, const char *const key, + struct json_object *const val, const unsigned opts) +{ + struct json_object *existing_value = NULL; + struct lh_entry *existing_entry; + unsigned long hash; + + assert(json_object_get_type(jso) == json_type_object); + + // We lookup the entry and replace the value, rather than just deleting + // and re-adding it, so the existing key remains valid. + hash = lh_get_hash(JC_OBJECT(jso)->c_object, (const void *)key); + existing_entry = + (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) + ? NULL + : lh_table_lookup_entry_w_hash(JC_OBJECT(jso)->c_object, (const void *)key, hash); + + // The caller must avoid creating loops in the object tree, but do a + // quick check anyway to make sure we're not creating a trivial loop. + if (jso == val) + return -1; + + if (!existing_entry) + { + const void *const k = + (opts & JSON_C_OBJECT_ADD_CONSTANT_KEY) ? (const void *)key : strdup(key); + if (k == NULL) + return -1; + return lh_table_insert_w_hash(JC_OBJECT(jso)->c_object, k, val, hash, opts); + } + existing_value = (json_object *)lh_entry_v(existing_entry); + if (existing_value) + json_object_put(existing_value); + lh_entry_set_val(existing_entry, val); + return 0; +} + +int json_object_object_add(struct json_object *jso, const char *key, struct json_object *val) +{ + return json_object_object_add_ex(jso, key, val, 0); +} + +int json_object_object_length(const struct json_object *jso) +{ + assert(json_object_get_type(jso) == json_type_object); + return lh_table_length(JC_OBJECT_C(jso)->c_object); +} + +size_t json_c_object_sizeof(void) +{ + return sizeof(struct json_object); +} + +struct json_object *json_object_object_get(const struct json_object *jso, const char *key) +{ + struct json_object *result = NULL; + json_object_object_get_ex(jso, key, &result); + return result; +} + +json_bool json_object_object_get_ex(const struct json_object *jso, const char *key, + struct json_object **value) +{ + if (value != NULL) + *value = NULL; + + if (NULL == jso) + return 0; + + switch (jso->o_type) + { + case json_type_object: + return lh_table_lookup_ex(JC_OBJECT_C(jso)->c_object, (const void *)key, + (void **)value); + default: + if (value != NULL) + *value = NULL; + return 0; + } +} + +void json_object_object_del(struct json_object *jso, const char *key) +{ + assert(json_object_get_type(jso) == json_type_object); + lh_table_delete(JC_OBJECT(jso)->c_object, key); +} + +/* json_object_boolean */ + +static int json_object_boolean_to_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + if (JC_BOOL(jso)->c_boolean) + return printbuf_strappend(pb, "true"); + return printbuf_strappend(pb, "false"); +} + +struct json_object *json_object_new_boolean(json_bool b) +{ + struct json_object_boolean *jso = JSON_OBJECT_NEW(boolean); + if (!jso) + return NULL; + jso->c_boolean = b; + return &jso->base; +} + +json_bool json_object_get_boolean(const struct json_object *jso) +{ + if (!jso) + return 0; + switch (jso->o_type) + { + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + case json_type_int: + switch (JC_INT_C(jso)->cint_type) + { + case json_object_int_type_int64: return (JC_INT_C(jso)->cint.c_int64 != 0); + case json_object_int_type_uint64: return (JC_INT_C(jso)->cint.c_uint64 != 0); + default: json_abort("invalid cint_type"); + } + case json_type_double: return (JC_DOUBLE_C(jso)->c_double != 0); + case json_type_string: return (JC_STRING_C(jso)->len != 0); + default: return 0; + } +} + +int json_object_set_boolean(struct json_object *jso, json_bool new_value) +{ + if (!jso || jso->o_type != json_type_boolean) + return 0; + JC_BOOL(jso)->c_boolean = new_value; + return 1; +} + +/* json_object_int */ + +static int json_object_int_to_json_string(struct json_object *jso, struct printbuf *pb, int level, + int flags) +{ + /* room for 19 digits, the sign char, and a null term */ + char sbuf[21]; + if (JC_INT(jso)->cint_type == json_object_int_type_int64) + snprintf(sbuf, sizeof(sbuf), "%" PRId64, JC_INT(jso)->cint.c_int64); + else + snprintf(sbuf, sizeof(sbuf), "%" PRIu64, JC_INT(jso)->cint.c_uint64); + return printbuf_memappend(pb, sbuf, strlen(sbuf)); +} + +struct json_object *json_object_new_int(int32_t i) +{ + return json_object_new_int64(i); +} + +int32_t json_object_get_int(const struct json_object *jso) +{ + int64_t cint64 = 0; + double cdouble; + enum json_type o_type; + + if (!jso) + return 0; + + o_type = jso->o_type; + if (o_type == json_type_int) + { + const struct json_object_int *jsoint = JC_INT_C(jso); + if (jsoint->cint_type == json_object_int_type_int64) + { + cint64 = jsoint->cint.c_int64; + } + else + { + if (jsoint->cint.c_uint64 >= INT64_MAX) + cint64 = INT64_MAX; + else + cint64 = (int64_t)jsoint->cint.c_uint64; + } + } + else if (o_type == json_type_string) + { + /* + * Parse strings into 64-bit numbers, then use the + * 64-to-32-bit number handling below. + */ + if (json_parse_int64(get_string_component(jso), &cint64) != 0) + return 0; /* whoops, it didn't work. */ + o_type = json_type_int; + } + + switch (o_type) + { + case json_type_int: + /* Make sure we return the correct values for out of range numbers. */ + if (cint64 <= INT32_MIN) + return INT32_MIN; + if (cint64 >= INT32_MAX) + return INT32_MAX; + return (int32_t)cint64; + case json_type_double: + cdouble = JC_DOUBLE_C(jso)->c_double; + if (cdouble <= INT32_MIN) + return INT32_MIN; + if (cdouble >= INT32_MAX) + return INT32_MAX; + return (int32_t)cdouble; + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + default: return 0; + } +} + +int json_object_set_int(struct json_object *jso, int new_value) +{ + return json_object_set_int64(jso, (int64_t)new_value); +} + +struct json_object *json_object_new_int64(int64_t i) +{ + struct json_object_int *jso = JSON_OBJECT_NEW(int); + if (!jso) + return NULL; + jso->cint.c_int64 = i; + jso->cint_type = json_object_int_type_int64; + return &jso->base; +} + +struct json_object *json_object_new_uint64(uint64_t i) +{ + struct json_object_int *jso = JSON_OBJECT_NEW(int); + if (!jso) + return NULL; + jso->cint.c_uint64 = i; + jso->cint_type = json_object_int_type_uint64; + return &jso->base; +} + +int64_t json_object_get_int64(const struct json_object *jso) +{ + int64_t cint; + + if (!jso) + return 0; + switch (jso->o_type) + { + case json_type_int: + { + const struct json_object_int *jsoint = JC_INT_C(jso); + switch (jsoint->cint_type) + { + case json_object_int_type_int64: return jsoint->cint.c_int64; + case json_object_int_type_uint64: + if (jsoint->cint.c_uint64 >= INT64_MAX) + return INT64_MAX; + return (int64_t)jsoint->cint.c_uint64; + default: json_abort("invalid cint_type"); + } + } + case json_type_double: + // INT64_MAX can't be exactly represented as a double + // so cast to tell the compiler it's ok to round up. + if (JC_DOUBLE_C(jso)->c_double >= (double)INT64_MAX) + return INT64_MAX; + if (JC_DOUBLE_C(jso)->c_double <= INT64_MIN) + return INT64_MIN; + return (int64_t)JC_DOUBLE_C(jso)->c_double; + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + case json_type_string: + if (json_parse_int64(get_string_component(jso), &cint) == 0) + return cint; + /* FALLTHRU */ + default: return 0; + } +} + +uint64_t json_object_get_uint64(const struct json_object *jso) +{ + uint64_t cuint; + + if (!jso) + return 0; + switch (jso->o_type) + { + case json_type_int: + { + const struct json_object_int *jsoint = JC_INT_C(jso); + switch (jsoint->cint_type) + { + case json_object_int_type_int64: + if (jsoint->cint.c_int64 < 0) + return 0; + return (uint64_t)jsoint->cint.c_int64; + case json_object_int_type_uint64: return jsoint->cint.c_uint64; + default: json_abort("invalid cint_type"); + } + } + case json_type_double: + // UINT64_MAX can't be exactly represented as a double + // so cast to tell the compiler it's ok to round up. + if (JC_DOUBLE_C(jso)->c_double >= (double)UINT64_MAX) + return UINT64_MAX; + if (JC_DOUBLE_C(jso)->c_double < 0) + return 0; + return (uint64_t)JC_DOUBLE_C(jso)->c_double; + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + case json_type_string: + if (json_parse_uint64(get_string_component(jso), &cuint) == 0) + return cuint; + /* FALLTHRU */ + default: return 0; + } +} + +int json_object_set_int64(struct json_object *jso, int64_t new_value) +{ + if (!jso || jso->o_type != json_type_int) + return 0; + JC_INT(jso)->cint.c_int64 = new_value; + JC_INT(jso)->cint_type = json_object_int_type_int64; + return 1; +} + +int json_object_set_uint64(struct json_object *jso, uint64_t new_value) +{ + if (!jso || jso->o_type != json_type_int) + return 0; + JC_INT(jso)->cint.c_uint64 = new_value; + JC_INT(jso)->cint_type = json_object_int_type_uint64; + return 1; +} + +int json_object_int_inc(struct json_object *jso, int64_t val) +{ + struct json_object_int *jsoint; + if (!jso || jso->o_type != json_type_int) + return 0; + jsoint = JC_INT(jso); + switch (jsoint->cint_type) + { + case json_object_int_type_int64: + if (val > 0 && jsoint->cint.c_int64 > INT64_MAX - val) + { + jsoint->cint.c_uint64 = (uint64_t)jsoint->cint.c_int64 + (uint64_t)val; + jsoint->cint_type = json_object_int_type_uint64; + } + else if (val < 0 && jsoint->cint.c_int64 < INT64_MIN - val) + { + jsoint->cint.c_int64 = INT64_MIN; + } + else + { + jsoint->cint.c_int64 += val; + } + return 1; + case json_object_int_type_uint64: + if (val > 0 && jsoint->cint.c_uint64 > UINT64_MAX - (uint64_t)val) + { + jsoint->cint.c_uint64 = UINT64_MAX; + } + else if (val < 0 && jsoint->cint.c_uint64 < (uint64_t)(-val)) + { + jsoint->cint.c_int64 = (int64_t)jsoint->cint.c_uint64 + val; + jsoint->cint_type = json_object_int_type_int64; + } + else if (val < 0 && jsoint->cint.c_uint64 >= (uint64_t)(-val)) + { + jsoint->cint.c_uint64 -= (uint64_t)(-val); + } + else + { + jsoint->cint.c_uint64 += val; + } + return 1; + default: json_abort("invalid cint_type"); + } +} + +/* json_object_double */ + +#if defined(HAVE___THREAD) +// i.e. __thread or __declspec(thread) +static SPEC___THREAD char *tls_serialization_float_format = NULL; +#endif +static char *global_serialization_float_format = NULL; + +int json_c_set_serialization_double_format(const char *double_format, int global_or_thread) +{ + if (global_or_thread == JSON_C_OPTION_GLOBAL) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + { + free(tls_serialization_float_format); + tls_serialization_float_format = NULL; + } +#endif + if (global_serialization_float_format) + free(global_serialization_float_format); + if (double_format) + { + char *p = strdup(double_format); + if (p == NULL) + { + _json_c_set_last_err("json_c_set_serialization_double_format: " + "out of memory\n"); + return -1; + } + global_serialization_float_format = p; + } + else + { + global_serialization_float_format = NULL; + } + } + else if (global_or_thread == JSON_C_OPTION_THREAD) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + { + free(tls_serialization_float_format); + tls_serialization_float_format = NULL; + } + if (double_format) + { + char *p = strdup(double_format); + if (p == NULL) + { + _json_c_set_last_err("json_c_set_serialization_double_format: " + "out of memory\n"); + return -1; + } + tls_serialization_float_format = p; + } + else + { + tls_serialization_float_format = NULL; + } +#else + _json_c_set_last_err("json_c_set_serialization_double_format: not compiled " + "with __thread support\n"); + return -1; +#endif + } + else + { + _json_c_set_last_err("json_c_set_serialization_double_format: invalid " + "global_or_thread value: %d\n", global_or_thread); + return -1; + } + return 0; +} + +static int json_object_double_to_json_string_format(struct json_object *jso, struct printbuf *pb, + int level, int flags, const char *format) +{ + struct json_object_double *jsodbl = JC_DOUBLE(jso); + char buf[128], *p, *q; + int size; + /* Although JSON RFC does not support + * NaN or Infinity as numeric values + * ECMA 262 section 9.8.1 defines + * how to handle these cases as strings + */ + if (isnan(jsodbl->c_double)) + { + size = snprintf(buf, sizeof(buf), "NaN"); + } + else if (isinf(jsodbl->c_double)) + { + if (jsodbl->c_double > 0) + size = snprintf(buf, sizeof(buf), "Infinity"); + else + size = snprintf(buf, sizeof(buf), "-Infinity"); + } + else + { + const char *std_format = "%.17g"; + int format_drops_decimals = 0; + int looks_numeric = 0; + + if (!format) + { +#if defined(HAVE___THREAD) + if (tls_serialization_float_format) + format = tls_serialization_float_format; + else +#endif + if (global_serialization_float_format) + format = global_serialization_float_format; + else + format = std_format; + } + size = snprintf(buf, sizeof(buf), format, jsodbl->c_double); + + if (size < 0) + return -1; + + p = strchr(buf, ','); + if (p) + *p = '.'; + else + p = strchr(buf, '.'); + + if (format == std_format || strstr(format, ".0f") == NULL) + format_drops_decimals = 1; + + looks_numeric = /* Looks like *some* kind of number */ + is_plain_digit(buf[0]) || (size > 1 && buf[0] == '-' && is_plain_digit(buf[1])); + + if (size < (int)sizeof(buf) - 2 && looks_numeric && !p && /* Has no decimal point */ + strchr(buf, 'e') == NULL && /* Not scientific notation */ + format_drops_decimals) + { + // Ensure it looks like a float, even if snprintf didn't, + // unless a custom format is set to omit the decimal. + strcat(buf, ".0"); + size += 2; + } + if (p && (flags & JSON_C_TO_STRING_NOZERO)) + { + /* last useful digit, always keep 1 zero */ + p++; + for (q = p; *q; q++) + { + if (*q != '0') + p = q; + } + /* drop trailing zeroes */ + if (*p != 0) + *(++p) = 0; + size = p - buf; + } + } + // although unlikely, snprintf can fail + if (size < 0) + return -1; + + if (size >= (int)sizeof(buf)) + // The standard formats are guaranteed not to overrun the buffer, + // but if a custom one happens to do so, just silently truncate. + size = sizeof(buf) - 1; + printbuf_memappend(pb, buf, size); + return size; +} + +static int json_object_double_to_json_string_default(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, NULL); +} + +int json_object_double_to_json_string(struct json_object *jso, struct printbuf *pb, int level, + int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, + (const char *)jso->_userdata); +} + +struct json_object *json_object_new_double(double d) +{ + struct json_object_double *jso = JSON_OBJECT_NEW(double); + if (!jso) + return NULL; + jso->base._to_json_string = &json_object_double_to_json_string_default; + jso->c_double = d; + return &jso->base; +} + +struct json_object *json_object_new_double_s(double d, const char *ds) +{ + char *new_ds; + struct json_object *jso = json_object_new_double(d); + if (!jso) + return NULL; + + new_ds = strdup(ds); + if (!new_ds) + { + json_object_generic_delete(jso); + errno = ENOMEM; + return NULL; + } + json_object_set_serializer(jso, _json_object_userdata_to_json_string, new_ds, + json_object_free_userdata); + return jso; +} + +/* + * A wrapper around json_object_userdata_to_json_string() used only + * by json_object_new_double_s() just so json_object_set_double() can + * detect when it needs to reset the serializer to the default. + */ +static int _json_object_userdata_to_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + return json_object_userdata_to_json_string(jso, pb, level, flags); +} + +int json_object_userdata_to_json_string(struct json_object *jso, struct printbuf *pb, int level, + int flags) +{ + int userdata_len = strlen((const char *)jso->_userdata); + printbuf_memappend(pb, (const char *)jso->_userdata, userdata_len); + return userdata_len; +} + +void json_object_free_userdata(struct json_object *jso, void *userdata) +{ + free(userdata); +} + +double json_object_get_double(const struct json_object *jso) +{ + double cdouble; + char *errPtr = NULL; + + if (!jso) + return 0.0; + switch (jso->o_type) + { + case json_type_double: return JC_DOUBLE_C(jso)->c_double; + case json_type_int: + switch (JC_INT_C(jso)->cint_type) + { + case json_object_int_type_int64: return JC_INT_C(jso)->cint.c_int64; + case json_object_int_type_uint64: return JC_INT_C(jso)->cint.c_uint64; + default: json_abort("invalid cint_type"); + } + case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; + case json_type_string: + errno = 0; + cdouble = strtod(get_string_component(jso), &errPtr); + + /* if conversion stopped at the first character, return 0.0 */ + if (errPtr == get_string_component(jso)) + { + errno = EINVAL; + return 0.0; + } + + /* + * Check that the conversion terminated on something sensible + * + * For example, { "pay" : 123AB } would parse as 123. + */ + if (*errPtr != '\0') + { + errno = EINVAL; + return 0.0; + } + + /* + * If strtod encounters a string which would exceed the + * capacity of a double, it returns +/- HUGE_VAL and sets + * errno to ERANGE. But +/- HUGE_VAL is also a valid result + * from a conversion, so we need to check errno. + * + * Underflow also sets errno to ERANGE, but it returns 0 in + * that case, which is what we will return anyway. + * + * See CERT guideline ERR30-C + */ + if ((HUGE_VAL == cdouble || -HUGE_VAL == cdouble) && (ERANGE == errno)) + cdouble = 0.0; + return cdouble; + default: errno = EINVAL; return 0.0; + } +} + +int json_object_set_double(struct json_object *jso, double new_value) +{ + if (!jso || jso->o_type != json_type_double) + return 0; + JC_DOUBLE(jso)->c_double = new_value; + if (jso->_to_json_string == &_json_object_userdata_to_json_string) + json_object_set_serializer(jso, NULL, NULL, NULL); + return 1; +} + +/* json_object_string */ + +static int json_object_string_to_json_string(struct json_object *jso, struct printbuf *pb, + int level, int flags) +{ + ssize_t len = JC_STRING(jso)->len; + printbuf_strappend(pb, "\""); + json_escape_str(pb, get_string_component(jso), len < 0 ? -(ssize_t)len : len, flags); + printbuf_strappend(pb, "\""); + return 0; +} + +static void json_object_string_delete(struct json_object *jso) +{ + if (JC_STRING(jso)->len < 0) + free(JC_STRING(jso)->c_string.pdata); + json_object_generic_delete(jso); +} + +static struct json_object *_json_object_new_string(const char *s, const size_t len) +{ + size_t objsize; + struct json_object_string *jso; + + /* + * Structures Actual memory layout + * ------------------- -------------------- + * [json_object_string [json_object_string + * [json_object] [json_object] + * ...other fields... ...other fields... + * c_string] len + * bytes + * of + * string + * data + * \0] + */ + if (len > (SSIZE_T_MAX - (sizeof(*jso) - sizeof(jso->c_string)) - 1)) + return NULL; + objsize = (sizeof(*jso) - sizeof(jso->c_string)) + len + 1; + if (len < sizeof(void *)) + // We need a minimum size to support json_object_set_string() mutability + // so we can stuff a pointer into pdata :( + objsize += sizeof(void *) - len; + + jso = (struct json_object_string *)json_object_new(json_type_string, objsize, + &json_object_string_to_json_string); + + if (!jso) + return NULL; + jso->len = len; + memcpy(jso->c_string.idata, s, len); + // Cast below needed for Clang UB sanitizer + ((char *)jso->c_string.idata)[len] = '\0'; + return &jso->base; +} + +struct json_object *json_object_new_string(const char *s) +{ + return _json_object_new_string(s, strlen(s)); +} + +struct json_object *json_object_new_string_len(const char *s, const int len) +{ + return _json_object_new_string(s, len); +} + +const char *json_object_get_string(struct json_object *jso) +{ + if (!jso) + return NULL; + switch (jso->o_type) + { + case json_type_string: return get_string_component(jso); + default: return json_object_to_json_string(jso); + } +} + +static inline ssize_t _json_object_get_string_len(const struct json_object_string *jso) +{ + ssize_t len; + len = jso->len; + return (len < 0) ? -(ssize_t)len : len; +} +int json_object_get_string_len(const struct json_object *jso) +{ + if (!jso) + return 0; + switch (jso->o_type) + { + case json_type_string: return _json_object_get_string_len(JC_STRING_C(jso)); + default: return 0; + } +} + +static int _json_object_set_string_len(json_object *jso, const char *s, size_t len) +{ + char *dstbuf; + ssize_t curlen; + ssize_t newlen; + if (jso == NULL || jso->o_type != json_type_string) + return 0; + + if (len >= INT_MAX - 1) + // jso->len is a signed ssize_t, so it can't hold the + // full size_t range. json_object_get_string_len returns + // length as int, cap length at INT_MAX. + return 0; + + dstbuf = get_string_component_mutable(jso); + curlen = JC_STRING(jso)->len; + if (curlen < 0) + curlen = -curlen; + newlen = len; + + if ((ssize_t)len > curlen) + { + // We have no way to return the new ptr from realloc(jso, newlen) + // and we have no way of knowing whether there's extra room available + // so we need to stuff a pointer in to pdata :( + dstbuf = (char *)malloc(len + 1); + if (dstbuf == NULL) + return 0; + if (JC_STRING(jso)->len < 0) + free(JC_STRING(jso)->c_string.pdata); + JC_STRING(jso)->c_string.pdata = dstbuf; + newlen = -(ssize_t)len; + } + else if (JC_STRING(jso)->len < 0) + { + // We've got enough room in the separate allocated buffer, + // so use it as-is and continue to indicate that pdata is used. + newlen = -(ssize_t)len; + } + + memcpy(dstbuf, (const void *)s, len); + dstbuf[len] = '\0'; + JC_STRING(jso)->len = newlen; + return 1; +} + +int json_object_set_string(json_object *jso, const char *s) +{ + return _json_object_set_string_len(jso, s, strlen(s)); +} + +int json_object_set_string_len(json_object *jso, const char *s, int len) +{ + return _json_object_set_string_len(jso, s, len); +} + +/* json_object_array */ + +static int json_object_array_to_json_string(struct json_object *jso, struct printbuf *pb, int level, + int flags) +{ + int had_children = 0; + size_t ii; + + printbuf_strappend(pb, "["); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + for (ii = 0; ii < json_object_array_length(jso); ii++) + { + struct json_object *val; + if (had_children) + { + printbuf_strappend(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + printbuf_strappend(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) + printbuf_strappend(pb, " "); + indent(pb, level + 1, flags); + val = json_object_array_get_idx(jso, ii); + if (val == NULL) + printbuf_strappend(pb, "null"); + else if (val->_to_json_string(val, pb, level + 1, flags) < 0) + return -1; + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + printbuf_strappend(pb, "\n"); + indent(pb, level, flags); + } + + if (flags & JSON_C_TO_STRING_SPACED && !(flags & JSON_C_TO_STRING_PRETTY)) + return printbuf_strappend(pb, " ]"); + return printbuf_strappend(pb, "]"); +} + +static void json_object_array_entry_free(void *data) +{ + json_object_put((struct json_object *)data); +} + +static void json_object_array_delete(struct json_object *jso) +{ + array_list_free(JC_ARRAY(jso)->c_array); + json_object_generic_delete(jso); +} + +struct json_object *json_object_new_array(void) +{ + return json_object_new_array_ext(ARRAY_LIST_DEFAULT_SIZE); +} +struct json_object *json_object_new_array_ext(int initial_size) +{ + struct json_object_array *jso = JSON_OBJECT_NEW(array); + if (!jso) + return NULL; + jso->c_array = array_list_new2(&json_object_array_entry_free, initial_size); + if (jso->c_array == NULL) + { + free(jso); + return NULL; + } + return &jso->base; +} + +struct array_list *json_object_get_array(const struct json_object *jso) +{ + if (!jso) + return NULL; + switch (jso->o_type) + { + case json_type_array: return JC_ARRAY_C(jso)->c_array; + default: return NULL; + } +} + +void json_object_array_sort(struct json_object *jso, int (*sort_fn)(const void *, const void *)) +{ + assert(json_object_get_type(jso) == json_type_array); + array_list_sort(JC_ARRAY(jso)->c_array, sort_fn); +} + +struct json_object *json_object_array_bsearch(const struct json_object *key, + const struct json_object *jso, + int (*sort_fn)(const void *, const void *)) +{ + struct json_object **result; + + assert(json_object_get_type(jso) == json_type_array); + result = (struct json_object **)array_list_bsearch((const void **)(void *)&key, + JC_ARRAY_C(jso)->c_array, sort_fn); + + if (!result) + return NULL; + return *result; +} + +size_t json_object_array_length(const struct json_object *jso) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_length(JC_ARRAY_C(jso)->c_array); +} + +int json_object_array_add(struct json_object *jso, struct json_object *val) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_add(JC_ARRAY(jso)->c_array, val); +} + +int json_object_array_put_idx(struct json_object *jso, size_t idx, struct json_object *val) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_put_idx(JC_ARRAY(jso)->c_array, idx, val); +} + +int json_object_array_del_idx(struct json_object *jso, size_t idx, size_t count) +{ + assert(json_object_get_type(jso) == json_type_array); + return array_list_del_idx(JC_ARRAY(jso)->c_array, idx, count); +} + +struct json_object *json_object_array_get_idx(const struct json_object *jso, size_t idx) +{ + assert(json_object_get_type(jso) == json_type_array); + return (struct json_object *)array_list_get_idx(JC_ARRAY_C(jso)->c_array, idx); +} + +static int json_array_equal(struct json_object *jso1, struct json_object *jso2) +{ + size_t len, i; + + len = json_object_array_length(jso1); + if (len != json_object_array_length(jso2)) + return 0; + + for (i = 0; i < len; i++) + { + if (!json_object_equal(json_object_array_get_idx(jso1, i), + json_object_array_get_idx(jso2, i))) + return 0; + } + return 1; +} + +int json_object_array_shrink(struct json_object *jso, int empty_slots) +{ + if (empty_slots < 0) + json_abort("json_object_array_shrink called with negative empty_slots"); + return array_list_shrink(JC_ARRAY(jso)->c_array, empty_slots); +} + +struct json_object *json_object_new_null(void) +{ + return NULL; +} + +static int json_object_all_values_equal(struct json_object *jso1, struct json_object *jso2) +{ + struct json_object_iter iter; + struct json_object *sub; + + assert(json_object_get_type(jso1) == json_type_object); + assert(json_object_get_type(jso2) == json_type_object); + /* Iterate over jso1 keys and see if they exist and are equal in jso2 */ + json_object_object_foreachC(jso1, iter) + { + if (!lh_table_lookup_ex(JC_OBJECT(jso2)->c_object, (void *)iter.key, + (void **)(void *)&sub)) + return 0; + if (!json_object_equal(iter.val, sub)) + return 0; + } + + /* Iterate over jso2 keys to see if any exist that are not in jso1 */ + json_object_object_foreachC(jso2, iter) + { + if (!lh_table_lookup_ex(JC_OBJECT(jso1)->c_object, (void *)iter.key, + (void **)(void *)&sub)) + return 0; + } + + return 1; +} + +int json_object_equal(struct json_object *jso1, struct json_object *jso2) +{ + if (jso1 == jso2) + return 1; + + if (!jso1 || !jso2) + return 0; + + if (jso1->o_type != jso2->o_type) + return 0; + + switch (jso1->o_type) + { + case json_type_boolean: return (JC_BOOL(jso1)->c_boolean == JC_BOOL(jso2)->c_boolean); + + case json_type_double: return (JC_DOUBLE(jso1)->c_double == JC_DOUBLE(jso2)->c_double); + + case json_type_int: + { + struct json_object_int *int1 = JC_INT(jso1); + struct json_object_int *int2 = JC_INT(jso2); + if (int1->cint_type == json_object_int_type_int64) + { + if (int2->cint_type == json_object_int_type_int64) + return (int1->cint.c_int64 == int2->cint.c_int64); + if (int1->cint.c_int64 < 0) + return 0; + return ((uint64_t)int1->cint.c_int64 == int2->cint.c_uint64); + } + // else jso1 is a uint64 + if (int2->cint_type == json_object_int_type_uint64) + return (int1->cint.c_uint64 == int2->cint.c_uint64); + if (int2->cint.c_int64 < 0) + return 0; + return (int1->cint.c_uint64 == (uint64_t)int2->cint.c_int64); + } + + case json_type_string: + { + return (_json_object_get_string_len(JC_STRING(jso1)) == + _json_object_get_string_len(JC_STRING(jso2)) && + memcmp(get_string_component(jso1), get_string_component(jso2), + _json_object_get_string_len(JC_STRING(jso1))) == 0); + } + + case json_type_object: return json_object_all_values_equal(jso1, jso2); + + case json_type_array: return json_array_equal(jso1, jso2); + + case json_type_null: return 1; + }; + + return 0; +} + +static int json_object_copy_serializer_data(struct json_object *src, struct json_object *dst) +{ + if (!src->_userdata && !src->_user_delete) + return 0; + + if (dst->_to_json_string == json_object_userdata_to_json_string || + dst->_to_json_string == _json_object_userdata_to_json_string) + { + char *p; + assert(src->_userdata); + p = strdup(src->_userdata); + if (p == NULL) + { + _json_c_set_last_err("json_object_copy_serializer_data: out of memory\n"); + return -1; + } + dst->_userdata = p; + } + // else if ... other supported serializers ... + else + { + _json_c_set_last_err( + "json_object_copy_serializer_data: unable to copy unknown serializer data: " + "%p\n", (void *)dst->_to_json_string); + return -1; + } + dst->_user_delete = src->_user_delete; + return 0; +} + +/** + * The default shallow copy implementation. Simply creates a new object of the same + * type but does *not* copy over _userdata nor retain any custom serializer. + * If custom serializers are in use, json_object_deep_copy() must be passed a shallow copy + * implementation that is aware of how to copy them. + * + * This always returns -1 or 1. It will never return 2 since it does not copy the serializer. + */ +int json_c_shallow_copy_default(json_object *src, json_object *parent, const char *key, + size_t index, json_object **dst) +{ + switch (src->o_type) + { + case json_type_boolean: *dst = json_object_new_boolean(JC_BOOL(src)->c_boolean); break; + + case json_type_double: *dst = json_object_new_double(JC_DOUBLE(src)->c_double); break; + + case json_type_int: + switch (JC_INT(src)->cint_type) + { + case json_object_int_type_int64: + *dst = json_object_new_int64(JC_INT(src)->cint.c_int64); + break; + case json_object_int_type_uint64: + *dst = json_object_new_uint64(JC_INT(src)->cint.c_uint64); + break; + default: json_abort("invalid cint_type"); + } + break; + + case json_type_string: + *dst = json_object_new_string_len(get_string_component(src), + _json_object_get_string_len(JC_STRING(src))); + break; + + case json_type_object: *dst = json_object_new_object(); break; + + case json_type_array: *dst = json_object_new_array(); break; + + default: errno = EINVAL; return -1; + } + + if (!*dst) + { + errno = ENOMEM; + return -1; + } + (*dst)->_to_json_string = src->_to_json_string; + // _userdata and _user_delete are copied later + return 1; +} + +/* + * The actual guts of json_object_deep_copy(), with a few additional args + * needed so we can keep track of where we are within the object tree. + * + * Note: caller is responsible for freeing *dst if this fails and returns -1. + */ +static int json_object_deep_copy_recursive(struct json_object *src, struct json_object *parent, + const char *key_in_parent, size_t index_in_parent, + struct json_object **dst, + json_c_shallow_copy_fn *shallow_copy) +{ + struct json_object_iter iter; + size_t src_array_len, ii; + + int shallow_copy_rc = 0; + shallow_copy_rc = shallow_copy(src, parent, key_in_parent, index_in_parent, dst); + /* -1=error, 1=object created ok, 2=userdata set */ + if (shallow_copy_rc < 1) + { + errno = EINVAL; + return -1; + } + assert(*dst != NULL); + + switch (src->o_type) + { + case json_type_object: + json_object_object_foreachC(src, iter) + { + struct json_object *jso = NULL; + /* This handles the `json_type_null` case */ + if (!iter.val) + jso = NULL; + else if (json_object_deep_copy_recursive(iter.val, src, iter.key, UINT_MAX, + &jso, shallow_copy) < 0) + { + json_object_put(jso); + return -1; + } + + if (json_object_object_add(*dst, iter.key, jso) < 0) + { + json_object_put(jso); + return -1; + } + } + break; + + case json_type_array: + src_array_len = json_object_array_length(src); + for (ii = 0; ii < src_array_len; ii++) + { + struct json_object *jso = NULL; + struct json_object *jso1 = json_object_array_get_idx(src, ii); + /* This handles the `json_type_null` case */ + if (!jso1) + jso = NULL; + else if (json_object_deep_copy_recursive(jso1, src, NULL, ii, &jso, + shallow_copy) < 0) + { + json_object_put(jso); + return -1; + } + + if (json_object_array_add(*dst, jso) < 0) + { + json_object_put(jso); + return -1; + } + } + break; + + default: + break; + /* else, nothing to do, shallow_copy already did. */ + } + + if (shallow_copy_rc != 2) + return json_object_copy_serializer_data(src, *dst); + + return 0; +} + +int json_object_deep_copy(struct json_object *src, struct json_object **dst, + json_c_shallow_copy_fn *shallow_copy) +{ + int rc; + + /* Check if arguments are sane ; *dst must not point to a non-NULL object */ + if (!src || !dst || *dst) + { + errno = EINVAL; + return -1; + } + + if (shallow_copy == NULL) + shallow_copy = json_c_shallow_copy_default; + + rc = json_object_deep_copy_recursive(src, NULL, NULL, UINT_MAX, dst, shallow_copy); + if (rc < 0) + { + json_object_put(*dst); + *dst = NULL; + } + + return rc; +} + +static void json_abort(const char *message) +{ + if (message != NULL) + fprintf(stderr, "json-c aborts with error: %s\n", message); + abort(); +} |