cson  TipsAndTricks

ACHTUNG: THIS PAGE IS NOW MAINTAINED IN THE NEW WIKI: http://whiki.wanderinghorse.net/wikis/cson/?page=TipsAndTricks

See also: HowTo

Tips and Tricks

This page demonstrates some useful tips and tricks for clients using the cson API, as well as useful information for users who want to "get fairly intimate" with cson.

Controlling and Extending Value Lifetimes

When a shared JSON value is used in several places within an application, e.g. in multiple containers, making sure it stays alive as long as it needs to can be tricky if one or more of the references to that value are not managed in a container (Object or Array), but other references are in a container. For example:

cson_value * sharedValue = cson_value_new_object();
cson_value * container = cson_value_new_object();
cson_value_set( cson_value_get_object(container), "foo", sharedValue );

In that case, the container object owns sharedValue, meaning that sharedValue will be destroyed/invalidated when container is cleaned up. If we need to ensure that sharedValue lives at least as long as container we have at least two options:

Option #1: Add sharedValue to a separate JSON container (Array or Object), shared app-wide. This will add a reference, keeping that value alive at least as long as that shared container is alive. See the next section for a use case where this approach is useful.

Option #2: Manually add a reference to sharedValue by calling cson_value_add_reference(sharedValue). At some point (when the app is done with it), cson_value_free(sharedValue) should be called to free the reference. If the container object is still alive at that time then ownership will effectively be handed over to it (or shared with it, if the value is in multiple containers). If, however, the container object has already been destroyed, the call to cson_value_free() will free it (assuming it is not also in other containers at that time).

Poor Man's Garbage Collection

JSON-intensive applications which store multiple cson_value references for the lifetime of the app may find that it becomes troublesome to clean them up when the time comes. A useful trick is to keep a separate Array/Object for garbage collection purposes and to add all global/shared/long-lived values there. At cleanup-time, cleaning up the array/object will clean up the values contained in it. Until the container is cleaned up, the values still have a live reference (from the container) and won't be cleaned up unless the API is mis-used (e.g. cson_value_free() is called when it should not be). The cson_cgi API uses this trick to simplify the lifetimes/cleanup of the numerous "environment objects" it manages. When adding new environments, we don't need to change the cleanup code - we simply have to add the new object to the "garbage collector."

Here's an example function to assist in this approach, taken from cson_cgi:

/**
   Adds v to the API-internal cleanup. key must be a unique key for
   the given element. Adding another item with that key may free the
   previous one. If freeOnError is true then v is passed to
   cson_value_free() if the key cannot be inserted, otherwise
   ownership of v is not changed on error.

   Returns 0 on success.
*/
static int cson_cgi_gc_add( char const * key, cson_value * v, char freeOnError )
{
    int const rc = cson_object_set( CgiEnv.gc.jobj, key, v );
    if( (0 != rc) && freeOnError )
    {
        cson_value_free( v );
    }
    return rc;
}

Extract Child Node and Discard Parent

Here's an obscure case: i have a JSON tree from which i want to extract one child element and discard the rest, including the parent. The memory ownership here gets a tiny bit tricky, but it's easy enough to solve:

cson_value * root = ... the root of my JSON tree ...;
cson_value * child = cson_object_get( cson_value_get_object(root), "theChild" );
if( NULL == child ) { ... not found or root is-not-a Object... }

Now comes the problem: if we free the parent, the child goes with it. We can work around that, though:

// 1. Claim partial ownership of the child:
  cson_value_add_reference( child );
// 2. Free the parent. child is NOT freed b/c of our reference:
  cson_value_free( root );
// 3. Re-map root of the tree to the child object:
  root = child;
  child = NULL;

The root object now has "normal" ownership again. Well, almost. There's still a subtle problem problem here involving the child element reference count. If the new root node is ever added to a container to transfer ownership, we must also call cson_value_free(root) after adding it to the new parent container. If we do not, the reference count which we incremented earlier will hang around and keep the node from being freed. If we call cson_value_free(root) before adding it to a container than it will be cleaned up, as expected (but then must not be added to a container!). The reason for this odd requirement is explained in the next section.

How Value Reference Counts Really Work

Initial versions of this library used no reference counting for values. This meant that it was illegal, due to memory ownership semantics, to add a given value to more than one container, or to add it more than once within the same container. To solve that, allowing us more flexibility at the client level and give us some more memory allocation optimization possibilities at the library level, reference counting was added.

When reference counting was added to the API i had to made a tough philosophical decision: when a value handle is freshly created, should it have a reference count of 1 or 0? Intuition says "1", but allow me to demonstrate how we would have to use the API if that were the case:

// ACHTUNG: this code is NOT legal cson: it is only to demonstrate a point
cson_value * v = cson_value_new_integer(42); // refcount == 1
cson_object_set( myObject, "myValue", v );
// v's reference count would be 2 here.

What that means is that when myObject is cleaned up, v would not be cleaned up because the value's reference count would, after cleanup of the container object, be 1. That means that we would have to, for normal use, add the following line after inserting the value into the container:

cson_value_free(v); // refcount reduced to 1. v is now owned by myObject.

That usage would be not only horribly non-intuitive (add the object to a container and then free it!?!?!?), but would make the simplest usages of cson much more difficult. e.g., the following code would not be useful:

cson_object_set( myObject, "myValue", cson_value_new_integer(42) );

Can you see why? The new value would have a refcount of 2 after the insertion and we would not have a concrete reference to it (only via the object accessor functions). i.e. this would lead to an unintentional leak more often than not.

Pedantic side-note: be aware that the above code actually still has a potential memory leak condition - if insertion fails (e.g. the internals cannot be allocated to hold it) then we will leak the new value. The "most correct" way to write it is:

cson_value * v = cson_value_new_integer(42);
if( 0 != cson_object_set( myObject, "myValue", v ) ) {
   ... we still own v ...
   cson_value_free(v);
}

Anyway... because of the hypothetical behaviour demonstrated above, it was decided that new values start their life with a reference count of 0. The following demonstrates how the library really works vis-a-vis reference counting:

cson_value * v = cson_value_new_integer(42); // refcount==0
if( 0 == cson_object_set( myObject, "myValue", v ) } {
  // v refcount==1, owned by myObject
}
else {
  // v refcount==0 and we still own it
  cson_value_free( v );
}

Upon successful insertion, myObject effectively owns the value until/unless we add the value to another container (which increases its reference count, sharing ownership with the object) or call cson_value_add_reference() to manually increase the reference count (again sharing ownership). If we do neither of these, myObject will clean the value when myObject is destroyed (i.e. when the value's reference count is decremented to 0).

When cson_value_free() is called, it actually treats reference counts of 1 or 0 identically - it frees the underlying memory. It could be argued that this behaviour is "unfortunate", and possibly a bit non-intuitive, but this approach plays much nicer with "normal use cases" than if the reference count started at 1. i'm open to suggestions if someone has a better idea.

Fully Buffering JSON Input

cson_parse() and friends read their input using a client-supplied callback with the type cson_data_source_f(). To minimize complexity in the core code cson_parse() currently reads byte-by-byte without buffering any input. If access to storage is slow, one can buffer the input into a cson_buffer by using the the same cson_data_source_f() which would normally be passed to cson_parse().

Here's a complete example, including error handling:

cson_value * root = NULL; // don't forget the NULL initialization!
cson_buffer buf = cson_buffer_empty;

// OPTIONALLY reserve memory in buf, to minimize future reallocations:
rc = cson_buffer_reserve( &buf, 1024 * 10 );
if( rc ) {
    ... allocation error ...
    return ...;
}

// Fill buf with the complete contents of stdin:
int rc = cson_buffer_fill_from( &buf, cson_data_source_FILE, stdin );

if( rc ) {
    ... error ...
    cson_buffer_reserve( &buf, 0 ); // frees its buffer memory
    return ...;
}

// Parse the buffered input:
rc = cson_parse_buffer( &root, &buf, NULL, NULL );

// Free the buffer's memory:
cson_buffer_reserve( &buf, 0 );

if( 0 == rc ) {
    ... use the root object and eventually free it ...
    cson_value_free( root );
}
else {
   assert( NULL == root ); // just for demonstration purposes
}