[Opensrf-commits] r1794 - in trunk: include/opensrf src/libopensrf (scottmk)

svn at svn.open-ils.org svn at svn.open-ils.org
Fri Sep 25 10:35:21 EDT 2009


Author: scottmk
Date: 2009-09-25 10:35:18 -0400 (Fri, 25 Sep 2009)
New Revision: 1794

Modified:
   trunk/include/opensrf/osrf_json.h
   trunk/src/libopensrf/osrf_json_object.c
Log:
1. Add doxygen markup for documentation.

2. In jsonNewObjectType(): explicitly initialize a JSON_BOOL to false, instead
of implicitly relying on the expectation that a NULL pointer is represented
by all-bits-zero.

3. In jsonObjectExtractIndex(): set the parent pointer to NULL in the
extracted jsonObject.

M    include/opensrf/osrf_json.h
M    src/libopensrf/osrf_json_object.c


Modified: trunk/include/opensrf/osrf_json.h
===================================================================
--- trunk/include/opensrf/osrf_json.h	2009-09-18 12:57:58 UTC (rev 1793)
+++ trunk/include/opensrf/osrf_json.h	2009-09-25 14:35:18 UTC (rev 1794)
@@ -13,6 +13,38 @@
 GNU General Public License for more details.
 */
 
+/**
+	@file osrf_json.h
+	@brief Header for parsing JSON structures and representing them in memory.
+
+	JSON is a format for representing hierarchical data structures as text.
+
+	A JSON string may be a quoted string, a numeric literal, or any of the keywords true, false
+	and null.
+
+	A JSON string may also be an array, i.e. a series of values, separated by commas and enclosed
+	in square brackets.  For example: [ "Adams", 42, null, true ]
+
+	A JSON string may also be an object, i.e. a series of name/value pairs, separated by commas
+	and enclosed by curly braces.  Each name/value pair is a quoted string, followed by a colon,
+	followed by a value.  For example: { "Author":"Adams", "answer":42, "question":null,
+	"profound": true }
+
+	The values in a JSON array or object may themselves be arrays or objects, nested to any
+	depth, in a hierarchical structure of arbitrary complexity.  For more information about
+	JSON, see http://json.org/.
+
+	Like a JSON string, a jsonObject can take several different forms.  It can hold a string, a
+	number, a boolean, or a null.  It can also hold an array, implemented internally by an
+	osrfList (see osrf_list.h).  It can also hold a series of name/value pairs, implemented
+	internally by an osrfHash (see osrf_hash.h).
+
+	A jsonObject can also tag its contents with a class name, typically referring to a
+	database table or view.  Such an object can be translated into a JSON string where the
+	class is encoded as the value of a name/value pair, with the original jsonObject encoded
+	as the value of a second name/value pair.
+*/
+
 #ifndef JSON_H
 #define JSON_H
 
@@ -25,6 +57,14 @@
 #endif
 
 /* parser states */
+/**
+	@name Parser states
+	@brief Used internally by a JSON parser.
+
+	A jsonParserContext stores these values in order to remember where the parser is in the
+	parsing.
+*/
+/*@{*/
 #define JSON_STATE_IN_OBJECT	0x1
 #define JSON_STATE_IN_ARRAY		0x2
 #define JSON_STATE_IN_STRING	0x4
@@ -40,53 +80,107 @@
 #define JSON_STATE_START_COMMEN	0x1000
 #define JSON_STATE_IN_COMMENT	0x2000
 #define JSON_STATE_END_COMMENT	0x4000
+/*@}*/
 
+/**
+	@name Parser state operations
+	@ Macros to manipulate the parser state in a jsonParserContext.
+*/
+/*@{*/
+/** Set a state. */
+#define JSON_STATE_SET(ctx,s) ctx->state |= s; 
+/** Unset a state. */
+#define JSON_STATE_REMOVE(ctx,s) ctx->state &= ~s;
+/** Check if a state is set. */
+#define JSON_STATE_CHECK(ctx,s) (ctx->state & s) ? 1 : 0
+/** Pop a state off the stack. */
+#define JSON_STATE_POP(ctx) osrfListPop( ctx->stateStack );
+/** Push a state on the stack. */
+#define JSON_STATE_PUSH(ctx, state) osrfListPush( ctx->stateStack,(void*) state );
+/** Check which container type we're currently in. */
+#define JSON_STATE_PEEK(ctx) osrfListGetIndex(ctx->stateStack, ctx->stateStack->size -1)
+/** Compare stack values. */
+#define JSON_STATE_CHECK_STACK(ctx, s) (JSON_STATE_PEEK(ctx) == (void*) s ) ? 1 : 0
+/** Check if a parser state is set. */
+#define JSON_PARSE_FLAG_CHECK(ctx, f) (ctx->flags & f) ? 1 : 0
+/*@}*/
 
-/* object and array (container) states are pushed onto a stack so we
- * can keep track of the object nest.  All other states are
- * simply stored in the state field of the parser */
-#define JSON_STATE_SET(ctx,s) ctx->state |= s; /* set a state */
-#define JSON_STATE_REMOVE(ctx,s) ctx->state &= ~s; /* unset a state */
-#define JSON_STATE_CHECK(ctx,s) (ctx->state & s) ? 1 : 0 /* check if a state is set */
-#define JSON_STATE_POP(ctx) osrfListPop( ctx->stateStack ); /* remove a state from the stack */
-#define JSON_STATE_PUSH(ctx, state) osrfListPush( ctx->stateStack,(void*) state );/* push a state on the stack */
-#define JSON_STATE_PEEK(ctx) osrfListGetIndex(ctx->stateStack, ctx->stateStack->size -1) /* check which container type we're currently in */
-#define JSON_STATE_CHECK_STACK(ctx, s) (JSON_STATE_PEEK(ctx) == (void*) s ) ? 1 : 0  /* compare stack values */
+/**
+	@name JSON types
+	@brief Macros defining types of jsonObject.
 
-/* JSON types */
+	A jsonObject includes a @em type member with one of these values, identifying the type of
+	jsonObject.  Client code should treat the @em type member as read-only.
+*/
+/*@{*/
 #define JSON_HASH 	0
 #define JSON_ARRAY	1
 #define JSON_STRING	2
 #define JSON_NUMBER	3
-#define JSON_NULL 	4	
+#define JSON_NULL 	4
 #define JSON_BOOL 	5
+/*@}*/
 
+/**
+	This macro is used only by a JSON parser.  It probably has no business being published
+	in a header.
+*/
 #define JSON_PARSE_LAST_CHUNK 0x1 /* this is the last part of the string we're parsing */
 
-#define JSON_PARSE_FLAG_CHECK(ctx, f) (ctx->flags & f) ? 1 : 0 /* check if a parser state is set */
+/**
+	@name JSON extensions
 
+	These two macros define tags used for encoding class information.  @em JSON_CLASS_KEY
+	labels a class name, and @em JSON_DATA_KEY labels an associated value.
+
+	Because each of these macros is subject to an ifndef clause, client code may override
+	the default choice of labels by defining alternatives before including this header.
+	At this writing, this potential for customization is unused.
+*/
+/*@{*/
 #ifndef JSON_CLASS_KEY
 #define JSON_CLASS_KEY "__c"
 #endif
 #ifndef JSON_DATA_KEY
 #define JSON_DATA_KEY "__p"
 #endif
+/*@}*/
 
+/**
+	@brief Stores the current state of a JSON parser.
 
+	One form of JSON parser operates as a finite state machine.  It stores the various
+	JSON_STATE_* values in order to keep track of what it's doing.  It also maintains a
+	stack of previous states in order to keep track of nesting.
+
+	The internals of this struct are published in the header in order to provide the client
+	with a window into the operations of the parser.  By installing its own callback functions,
+	and possibly by tinkering with the insides of the jsonParserContext, the client code can
+	customize the behavior of the parser.
+
+	In practice only the default callbacks are ever installed, at this writing.  The potential
+	for customized parsing is unused.
+*/
 struct jsonParserContextStruct {
-	int state;						/* what are we currently parsing */
-	const char* chunk;				/* the chunk we're currently parsing */
-	int index;						/* where we are in parsing the current chunk */
-	int chunksize;					/* the size of the current chunk */
-	int flags;						/* parser flags */
-	osrfList* stateStack;		/* represents the nest of object/array states */
-	growing_buffer* buffer;		/* used to hold JSON strings, number, true, false, and null sequences */
-	growing_buffer* utfbuf;		/* holds the current unicode characters */
-	void* userData;				/* opaque user pointer.  we ignore this */
-	const struct jsonParserHandlerStruct* handler; /* the event handler struct */
+	int state;                  /**< What are we currently parsing. */
+	const char* chunk;          /**< The chunk we're currently parsing. */
+	int index;                  /**< Where we are in parsing the current chunk. */
+	int chunksize;              /**< Size of the current chunk. */
+	int flags;                  /**< Parser flags. */
+	osrfList* stateStack;       /**< The nest of object/array states. */
+	growing_buffer* buffer;     /**< Buffer for building strings, numbers, and keywords. */
+	growing_buffer* utfbuf;     /**< Holds the current unicode characters. */
+	void* userData;             /**< Opaque user pointer.  We ignore this. */
+	const struct jsonParserHandlerStruct* handler; /**< The event handler struct. */
 };
 typedef struct jsonParserContextStruct jsonParserContext;
 
+/**
+	@brief A collection of function pointers for customizing parser behavior.
+
+	The client code can install pointers to its own functions in this struct, in order to
+	customize the behavior of the parser at various points in the parsing.
+*/
 struct jsonParserHandlerStruct {
 	void (*handleStartObject)	(void* userData);
 	void (*handleObjectKey)		(void* userData, char* key);
@@ -94,234 +188,194 @@
 	void (*handleStartArray)	(void* userData);
 	void (*handleEndArray)		(void* userData);
 	void (*handleNull)			(void* userData);
-	void (*handleString)			(void* userData, char* string);
+	void (*handleString)		(void* userData, char* string);
 	void (*handleBool)			(void* userData, int boolval);
 	void (*handleNumber)		(void* userData, const char* numstr);
 	void (*handleError)			(void* userData, char* err, ...);
 };
 typedef struct jsonParserHandlerStruct jsonParserHandler;
 
+/**
+	@brief Representation of a JSON string in memory
+
+	Different types of jsonObject use different members of the @em value union.
+
+	Numbers are stored as character strings, using the same buffer that we use for
+	strings.  As a result, a JSON_NUMBER can store numbers that cannot be represented
+	by native C types.
+
+	(We used to store numbers as doubles.  We still have the @em n member lying around as
+	a relic of those times, but we don't use it.  We can't get rid of it yet, either.  Long
+	story.)
+*/
 struct _jsonObjectStruct {
-	unsigned long size;	/* number of sub-items */
-	char* classname;		/* optional class hint (not part of the JSON spec) */
-	int type;				/* JSON type */
-	struct _jsonObjectStruct* parent;	/* who we're attached to */
-	union __jsonValue {	/* cargo */
-		osrfHash*	h;		/* object container */
-		osrfList*	l;		/* array container */
-		char* 		s;		/* string */
-		int 			b;		/* bool */
-//		double	n;		/* number */
-		double	n;		/* number */
+	unsigned long size;     /**< Number of sub-items. */
+	char* classname;        /**< Optional class hint (not part of the JSON spec). */
+	int type;               /**< JSON type. */
+	struct _jsonObjectStruct* parent;   /**< Whom we're attached to. */
+	/** Union used for various types of cargo. */
+	union _jsonValue {
+		osrfHash*	h;      /**< Object container. */
+		osrfList*	l;      /**< Array container. */
+		char* 		s;      /**< String or number. */
+		int 		b;      /**< Bool. */
+		double	n;          /**< Number (no longer used). */
 	} value;
 };
 typedef struct _jsonObjectStruct jsonObject;
 
+/**
+	@brief Iterator for traversing a jsonObject.
+
+	A jsonIterator traverses a jsonIterator only at a single level.  It does @em not descend
+	into lower levels to traverse them recursively.
+*/
 struct _jsonIteratorStruct {
-	jsonObject* obj; /* the object we're traversing */
-	osrfHashIterator* hashItr; /* the iterator for this hash */
-	const char* key; /* if this object is a hash, the current key */
-	unsigned long index; /* if this object is an array, the index */
+	jsonObject* obj;           /**< The object we're traversing. */
+	osrfHashIterator* hashItr; /**< The iterator for this hash. */
+	const char* key;           /**< If this object is a hash, the current key. */
+	unsigned long index;       /**< If this object is an array, the index. */
 };
 typedef struct _jsonIteratorStruct jsonIterator;
 
 
 
 /** 
- * Allocates a new parser context object
- * @param handler The event handler struct
- * @param userData Opaque user pointer which is available in callbacks
- * and ignored by the parser
- * @return An allocated parser context, NULL on error
- */
+	@brief Allocate a new parser context object.
+	@param handler Pointer to a collection of function pointers for callback functions.
+	@param userData Opaque user pointer which is available to callbacks; ignored by the parser.
+	@return An allocated parser context, or NULL on error.
+*/
 jsonParserContext* jsonNewParser( const jsonParserHandler* handler, void* userData);
 
 /**
- * Deallocates a parser context
- * @param ctx The context object
- */
+	@brief Free a jsonParserContext.
+	@param ctx Pointer to the jsonParserContext to be freed.
+*/
 void jsonParserFree( jsonParserContext* ctx );
 
 /**
- * Parse a chunk of data.
- * @param ctx The parser context
- * @param data The data to parse
- * @param datalen The size of the chunk to parser
- * @param flags Reserved
- */
+	@brief Parse a chunk of data.
+	@param ctx Pointer to the parser context.
+	@param data Pointer the data to parse.
+	@param datalen The size of the chunk to parse.
+	@param flags Reserved.
+*/
 int jsonParseChunk( jsonParserContext* ctx, const char* data, int datalen, int flags );
 
+/**
+	@name Parsing functions
+	
+	There are two sets of parsing functions, which are mostly plug-compatible with each other.
+	The older series:
 
+	- jsonParseString()
+	- jsonParseStringRaw()
+	- jsonParseStringFmt()
+
+	...and a newer series:
+
+	- jsonParseString();
+	- jsonParseStringRaw();
+	- jsonParseStringFmt();
+
+	The first series is based on a finite state machine.  Its innards are accessible, in
+	theory, through the jsonParserContext structure and through callback functions.  In
+	practice this flexibility is unused at this writing.
+
+	The second series is based on recursive descent.  It doesn't use the jsonParserContext
+	structure, nor does it accept callback functions.  However it is faster than the first
+	parser.  In addition its syntax checking is much stricter -- it catches many kinds of
+	syntax errors that slip through the first parser.
+*/
+/*@{*/
 /**
- * Parses a JSON string;
- * @param str The string to parser
- * @return The resulting JSON object or NULL on error
- */
+	@brief Parse a JSON string;
+	@param str Pointer to the JSON string to parse.
+	@return The resulting JSON object, or NULL on error.
+*/
 jsonObject* jsonParseString( const char* str );
 jsonObject* jsonParseStringRaw( const char* str );
-
 jsonObject* jsonParseStringFmt( const char* str, ... );
 
 /**
- * Similar to jsonParseString(), jsonParseStringRaw() and
- * jsonParseStringFmt(), but with stricter and more robust
- * syntax validation
+	@brief Parse a JSON string;
+	@param s Pointer to the JSON string to parse.
+	@return The resulting JSON object, or NULL on error.
  */
 jsonObject* jsonParse( const char* s );
 jsonObject* jsonParseRaw( const char* s );
 jsonObject* jsonParseFmt( const char* str, ... );
+/*@}*/
 
-
 /**
- * Parses a JSON string;
- * @param str The string to parser
- * @return The resulting JSON object or NULL on error
+	@brief Parses a JSON string, using a customized error handler.
+	@param errorHandler A function pointer to an error-handling function.
+	@param str The string to parse.
+	@return The resulting JSON object, or NULL on error.
  */
 jsonObject* jsonParseStringHandleError( void (*errorHandler) (const char*), char* str, ... );
 
+jsonObject* jsonNewObject(const char* data);
 
-
-/**
- * Creates a new json object
- * @param data The string data this object will hold if 
- * this object happens to be a JSON_STRING, NULL otherwise
- * @return The allocated json object.  Must be freed with 
- * jsonObjectFree()
- */
-jsonObject* jsonNewObject(const char* data);
 jsonObject* jsonNewObjectFmt(const char* data, ...);
 
-/**
- * Creates a new object of the given type
- */
 jsonObject* jsonNewObjectType(int type);
 
-/**
- * Creates a new number object from a double
- */
 jsonObject* jsonNewNumberObject( double num );
 
-/**
- * Creates a new number object from a numeric string
- */
 jsonObject* jsonNewNumberStringObject( const char* numstr );
 
-/**
- * Creates a new json bool
- */
 jsonObject* jsonNewBoolObject(int val);
 
-/**
- * Deallocates an object
- */
 void jsonObjectFree( jsonObject* o );
 
-/**
- * Returns all unused jsonObjects to the heap
- */
 void jsonObjectFreeUnused( void );
 
-/**
- * Forces the given object to become an array (if it isn't already one) 
- * and pushes the new object into the array
- */
 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo);
 
-/**
- * Forces the given object to become a hash (if it isn't already one)
- * and assigns the new object to the key of the hash
- */
 unsigned long jsonObjectSetKey(
 		jsonObject* o, const char* key, jsonObject* newo);
 
-
-/**
+/*
  * Turns the object into a JSON string.  The string must be freed by the caller */
 char* jsonObjectToJSON( const jsonObject* obj );
 char* jsonObjectToJSONRaw( const jsonObject* obj );
 
+jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key );
 
-/**
- * Retrieves the object at the given key
- */
-jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key );
 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key );
 
-
-
-
-
-/** Allocates a new iterator 
-	@param obj The object over which to iterate.
-*/
 jsonIterator* jsonNewIterator(const jsonObject* obj);
 
+void jsonIteratorFree(jsonIterator* itr);
 
-/** 
-	De-allocates an iterator 
-	@param iter The iterator object to free
-*/
-void jsonIteratorFree(jsonIterator* iter);
-
-/** 
-	Returns the object_node currently pointed to by the iterator
-  	and increments the pointer to the next node
-	@param iter The iterator in question.
- */
 jsonObject* jsonIteratorNext(jsonIterator* iter);
 
+int jsonIteratorHasNext(const jsonIterator* itr);
 
-/** 
-	@param iter The iterator.
-	@return True if there is another node after the current node.
- */
-int jsonIteratorHasNext(const jsonIterator* iter);
-
-
-/** 
-	Returns a pointer to the object at the given index.  This call is
-	only valid if the object has a type of JSON_ARRAY.
-	@param obj The object
-	@param index The position within the object
-	@return The object at the given index.
-*/
 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index );
 
-
-/* removes (and deallocates) the object at the given index (if one exists) and inserts 
- * the new one.  returns the size on success, -1 on error 
- * If obj is NULL, inserts a new object into the list with is_null set to true
- */
 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj);
 
-/* removes and deallocates the object at the given index, replacing
-   it with a NULL pointer
- */
 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index);
 
-/* removes (but does not deallocate) the object at the given index,
- * replacing it with a NULL pointer; returns a pointer to the object removed
- */
 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index);
 
-/* removes (and deallocates) the object with key 'key' if it exists */
 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key);
 
-/* returns a pointer to the string data held by this object if this object
-	is a string.  Otherwise returns NULL*/
 char* jsonObjectGetString(const jsonObject*);
 
 double jsonObjectGetNumber( const jsonObject* obj );
 
-/* sets the string data */
 void jsonObjectSetString(jsonObject* dest, const char* string);
 
-/* sets the number value for the object */
 void jsonObjectSetNumber(jsonObject* dest, double num);
+
 int jsonObjectSetNumberString(jsonObject* dest, const char* string);
 
-/* sets the class hint for this object */
 void jsonObjectSetClass(jsonObject* dest, const char* classname );
+
 const char* jsonObjectGetClass(const jsonObject* dest);
 
 int jsonBoolIsTrue( const jsonObject* boolObj );
@@ -330,79 +384,68 @@
 
 jsonObject* jsonObjectClone( const jsonObject* o );
 
-
-/* tries to extract the string data from an object.
-	if object	-> NULL (the C NULL)
-	if array		->	NULL  
-	if null		-> NULL 
-	if bool		-> NULL
-	if string/number the string version of either of those
-	The caller is responsible for freeing the returned string
-	*/
 char* jsonObjectToSimpleString( const jsonObject* o );
 
-/**
- Allocate a buffer and format a specified numeric value into it,
- with up to 30 decimal digits of precision.   Caller is responsible
- for freeing the buffer.
- **/
 char* doubleToString( double num );
 
-/**
- Return 1 if the string is numeric, otherwise return 0.
- This validation follows the rules defined by the grammar at:
- http://www.json.org/
- **/
 int jsonIsNumeric( const char* s );
 
-/**
- Allocate and reformat a numeric string into one that is valid
- by JSON rules.  If the string is not numeric, return NULL.
- Caller is responsible for freeing the buffer.
- **/
 char* jsonScrubNumber( const char* s );
 
-/* provides an XPATH style search interface (e.g. /some/node/here) and 
-	return the object at that location if one exists.  Naturally,  
-	every element in the path must be a proper object ("hash" / {}).
-	Returns NULL if the specified node is not found 
-	Note also that the object returned is a clone and
-	must be freed by the caller
+/**
+	@brief Provide an XPATH style search interface to jsonObjects.
+	@param obj Pointer to the jsonObject to be searched.
+	@param path Pointer to a printf-style format string specifying the search path.  Subsequent
+		parameters, if any, are formatted and inserted into the formatted string.
+	@return A copy of the object at the specified location, if it exists, or NULL if it doesn't.
+
+	Example search path: /some/node/here.
+	
+	Every element in the path must be a proper object, a JSON_HASH.
+
+	The calling code is responsible for freeing the jsonObject to which the returned pointer
+	points.
 */
 jsonObject* jsonObjectFindPath( const jsonObject* obj, const char* path, ... );
 
 
-/* formats a JSON string from printing.  User must free returned string */
+/**
+	@brief Prettify a JSON string for printing, by adding newlines and other white space.
+	@param jsonString Pointer to the original JSON string.
+	@return Pointer to a prettified JSON string.
+
+	The calling code is responsible for freeing the returned string.
+*/
 char* jsonFormatString( const char* jsonString );
 
 /* sets the error handler for all parsers */
 void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*));
 
 /* ------------------------------------------------------------------------- */
-/**
+/*
  * The following methods provide a facility for serializing and
  * deserializing "classed" JSON objects.  To give a JSON object a 
- * class, simply call jsonObjectSetClass().  
+ * class, simply call jsonObjectSetClass().
  * Then, calling jsonObjectEncodeClass() will convert the JSON
- * object (and any sub-objects) to a JSON object with class 
+ * object (and any sub-objects) to a JSON object with class
  * wrapper objects like so:
- * { _c : "classname", _d : <json_thing> }
- * In this example _c is the class key and _d is the data (object)
+ * { "__c" : "classname", "__p" : [json_thing] }
+ * In this example __c is the class key and __p is the data (object)
  * key.  The keys are defined by the constants 
- * OSRF_JSON_CLASS_KEY and OSRF_JSON_DATA_KEY
+ * JSON_CLASS_KEY and JSON_DATA_KEY
  * To revive a serialized object, simply call
  * jsonObjectDecodeClass()
  */
 
 
-/** Converts a class-wrapped object into an object with the
+/* Converts a class-wrapped object into an object with the
  * classname set
  * Caller must free the returned object 
  */ 
 jsonObject* jsonObjectDecodeClass( const jsonObject* obj );
 
 
-/** Converts an object with a classname into a
+/* Converts an object with a classname into a
  * class-wrapped (serialized) object
  * Caller must free the returned object 
  */ 
@@ -411,11 +454,10 @@
 /* ------------------------------------------------------------------------- */
 
 
-/**
+/*
  *	Generates an XML representation of a JSON object */
 char* jsonObjectToXML(const jsonObject*);
 
-
 /*
  * Builds a JSON object from the provided XML 
  */

Modified: trunk/src/libopensrf/osrf_json_object.c
===================================================================
--- trunk/src/libopensrf/osrf_json_object.c	2009-09-18 12:57:58 UTC (rev 1793)
+++ trunk/src/libopensrf/osrf_json_object.c	2009-09-25 14:35:18 UTC (rev 1794)
@@ -13,6 +13,17 @@
 GNU General Public License for more details.
 */
 
+/**
+	@file osrf_json_object.c
+	@brief Implementation of the basic operations involving jsonObjects.
+
+	As a performance tweak: we maintain a free list of jsonObjects that have already
+	been allocated but are not currently in use.  When we need to create a jsonObject,
+	we can take one from the free list, if one is available, instead of calling
+	malloc().  Likewise when we free a jsonObject, we can stick it on the free list
+	for potential reuse instead of calling free().
+*/
+
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
@@ -24,6 +35,19 @@
 
 /* cleans up an object if it is morphing another object, also
  * verifies that the appropriate storage container exists where appropriate */
+/**
+	@brief Coerce a jsonObj into a specified type, if it isn't already of that type.
+	@param _obj_ Pointer to the jsonObject to be coerced.
+	@param newtype The desired type.
+
+	If the old type and the new type don't match, discard and free the old contents.
+
+	If the old type is JSON_STRING or JSON_NUMBER, free the internal string buffer even
+	if the type is not changing.
+
+	If the new type is JSON_ARRAY or JSON_HASH, make sure there is an osrfList or osrfHash
+	in the jsonObject, respectively.
+*/
 #define JSON_INIT_CLEAR(_obj_, newtype)		\
 	if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) {			\
 		osrfHashFree(_obj_->value.h);			\
@@ -46,11 +70,19 @@
 		_obj_->value.l->freeItem = _jsonFreeListItem;\
 	}
 
+/** Count of the times we put a freed jsonObject on the free list instead of calling free() */
 static int unusedObjCapture = 0;
+/** Count of the times we reused a jsonObject from the free list instead of calling malloc() */
 static int unusedObjRelease = 0;
+/** Count of the times we allocated a jsonObject with malloc() */
 static int mallocObjCreate = 0;
+/** Number of unused jsonObjects currently on the free list */
 static int currentListLen = 0;
 
+/**
+	Union overlaying a jsonObject with a pointer.  When the jsonObject is not in use as a
+	jsonObject, we use the overlaid pointer to maintain a linked list of unused jsonObjects.
+*/
 union unusedObjUnion{
 
 	union unusedObjUnion* next;
@@ -58,19 +90,20 @@
 };
 typedef union unusedObjUnion unusedObj;
 
-// We maintain a free list of jsonObjects that are available
-// for use, in order to reduce the churning through
-// malloc() and free().
-
+/** Pointer to the head of the free list */
 static unusedObj* freeObjList = NULL;
 
 static void add_json_to_buffer( const jsonObject* obj,
 	growing_buffer * buf, int do_classname, int second_pass );
 
 /**
- * Return all unused jsonObjects to the heap
- * @return Nothing
- */
+	@brief Return all jsonObjects in the free list to the heap.
+
+	Reclaims memory occupied by unused jsonObjects in the free list.  It is never really
+	necessary to call this function, assuming that we don't run out of memory.  However
+	it might be worth calling if we have built and destroyed a lot of jsonObjects that
+	we don't expect to need again, in order to reduce our memory footprint.
+*/
 void jsonObjectFreeUnused( void ) {
 
 	unusedObj* temp;
@@ -81,10 +114,22 @@
 	}
 }
 
+/**
+	@brief Create a new jsonObject, optionally containing a string.
+	@param data Pointer to a string to be stored in the jsonObject; may be NULL.
+	@return Pointer to a newly allocate jsonObject.
+
+	If @a data is NULL, create a jsonObject of type JSON_NULL.  Otherwise create
+	a jsonObject of type JSON_STRING, containing the specified string.
+
+	The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewObject(const char* data) {
 
 	jsonObject* o;
 
+	// Allocate a jsonObject; from the free list if possible,
+	// or from the heap if necessary.
 	if( freeObjList ) {
 		o = (jsonObject*) freeObjList;
 		freeObjList = freeObjList->next;
@@ -110,6 +155,17 @@
 	return o;
 }
 
+/**
+	@brief Create a jsonObject, optionally containing a formatted string.
+	@param data Pointer to a printf-style format string; may be NULL.  Subsequent parameters,
+	if any, will be formatted and inserted into the resulting string.
+	@return Pointer to a newly created jsonObject.
+
+	If @a data is NULL, create a jsonObject of type JSON_NULL, and ignore any extra parameters.
+	Otherwise create a jsonObject of type JSON_STRING, containing the formatted string.
+
+	The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+ */
 jsonObject* jsonNewObjectFmt(const char* data, ...) {
 
 	jsonObject* o;
@@ -141,6 +197,16 @@
 	return o;
 }
 
+/**
+	@brief Create a new jsonObject of type JSON_NUMBER.
+	@param num The number to store in the jsonObject.
+	@return Pointer to the newly created jsonObject.
+
+	The number is stored internally as a character string, as formatted by
+	doubleToString().
+
+	The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewNumberObject( double num ) {
 	jsonObject* o = jsonNewObject(NULL);
 	o->type = JSON_NUMBER;
@@ -149,8 +215,15 @@
 }
 
 /**
- * Creates a new number object from a numeric string
- */
+	@brief Create a new jsonObject of type JSON_NUMBER from a numeric string.
+	@param numstr Pointer to a numeric character string.
+	@return Pointer to a newly created jsonObject containing the numeric value, or NULL
+		if the string is not numeric.
+
+	The jsonIsNumeric() function determines whether the input string is numeric.
+
+	The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewNumberStringObject( const char* numstr ) {
 	if( !numstr )
 		numstr = "0";
@@ -163,6 +236,17 @@
 	return o;
 }
 
+/**
+	@brief Create a new jsonObject of type JSON_BOOL, with a specified boolean value.
+	@param val An int used as a boolean value.
+	@return Pointer to the new jsonObject.
+
+	Zero represents false, and non-zero represents true, according to the usual convention.
+	In practice the value of @a val is stored unchanged, but the calling code should not try to
+	take advantage of that fact, because future versions may behave differently.
+
+	The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewBoolObject(int val) {
     jsonObject* o = jsonNewObject(NULL);
     o->type = JSON_BOOL;
@@ -170,12 +254,35 @@
     return o;
 }
 
+/**
+	@brief Create a new jsonObject of a specified type, with a default value.
+	@param type One of the 6 JSON types, as specified by the JSON_* macros.
+	@return Pointer to the new jsonObject.
+
+	An invalid type parameter will go unnoticed, and may lead to unpleasantness.
+
+	The default value is equivalent to an empty string (for a JSON_STRING), zero (for a
+	JSON_NUMBER), an empty hash (for a JSON_HASH), an empty array (for a JSON_ARRAY), or
+	false (for a JSON_BOOL).  A JSON_NULL, of course, has no value, but can still be
+	useful.
+
+	The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
+*/
 jsonObject* jsonNewObjectType(int type) {
 	jsonObject* o = jsonNewObject(NULL);
 	o->type = type;
+	if( JSON_BOOL == type )
+		o->value.b = 0;
 	return o;
 }
 
+/**
+	@brief Free a jsonObject and everything in it.
+	@param o Pointer to the jsonObject to be freed.
+
+	Any jsonObjects stored inside the jsonObject (in hashes or arrays) will be freed as
+	well, and so one, recursively.
+*/
 void jsonObjectFree( jsonObject* o ) {
 
 	if(!o || o->parent) return;
@@ -204,12 +311,26 @@
 			mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
 }
 
+/**
+	@brief Free a jsonObject through a void pointer.
+	@param key Not used.
+	@param item Pointer to the jsonObject to be freed, cast to a void pointer.
+
+	This function is a callback for freeing jsonObjects stored in an osrfHash.
+*/
 static void _jsonFreeHashItem(char* key, void* item){
 	if(!item) return;
 	jsonObject* o = (jsonObject*) item;
 	o->parent = NULL; /* detach the item */
 	jsonObjectFree(o);
 }
+
+/**
+	@brief Free a jsonObject through a void pointer.
+	@param item Pointer to the jsonObject to be freed, cast to a void pointer.
+
+	This function is a callback for freeing jsonObjects stored in an osrfList.
+ */
 static void _jsonFreeListItem(void* item){
 	if(!item) return;
 	jsonObject* o = (jsonObject*) item;
@@ -217,12 +338,39 @@
 	jsonObjectFree(o);
 }
 
+/**
+	@brief Assign a boolean value to a jsonObject of type JSON_BOOL.
+	@param bl Pointer to the jsonObject.
+	@param val The boolean value to be applied, encoded as an int.
+
+	If the jsonObject is not already of type JSON_BOOL, it is converted to one, and
+	any previous contents are freed.
+
+	Zero represents false, and non-zero represents true, according to the usual convention.
+	In practice the value of @a val is stored unchanged, but the calling code should not try to
+	take advantage of that fact, because future versions may behave differently.
+*/
 void jsonSetBool(jsonObject* bl, int val) {
     if(!bl) return;
     JSON_INIT_CLEAR(bl, JSON_BOOL);
     bl->value.b = val;
 }
 
+/**
+	@brief Insert one jsonObject into a jsonObect of type JSON_ARRAY, at the end of the array.
+	@param o Pointer to the outer jsonObject that will receive additional payload.
+	@param newo Pointer to the inner jsonObject, or NULL.
+	@return The number of jsonObjects directly subordinate to the outer jsonObject,
+		or -1 for error.
+
+	If the pointer to the outer jsonObject is NULL, jsonObjectPush returns -1.
+
+	If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
+	any previous contents are freed.
+
+	If the pointer to the inner jsonObject is NULL, jsonObjectPush creates a new jsonObject
+	of type JSON_NULL, and appends it to the array.
+*/
 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
     if(!o) return -1;
     if(!newo) newo = jsonNewObject(NULL);
@@ -233,9 +381,31 @@
 	return o->size;
 }
 
+/**
+	@brief Insert a jsonObject at a specified position in a jsonObject of type JSON_ARRAY.
+	@param dest Pointer to the outer jsonObject that will receive the new payload.
+	@param index A zero-based subscript specifying the position of the new jsonObject.
+	@param newObj Pointer to the new jsonObject to be inserted, or NULL.
+	@return The size of the internal osrfList where the array is stored, or -1 on error.
+
+	If @a dest is NULL, jsonObjectSetIndex returns -1.
+
+	If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
+	any previous contents are freed.
+
+	If @a newObj is NULL, jsonObject creates a new jsonObject of type JSON_NULL and
+	inserts it into the array.
+
+	If there is already a jsonObject at the specified location, it is freed and replaced.
+
+	Depending on the placement of the inner jsonObject, it may leave unoccupied holes in the
+	array that are included in the reported size.  As a result of this and other peculiarities
+	of the underlying osrfList within the jsonObject, the reported size may not reflect the
+	number of jsonObjects in the array.  See osrf_list.c for further details.
+*/
 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
-    if(!dest) return -1;
-    if(!newObj) newObj = jsonNewObject(NULL);
+	if(!dest) return -1;
+	if(!newObj) newObj = jsonNewObject(NULL);
 	JSON_INIT_CLEAR(dest, JSON_ARRAY);
 	newObj->parent = dest;
 	osrfListSet( dest->value.l, newObj, index );
@@ -243,6 +413,26 @@
 	return dest->value.l->size;
 }
 
+/**
+	@brief Insert a jsonObject into a jsonObject of type JSON_HASH, for a specified key.
+	@param o Pointer to the outer jsonObject that will receive the new payload.
+	@param key Pointer to a string that will serve as the key.
+	@param newo Pointer to the new jsonObject that will be inserted, or NULL.
+	@return The number of items stored in the first level of the hash.
+
+	If the @a o parameter is NULL, jsonObjectSetKey returns -1.
+
+	If the outer jsonObject is not already of type JSON_HASH, it will be converted to one,
+	and any previous contents will be freed.
+
+	If @a key is NULL, jsonObjectSetKey returns the size of the hash without doing anything
+	(apart from possibly converting the outer jsonObject as described above).
+
+	If @a newo is NULL, jsonObjectSetKey creates a new jsonObject of type JSON_NULL and inserts
+	it with the specified key.
+
+	If a previous jsonObject is already stored with the same key, it is freed and replaced.
+*/
 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
     if(!o) return -1;
     if(!newo) newo = jsonNewObject(NULL);
@@ -253,31 +443,52 @@
 	return o->size;
 }
 
+/**
+	@brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
+	@param obj Pointer to the outer jsonObject.
+	@param key The key for the associated item to be found.
+	@return A pointer to the inner jsonObject, if found, or NULL if not.
+
+	Returns NULL if either parameter is NULL, or if @a obj points to a jsonObject not of type
+	JSON_HASH, or if the specified key is not found in the outer jsonObject.
+
+	The returned pointer (if not NULL) points to the interior of the outer jsonObject.  The
+	calling code should @em not try to free it, but it may change its contents.
+*/
 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
 	if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
 	return osrfHashGet( obj->value.h, key);
 }
 
+/**
+	@brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
+	@param obj Pointer to the outer jsonObject.
+	@param key The key for the associated item to be found.
+	@return A pointer to the inner jsonObject, if found, or NULL if not.
+
+	This function is identical to jsonObjectGetKey(), except that the outer jsonObject may be
+	const, and the pointer returned points to const.  As a result, the calling code is put on
+	notice that it should not try to modify the contents of the inner jsonObject.
+ */
 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
 	if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
 	return osrfHashGet( obj->value.h, key);
 }
 
 /**
- * Recursively traverse a jsonObject, formatting it into a JSON string.
- *
- * The last two parameters are booleans.
- *
- * If do_classname is true, examine each node for a classname, and if you
- * find one, pretend that the node is under an extra layer of JSON_HASH, with
- * JSON_CLASS_KEY and JSON_DATA_KEY as keys.
- *
- * second_pass should always be false except for some recursive calls.  It
- * is used when expanding classnames, to distinguish between the first and
- * second passes through a given node.
- *
- * @return Nothing
- */
+	@brief Recursively traverse a jsonObject, translating it into a JSON string.
+	@param obj Pointer to the jsonObject to be translated.
+	@param buf Pointer to a growing_buffer that will receive the JSON string.
+	@param do_classname Boolean; if true, expand class names.
+	@param second_pass Boolean; should always be false except for some recursive calls.
+ 
+	If @a do_classname is true, expand any class names, as described in the discussion of
+	jsonObjectToJSON().
+
+	@a second_pass should always be false except for some recursive calls.  It is used
+	when expanding classnames, to distinguish between the first and second passes
+	through a given node.
+*/
 static void add_json_to_buffer( const jsonObject* obj,
 	growing_buffer * buf, int do_classname, int second_pass ) {
 
@@ -366,6 +577,13 @@
 	}
 }
 
+/**
+	@brief Translate a jsonObject into a JSON string, without expanding class names.
+	@param obj Pointer to the jsonObject to be translated.
+	@return A pointer to a newly allocated string containing the JSON.
+
+	The calling code is responsible for freeing the resulting string.
+*/
 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
 	if(!obj) return NULL;
 	growing_buffer* buf = buffer_init(32);
@@ -373,6 +591,17 @@
 	return buffer_release( buf );
 }
 
+/**
+	@brief Translate a jsonObject into a JSON string, with expansion of class names.
+	@param obj Pointer to the jsonObject to be translated.
+	@return A pointer to a newly allocated string containing the JSON.
+
+	At every level, any jsonObject containing a class name will be translated as if it were
+	under an extra layer of JSON_HASH, with JSON_CLASS_KEY as the key for the class name and
+	JSON_DATA_KEY as the key for the jsonObject.
+
+	The calling code is responsible for freeing the resulting string.
+ */
 char* jsonObjectToJSON( const jsonObject* obj ) {
 	if(!obj) return NULL;
 	growing_buffer* buf = buffer_init(32);
@@ -380,6 +609,18 @@
 	return buffer_release( buf );
 }
 
+/**
+	@brief Create a new jsonIterator for traversing a specified jsonObject.
+	@param obj Pointer to the jsonObject to be traversed.
+	@return A pointer to the newly allocated jsonIterator, or NULL upon error.
+
+	jsonNewIterator returns NULL if @a obj is NULL.
+
+	The new jsonIterator does not point to any particular position within the jsonObject to
+	be traversed.  The next call to jsonIteratorNext() will position it at the beginning.
+
+	The calling code is responsible for freeing the jsonIterator by calling jsonIteratorFree().
+*/
 jsonIterator* jsonNewIterator(const jsonObject* obj) {
 	if(!obj) return NULL;
 	jsonIterator* itr;
@@ -397,12 +638,42 @@
 	return itr;
 }
 
+/**
+	@brief Free a jsonIterator and everything in it.
+	@param itr Pointer to the jsonIterator to be freed.
+*/
 void jsonIteratorFree(jsonIterator* itr) {
 	if(!itr) return;
 	osrfHashIteratorFree(itr->hashItr);
 	free(itr);
 }
 
+/**
+	@brief Advance a jsonIterator to the next position within a jsonObject.
+	@param itr Pointer to the jsonIterator to be advanced.
+	@return A Pointer to the next jsonObject within the jsonObject being traversed; or NULL.
+
+	If the jsonObject being traversed is of type JSON_HASH, jsonIteratorNext returns a pointer
+	to the next jsonObject within the internal osrfHash.  The associated key string is available
+	via the pointer member itr->key.
+
+	If the jsonObject being traversed is of type JSON_ARRAY, jsonIteratorNext returns a pointer
+	to the next jsonObject within the internal osrfList.
+
+	In either case, the jsonIterator remains at the same level within the jsonObject that it is
+	traversing.  It does @em not descend to traverse deeper levels recursively.
+
+	If there is no next jsonObject within the jsonObject being traversed, jsonIteratorNext
+	returns NULL.  It also returns NULL if @a itr is NULL, or if the jsonIterator is
+	detectably corrupted, or if the jsonObject to be traversed is of a type other than
+	JSON_HASH or JSON_ARRAY.
+
+	Once jsonIteratorNext has returned NULL, subsequent calls using the same iterator will
+	continue to return NULL.  There is no available function to start over at the beginning.
+
+	The pointer returned, if not NULL, points to an internal element of the jsonObject being
+	traversed.  The calling code should @em not try to free it, but it may modify its contents.
+*/
 jsonObject* jsonIteratorNext(jsonIterator* itr) {
 	if(!(itr && itr->obj)) return NULL;
 	if( itr->obj->type == JSON_HASH ) {
@@ -420,6 +691,14 @@
 	}
 }
 
+/**
+	@brief Determine whether a jsonIterator is positioned at the last element of a jsonObject.
+	@param itr Pointer to the jsonIterator whose position is to be tested.
+	@return An int, as boolean: 0 if the iterator is positioned at the end, or 1 if it isn't.
+
+	If the jsonIterator is positioned where a call to jsonIteratorNext() would return NULL,
+	then jsonIteratorHasNext returns 0.  Otherwise it returns 1.
+*/
 int jsonIteratorHasNext(const jsonIterator* itr) {
 	if(!(itr && itr->obj)) return 0;
 	if( itr->obj->type == JSON_HASH )
@@ -427,14 +706,34 @@
 	return (itr->index < itr->obj->size) ? 1 : 0;
 }
 
+/**
+	@brief Fetch a pointer to a specified element within a jsonObject of type JSON_ARRAY.
+	@param obj Pointer to the outer jsonObject.
+	@param index A zero-based index identifying the element to be fetched.
+	@return A pointer to the element at the specified location, if any, or NULL.
+
+	The return value is NULL if @a obj is null, or if the outer jsonObject is not of type
+	JSON_ARRAY, or if there is no element at the specified location.
+
+	If not NULL, the pointer returned points to an element within the outer jsonObject.  The
+	calling code should @em not try to free it, but it may modify its contents.
+*/
 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
 	if(!obj) return NULL;
 	return (obj->type == JSON_ARRAY) ? 
         (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
 }
 
+/**
+	@brief Remove a specified element from a jsonObject of type JSON_ARRAY.
+	@param dest Pointer to the jsonObject from which the element is to be removed.
+	@param index A zero-based index identifying the element to be removed.
+	@return The number of elements remaining at the top level, or -1 upon error.
 
-
+	The return value is -1 if @a dest is NULL, or if it points to a jsonObject not of type
+	JSON_ARRAY.  Otherwise it reflects the number of elements remaining in the top level of
+	the outer jsonObject, not counting any at lower levels.
+*/
 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
 	if( dest && dest->type == JSON_ARRAY ) {
 		osrfListRemove(dest->value.l, index);
@@ -443,15 +742,41 @@
 	return -1;
 }
 
+/**
+	@brief Extract a specified element from a jsonObject of type JSON_ARRAY.
+	@param dest Pointer to the jsonObject from which the element is to be extracted.
+	@param index A zero-based index identifying the element to be extracted.
+	@return A pointer to the extracted element, if successful; otherwise NULL.
 
+	THe return value is NULL if @a dest is NULL, or if it points to a jsonObject not of type
+	JSON_ARRAY, or if there is no element at the specified location.
+
+	Otherwise, the calling code assumes ownership of the jsonObject to which the return value
+	points, and is responsible for freeing it by calling jsonObjectFree().  The original outer
+	jsonObject remains unchanged except for the removal of the specified element.
+
+	This function is sijmilar to jsonObjectRemoveIndex(), except that it returns a pointer to
+	the removed sub-object instead of destroying it.
+*/
 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index) {
 	if( dest && dest->type == JSON_ARRAY ) {
-		return osrfListExtract(dest->value.l, index);
+		jsonObject* obj = osrfListExtract(dest->value.l, index);
+		if( obj )
+			obj->parent = NULL;
+		return obj;
 	} else
 		return NULL;
 }
 
+/**
+	@brief Remove an element, specified by key, from a jsonObject of type JSON_HASH.
+	@param dest Pointer to the outer jsonObject from which an element is to be removed.
+	@param key The key for the associated element to be removed.
+	@return 1 if successful, or -1 if not.
 
+	The operation is considered successful if @a dest and @a key are both non-NULL, and
+	@a dest points to a jsonObject of type JSON_HASH, even if the specified key is not found.
+*/
 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
 	if( dest && key && dest->type == JSON_HASH ) {
 		osrfHashRemove(dest->value.h, key);
@@ -461,11 +786,17 @@
 }
 
 /**
- Allocate a buffer and format a specified numeric value into it.
- Caller is responsible for freeing the buffer.
-**/
+	@brief Format a double into a character string.
+	@param num The double to be formatted.
+	@return A newly allocated character string containing the formatted number.
+
+	The number is formatted according to the printf-style format specification "%.30g". and
+	will therefore contain no more than 30 significant digits.
+
+	The calling code is responsible for freeing the resulting string.
+*/
 char* doubleToString( double num ) {
-	
+
 	char buf[ 64 ];
 	size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
 	if( len < sizeof( buf ) )
@@ -480,6 +811,19 @@
 	}
 }
 
+/**
+	@brief Fetch a pointer to the string stored in a jsonObject, if any.
+	@param obj Pointer to the jsonObject.
+	@return Pointer to the string stored internally, or NULL.
+
+	If @a obj points to a jsonObject of type JSON_STRING or JSON_NUMBER, the returned value
+	points to the string stored internally (a numeric string in the case of a JSON_NUMBER).
+	Otherwise the returned value is NULL.
+
+	The returned pointer should be treated as a pointer to const.  In particular it should
+	@em not be freed.  In a future release, the returned pointer may indeed be a pointer
+	to const.
+*/
 char* jsonObjectGetString(const jsonObject* obj) {
 	if(obj)
 	{
@@ -494,11 +838,29 @@
 		return NULL;
 }
 
+/**
+	@brief Translate a jsonObject to a double.
+	@param @obj Pointer to the jsonObject.
+	@return The numeric value stored in the jsonObject.
+
+	If @a obj is NULL, or if it points to a jsonObject not of type JSON_NUMBER, the value
+	returned is zero.
+*/
 double jsonObjectGetNumber( const jsonObject* obj ) {
 	return (obj && obj->type == JSON_NUMBER && obj->value.s)
 			? strtod( obj->value.s, NULL ) : 0;
 }
 
+/**
+	@brief Store a copy of a specified character string in a jsonObject of type JSON_STRING.
+	@param dest Pointer to the jsonObject in which the string will be stored.
+	@param string Pointer to the string to be stored.
+
+	Both @a dest and @a string must be non-NULL.
+
+	If the jsonObject is not already of type JSON_STRING, it is converted to a JSON_STRING,
+	with any previous contents freed.
+*/
 void jsonObjectSetString(jsonObject* dest, const char* string) {
 	if(!(dest && string)) return;
 	JSON_INIT_CLEAR(dest, JSON_STRING);
@@ -510,6 +872,19 @@
  a specified numeric string in it.  If the string is not numeric,
  store the equivalent of zero, and return an error status.
 **/
+/**
+	@brief Store a copy of a numeric character string in a jsonObject of type JSON_NUMBER.
+	@param dest Pointer to the jsonObject in which the number will be stored.
+	@param string Pointer to the numeric string to be stored.
+
+	Both @a dest and @a string must be non-NULL.
+
+	If the jsonObject is not already of type JSON_NUMBER, it is converted to a JSON_STRING,
+	with any previous contents freed.
+
+	If the input string is not numeric as determined by jsonIsNumeric(), the number stored
+	is zero.
+ */
 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
 	if(!(dest && string)) return -1;
 	JSON_INIT_CLEAR(dest, JSON_NUMBER);
@@ -524,22 +899,53 @@
 	}
 }
 
+/**
+	@brief Store a number in a jsonObject of type JSON_NUMBER.
+	@param dest Pointer to the jsonObject in which the number will be stored.
+	@param num The number to be stored.
+
+	If the jsonObject is not already of type JSON_NUMBER, it is converted to one, with any
+	previous contents freed.
+*/
 void jsonObjectSetNumber(jsonObject* dest, double num) {
 	if(!dest) return;
 	JSON_INIT_CLEAR(dest, JSON_NUMBER);
 	dest->value.s = doubleToString( num );
 }
 
+/**
+	@brief Assign a class name to a jsonObject.
+	@param dest Pointer to the jsonObject.
+	@param classname Pointer to a string containing the class name.
+
+	Both dest and classname must be non-NULL.
+*/
 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
 	if(!(dest && classname)) return;
 	free(dest->classname);
 	dest->classname = strdup(classname);
 }
+
+/**
+	@brief Fetch a pointer to the class name of a jsonObject, if any.
+	@param dest Pointer to the jsonObject.
+	@return Pointer to a string containing the class name, if there is one; or NULL.
+
+	If not NULL, the pointer returned points to a string stored internally within the
+	jsonObject.  The calling code should @em not try to free it.
+*/
 const char* jsonObjectGetClass(const jsonObject* dest) {
     if(!dest) return NULL;
     return dest->classname;
 }
 
+/**
+	@brief Create a copy of an existing jsonObject, including all internal sub-objects.
+	@param o Pointer to the jsonObject to be copied.
+	@return A pointer to the newly created copy.
+
+	The calling code is responsible for freeing the copy of the original.
+*/
 jsonObject* jsonObjectClone( const jsonObject* o ) {
     if(!o) return jsonNewObject(NULL);
 
@@ -586,6 +992,14 @@
     return result;
 }
 
+/**
+	@brief Return the truth or falsity of a jsonObject of type JSON_BOOL.
+	@param boolObj Pointer to the jsonObject.
+	@return 1 or 0, depending on whether the stored boolean is true or false.
+
+	If @a boolObj is NULL, or if it points to a jsonObject not of type JSON_BOOL, the
+	returned value is zero.
+*/
 int jsonBoolIsTrue( const jsonObject* boolObj ) {
     if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
         return 1;
@@ -593,6 +1007,16 @@
 }
 
 
+/**
+	@brief Create a copy of the string stored in a jsonObject.
+	@param o Pointer to the jsonObject whose string is to be copied.
+	@return Pointer to a newly allocated string copied from the jsonObject.
+
+	If @a o is NULL, or if it points to a jsonObject not of type JSON_STRING or JSON_NUMBER,
+	the returned value is NULL.  In the case of a JSON_NUMBER, the string created is numeric.
+
+	The calling code is responsible for freeing the newly created string.
+*/
 char* jsonObjectToSimpleString( const jsonObject* o ) {
 	if(!o) return NULL;
 
@@ -616,6 +1040,29 @@
  This validation follows the rules defined by the grammar at:
  http://www.json.org/
  **/
+/**
+	@brief Determine whether a specified character string is a valid JSON number.
+	@param s Pointer to the string to be examined.
+	@return 1 if the string is numeric, or 0 if not.
+
+	This function defines numericity according to JSON rules; see http://json.org/.  This
+	determination is based purely on the lexical properties of the string.  In particular
+	there is no guarantee that the number in a numeric string is representable in C as a
+	long long, a long double, or any other built-in type.
+
+	A numeric string consists of:
+
+	- An optional leading minus sign (but not a plus sign)
+	- One or more decimal digits.  The first digit may be a zero only if it is the only
+	  digit to the left of the decimal.
+	- Optionally, a decimal point followed by one or more decimal digits.
+	- An optional exponent, consisting of:
+		- The letter E, in upper or lower case
+		- An optional plus or minus sign
+		- One or more decimal digits
+
+	See also jsonScrubNumber().
+*/
 int jsonIsNumeric( const char* s ) {
 
 	if( !s || !*s ) return 0;
@@ -699,10 +1146,23 @@
 }
 
 /**
- Allocate and reformat a numeric string into one that is valid
- by JSON rules.  If the string is not numeric, return NULL.
- Caller is responsible for freeing the buffer.
- **/
+	@brief Edit a string into a valid JSON number, if possible.
+	@param s Pointer to the string to be edited.
+	@return A pointer to a newly created numeric string, if possible; otherwise NULL.
+
+	JSON has rather exacting requirements about what constitutes a valid numeric string (see
+	jsonIsNumeric()).  Real-world input may be a bit sloppy.  jsonScrubNumber accepts numeric
+	strings in a less formal format and reformats them, where possible, according to JSON
+	rules.  It removes leading white space, a leading plus sign, and extraneous leading zeros.
+	It adds a leading zero as needed when the absolute value is less than 1.  It also accepts
+	scientific notation in the form of a bare exponent (e.g. "E-3"), supplying a leading factor
+	of "1".
+
+	If the input string is non-numeric even according to these relaxed rules, the return value
+	is NULL.
+
+	The calling code is responsible for freeing the newly created numeric string.
+*/
 char* jsonScrubNumber( const char* s ) {
 	if( !s || !*s ) return NULL;
 



More information about the opensrf-commits mailing list