From 74d830dc035db05e2d7c4fecc4aff69c2c6d17e7 Mon Sep 17 00:00:00 2001 From: Keith Derrick Date: Thu, 12 Apr 2012 11:40:44 -0700 Subject: [PATCH 1/4] Add JASSERT macro to guarantee aborts --- debug.h | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/debug.h b/debug.h index 59a4dfc..1e09701 100644 --- a/debug.h +++ b/debug.h @@ -3,6 +3,7 @@ * * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. * Michael Clark + * 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. @@ -12,6 +13,8 @@ #ifndef _DEBUG_H_ #define _DEBUG_H_ +#include + #ifdef __cplusplus extern "C" { #endif @@ -25,21 +28,40 @@ extern void mc_debug(const char *msg, ...); extern void mc_error(const char *msg, ...); extern void mc_info(const char *msg, ...); +#ifndef __STRING +#define __STRING(x) #x +#endif + +#ifndef PARSER_BROKEN_FIXED + +#define JASSERT(cond) do {} while(0) + +#else + +#define JASSERT(cond) do { \ + if (!(cond)) { \ + mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", __FILE__, __LINE__); \ + *(int *)0 = 1;\ + abort(); \ + }\ + } while(0) + +#endif + +#define MC_ABORT(x, ...) mc_abort(x, ##__VA_ARGS__) +#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) + #ifdef MC_MAINTAINER_MODE #define MC_SET_DEBUG(x) mc_set_debug(x) #define MC_GET_DEBUG() mc_get_debug() #define MC_SET_SYSLOG(x) mc_set_syslog(x) -#define MC_ABORT(x, ...) mc_abort(x, ##__VA_ARGS__) #define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) -#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) #define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) #else #define MC_SET_DEBUG(x) if (0) mc_set_debug(x) #define MC_GET_DEBUG() (0) #define MC_SET_SYSLOG(x) if (0) mc_set_syslog(x) -#define MC_ABORT(x, ...) if (0) mc_abort(x, ##__VA_ARGS__) #define MC_DEBUG(x, ...) if (0) mc_debug(x, ##__VA_ARGS__) -#define MC_ERROR(x, ...) if (0) mc_error(x, ##__VA_ARGS__) #define MC_INFO(x, ...) if (0) mc_info(x, ##__VA_ARGS__) #endif From 4a2cd966f5ef267545ec57f2490e3ec14f47d70f Mon Sep 17 00:00:00 2001 From: Keith Derrick Date: Thu, 12 Apr 2012 11:43:34 -0700 Subject: [PATCH 2/4] Add NULL-safe lookup function New lh_table_lookup_ex() method protects itself against null pointers and invalid objects being passed in. --- linkhash.c | 17 ++++++++++++++--- linkhash.h | 12 ++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/linkhash.c b/linkhash.c index 88c0a7c..ddedc12 100644 --- a/linkhash.c +++ b/linkhash.c @@ -3,6 +3,7 @@ * * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. * Michael Clark + * 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. @@ -174,11 +175,21 @@ struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) const void* lh_table_lookup(struct lh_table *t, const void *k) { - struct lh_entry *e = lh_table_lookup_entry(t, k); - if(e) return e->v; - return NULL; + void *result; + lh_table_lookup_ex(t, k, &result); + return result; } +json_bool lh_table_lookup_ex(struct lh_table* t, const void* k, void **v) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if (e != NULL) { + if (v != NULL) *v = (void *)e->v; + return TRUE; /* key found */ + } + if (v != NULL) *v = NULL; + return FALSE; /* key not found */ +} int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) { diff --git a/linkhash.h b/linkhash.h index 9d89460..bbb5488 100644 --- a/linkhash.h +++ b/linkhash.h @@ -3,6 +3,7 @@ * * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. * Michael Clark + * 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. @@ -12,6 +13,8 @@ #ifndef _linkhash_h_ #define _linkhash_h_ +#include "json_object.h" + #ifdef __cplusplus extern "C" { #endif @@ -241,9 +244,18 @@ extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) * @param t the table to lookup * @param k a pointer to the key to lookup * @return a pointer to the found value or NULL if it does not exist. + * @deprecated Use lh_table_lookup_ex instead. */ extern const void* lh_table_lookup(struct lh_table *t, const void *k); +/** + * Lookup a record in the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param v a pointer to a where to store the found value (set to NULL if it doesn't exist). + * @return whether or not the key was found + */ +extern json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v); /** * Delete a record from the table. From 6917586acfb49d976b9a8a441aaef694a9e2d774 Mon Sep 17 00:00:00 2001 From: Keith Derrick Date: Thu, 12 Apr 2012 11:44:13 -0700 Subject: [PATCH 3/4] Add NULL-safe get object method New json_object_object_get_ex() method protects itself against null pointers and invalid objects being passed in. --- json_object.c | 23 +++++++++++++++++++++-- json_object.h | 24 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/json_object.c b/json_object.c index ef54ecd..842ca22 100644 --- a/json_object.c +++ b/json_object.c @@ -3,6 +3,7 @@ * * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. * Michael Clark + * 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. @@ -24,11 +25,13 @@ #include "json_object.h" #include "json_object_private.h" #include "json_util.h" +#include "json_tokener.h" #if !HAVE_STRNDUP char* strndup(const char* str, size_t n); #endif /* !HAVE_STRNDUP */ +// Don't define this. It's not thread-safe. /* #define REFCOUNT_DEBUG 1 */ const char *json_number_chars = "0123456789.+-eE"; @@ -260,8 +263,24 @@ void json_object_object_add(struct json_object* jso, const char *key, struct json_object* json_object_object_get(struct json_object* jso, const char *key) { - if(!jso) return NULL; - return (struct json_object*) lh_table_lookup(jso->o.c_object, key); + struct json_object *result; + json_object_object_get_ex(jso, key, &result); + return result; +} + +json_bool json_object_object_get_ex(struct json_object* jso, const char *key, struct json_object **value) +{ + if (NULL == jso) return FALSE; + + switch(jso->o_type) { + case json_type_object: + return lh_table_lookup_ex(jso->o.c_object, (void*)key, (void**)value); + default: + if (value != NULL) { + *value = NULL; + } + return FALSE; + } } void json_object_object_del(struct json_object* jso, const char *key) diff --git a/json_object.h b/json_object.h index 7b2c4ee..f7ec9ea 100644 --- a/json_object.h +++ b/json_object.h @@ -3,6 +3,7 @@ * * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. * Michael Clark + * 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. @@ -172,10 +173,33 @@ extern void json_object_object_add(struct json_object* obj, const char *key, * @param obj the json_object instance * @param key the object field name * @returns the json_object associated with the given field name + * @deprecated Please use json_object_object_get_ex */ extern struct json_object* json_object_object_get(struct json_object* obj, const char *key); +/** Get the json_object associated with a given object field. + * + * This returns true if the key is found, false in all other cases (including + * if obj isn't a json_type_object). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of value is retained by obj. + * + * @param obj the json_object instance + * @param key the object field name + * @param value a pointer where to store a reference to the json_object + * associated with the given field name. + * + * It is safe to pass a NULL value. + * @returns whether or not the key exists + */ +extern json_bool json_object_object_get_ex(struct json_object* obj, + const char *key, + struct json_object **value); + /** Delete the given json_object field * * The reference count will be decremented for the deleted object. If there From bcfd1f57ac3aeb6b2ce7af89c3130e617ab6e5c0 Mon Sep 17 00:00:00 2001 From: Keith Derrick Date: Thu, 12 Apr 2012 09:54:21 -0700 Subject: [PATCH 4/4] Add alternative iterator implementation --- json.h | 2 + json_object_iterator.c | 169 +++++++++++++++++++++++++++ json_object_iterator.h | 254 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 425 insertions(+) create mode 100644 json_object_iterator.c create mode 100644 json_object_iterator.h diff --git a/json.h b/json.h index a5a3432..d49715b 100644 --- a/json.h +++ b/json.h @@ -3,6 +3,7 @@ * * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. * Michael Clark + * 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. @@ -23,6 +24,7 @@ extern "C" { #include "json_util.h" #include "json_object.h" #include "json_tokener.h" +#include "json_object_iterator.h" #ifdef __cplusplus } diff --git a/json_object_iterator.c b/json_object_iterator.c new file mode 100644 index 0000000..b887eb2 --- /dev/null +++ b/json_object_iterator.c @@ -0,0 +1,169 @@ +/** +******************************************************************************* +* @file cjson_object_iterator.c +* +* 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. +* +* @brief cjson forces clients to use its private data +* structures for JSON Object iteration. This API +* implementation corrects that by abstracting the +* private cjson details. +* +******************************************************************************* +*/ + +#include +#include + +#include "json.h" +#include "json_object_private.h" + +#include "json_object_iterator.h" + +/** + * How It Works + * + * For each JSON Object, cjson maintains a linked list of zero + * or more lh_entry (link-hash entry) structures inside the + * Object's link-hash table (lh_table). + * + * Each lh_entry structure on the JSON Object's linked list + * represents a single name/value pair. The "next" field of the + * last lh_entry in the list is set to NULL, which terminates + * the list. + * + * We represent a valid iterator that refers to an actual + * name/value pair via a pointer to the pair's lh_entry + * structure set as the iterator's opaque_ field. + * + * We follow cjson's current pair list representation by + * representing a valid "end" iterator (one that refers past the + * last pair) with a NULL value in the iterator's opaque_ field. + * + * A JSON Object without any pairs in it will have the "head" + * field of its lh_table structure set to NULL. For such an + * object, json_object_iter_begin will return an iterator with + * the opaque_ field set to NULL, which is equivalent to the + * "end" iterator. + * + * When iterating, we simply update the iterator's opaque_ field + * to point to the next lh_entry structure in the linked list. + * opaque_ will become NULL once we iterate past the last pair + * in the list, which makes the iterator equivalent to the "end" + * iterator. + */ + +/// Our current representation of the "end" iterator; +/// +/// @note May not always be NULL +static const void* kObjectEndIterValue = NULL; + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj) +{ + struct json_object_iterator iter; + struct lh_table* pTable; + + /// @note json_object_get_object will return NULL if passed NULL + /// or a non-json_type_object instance + pTable = json_object_get_object(obj); + JASSERT(NULL != pTable); + + /// @note For a pair-less Object, head is NULL, which matches our + /// definition of the "end" iterator + iter.opaque_ = pTable->head; + return iter; +} + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj) +{ + struct json_object_iterator iter; + + JASSERT(NULL != obj); + JASSERT(json_object_is_type(obj, json_type_object)); + + iter.opaque_ = kObjectEndIterValue; + + return iter; +} + +/** + * **************************************************************************** + */ +void +json_object_iter_next(struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + iter->opaque_ = ((struct lh_entry *)iter->opaque_)->next; +} + + +/** + * **************************************************************************** + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (const char*)(((struct lh_entry *)iter->opaque_)->k); +} + + +/** + * **************************************************************************** + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (struct json_object*)(((struct lh_entry *)iter->opaque_)->v); +} + + +/** + * **************************************************************************** + */ +bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2) +{ + JASSERT(NULL != iter1); + JASSERT(NULL != iter2); + + return (iter1->opaque_ == iter2->opaque_); +} + + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_init_default(void) +{ + struct json_object_iterator iter; + + /** + * @note Make this a negative, invalid value, such that + * accidental access to it would likely be trapped by the + * hardware as an invalid address. + */ + iter.opaque_ = NULL; + + return iter; +} diff --git a/json_object_iterator.h b/json_object_iterator.h new file mode 100644 index 0000000..21feaf5 --- /dev/null +++ b/json_object_iterator.h @@ -0,0 +1,254 @@ +/** +******************************************************************************* +* @file json_object_iterator.h +* +* 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. +* +* @brief cjson forces clients to use its private data +* structures for JSON Object iteration. This API +* corrects that by abstracting the private cjson +* details. +* +* The intention is to add this API (and its +* implementation) to Palm's version of the cjson +* library, at which point it can be removed from the +* Wireless System Framework library implementation. +* +* API attributes: +* * Thread-safe: NO +* * Re-entrant: NO +* +******************************************************************************* +*/ + + +#ifndef JSON_OBJECT_ITERATOR_H +#define JSON_OBJECT_ITERATOR_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Forward declaration for the opaque iterator information. + */ +struct json_object_iter_info_; + +/** + * The opaque iterator that references a name/value pair within + * a JSON Object intance or the "end" iterator value. + */ +struct json_object_iterator { + const void* opaque_; +}; + + +/** + * forward declaration of cjson's JSON value instance structure + */ +struct json_object; + + +/** + * Initializes an iterator structure to a "default" value that + * is convenient for initializing an iterator variable to a + * default state (e.g., initialization list in a class' + * constructor). + * + * @code + * struct json_object_iterator iter = json_object_iter_init_default(); + * MyClass() : iter_(json_object_iter_init_default()) + * @endcode + * + * @note The initialized value doesn't reference any specific + * pair, is considered an invalid iterator, and MUST NOT + * be passed to any cjson API that expects a valid + * iterator. + * + * @note User and internal code MUST NOT make any assumptions + * about and dependencies on the value of the "default" + * iterator value. + * + * @return json_object_iterator + */ +struct json_object_iterator +json_object_iter_init_default(void); + +/** Retrieves an iterator to the first pair of the JSON Object. + * + * @note WARNING: Any modification of the underlying pair + * invalidates all iterators to that pair. + * + * @param obj JSON Object instance (MUST be of type + * json_type_object) + * + * @return json_object_iterator If the JSON Object has at + * least one pair, on return, the iterator refers + * to the first pair. If the JSON Object doesn't + * have any pairs, the returned iterator is + * equivalent to the "end" iterator for the same + * JSON Object instance. + * + * @code + * struct json_object_iterator it; + * struct json_object_iterator itEnd; + * struct json_object* obj = json_tokener_parse( + * "{'first':'george', 'age':100}"); + * json_object_iter_begin(obj, &it); + * json_object_iter_end(obj, &itEnd); + * while (!json_object_iter_equal(&it, &itEnd)) { + * printf("%s\n", + * json_object_iter_peek_name(&it)); + * json_object_iter_next(&it); + * } + * + * struct json_object* obj = json_tokener_parse( + * "{'first':'george', 'age':100}"); + * struct json_object_iterator it; + * bool iterable = json_object_iter_begin(&it); + * if (iterable) { + * do { + * printf("%s\n", json_object_iter_peek_name(&it)); + * } while (json_object_iter_next(&it)); + * } + * @endcode + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj); + +/** Retrieves the iterator that represents the position beyond the + * last pair of the given JSON Object instance. + * + * @note WARNING: Do NOT write code that assumes that the "end" + * iterator value is NULL, even if it is so in a + * particular instance of the implementation. + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The "end" iterator and the + * equality test method, on the other hand, permit us to + * cleanly abstract pretty much any reasonable underlying + * representation without burdening the iterator + * structure with unnecessary data. + * + * @note For performance reasons, memoize the "end" iterator prior + * to any loop. + * + * @param obj JSON Object instance (MUST be of type + * json_type_object) + * + * @return json_object_iterator On return, the iterator refers + * to the "end" of the Object instance's pairs + * (i.e., NOT the last pair, but "beyond the last + * pair" value) + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj); + +/** Returns an iterator to the next pair, if any + * + * @note WARNING: Any modification of the underlying pair + * invalidates all iterators to that pair. + * + * @param iter Iterator that references a name/value pair; + * + * @param iter [IN/OUT] Pointer to iterator that references a + * name/value pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or "end" + * iterator is passed. Upon return will contain the + * reference to the next pair if there is one; if there + * are no more pairs, will contain the "end" iterator + * value, which may be compared against the return value + * of json_object_iter_end() for the same JSON Object + * instance. + */ +void +json_object_iter_next(struct json_object_iterator* iter); + + +/** Returns a const pointer to the name of the pair referenced + * by the given iterator. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or + * "end" iterator is passed. + * + * @return const char* Pointer to the name of the rerferenced + * name/value pair. The name memory belongs to the + * name/value pair, will be freed when the pair is + * deleted or modified, and MUST NOT be modified or + * freed by the user. + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter); + + +/** Returns a pointer to the cjson instance representing the + * value of the referenced name/value pair, without altering + * the instance's reference count. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or + * "end" iterator is passed. + * + * @return struct json_object* Pointer to the cjson value + * instance of the referenced name/value pair; the + * value's reference count is not changed by this + * function: if you plan to hold on to this cjson node, + * take a look at json_object_get() and + * json_object_put(). IMPORTANT: cjson API represents + * the JSON Null value as a NULL json_object instance + * pointer. + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter); + + +/** Tests two iterators for equality. Typically used to test + * for end of iteration by comparing an iterator to the + * corresponding "end" iterator (that was derived from the same + * JSON Object instance). + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The equality test method, on + * the other hand, permits us to cleanly abstract pretty + * much any reasonable underlying representation. + * + * @param iter1 Pointer to first valid, non-NULL iterator + * @param iter2 POinter to second valid, non-NULL iterator + * + * @note WARNING: if a NULL iterator pointer or an uninitialized + * or invalid iterator, or iterators derived from + * different JSON Object instances are passed, bad things + * will happen! + * + * @return bool non-zero if iterators are equal (i.e., both + * reference the same name/value pair or are both at + * "end"); zero if they are not equal. + */ +bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2); + + +#ifdef __cplusplus +} +#endif + + +#endif // JSON_OBJECT_ITERATOR_H