cson  Artifact Content

Artifact 9210987ab163d5a4278cfd06eff76da10a7637de:

Wiki page [TipsAndTricks] by stephan 2011-05-09 17:58:46.
D 2011-05-09T17:58:46.753
L TipsAndTricks
P 037ba258af63bfd5c669bd0ceeb971c1dd55bcb6
U stephan
W 10580
<strong>ACHTUNG: THIS PAGE IS NOW MAINTAINED IN THE NEW WIKI:</strong> [http://whiki.wanderinghorse.net/wikis/cson/?page=TipsAndTricks]


See also: [HowTo]

<h1>Tips and Tricks</h1>

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.

<h1>Controlling and Extending Value Lifetimes</h1>

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 <em>not</em> managed in a container (Object or Array), but other references <em>are</em> in a container. For example:

<verbatim>
cson_value * sharedValue = cson_value_new_object();
cson_value * container = cson_value_new_object();
cson_value_set( cson_value_get_object(container), "foo", sharedValue );
</verbatim>

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

Option #1: Add <tt>sharedValue</tt> 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 <tt>sharedValue</tt> by calling <tt>cson_value_add_reference(sharedValue)</tt>. At some point (when the app is done with it), <tt>cson_value_free(sharedValue)</tt> should be called to free the reference. If the <tt>container</tt> 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 <tt>container</tt> object has already been destroyed, the call to <tt>cson_value_free()</tt> will free it (assuming it is not also in other containers at that time).


<h1>Poor Man's Garbage Collection</h1>

JSON-intensive applications which store multiple <tt>cson_value</tt> 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. <tt>cson_value_free()</tt> 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]:

<verbatim>
/**
   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;
}
</verbatim>

<h1>Extract Child Node and Discard Parent</h1>

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:

<verbatim>
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... }
</verbatim>

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

<verbatim>
// 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;
</verbatim>

The root object now has "normal" ownership again. Well, almost. There's still a subtle problem problem here involving the child element reference count. <em>If</em> the new root node is ever added to a container to transfer ownership, we <em>must also</em> call <tt>cson_value_free(root)</tt> <em>after</em> 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 <tt>cson_value_free(root)</tt> <em>before</em> adding it to a container than it will be cleaned up, as expected (but then <em>must not</em> be added to a container!). The reason for this odd requirement is explained in the next section.

<h1>How Value Reference Counts Really Work</h1>

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:

<verbatim>
// 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.
</verbatim>

What that means is that when <tt>myObject</tt> is cleaned up, <tt>v</tt> would <em>not</em> 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 <em>after</em> inserting the value into the container:

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

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:

<verbatim>
cson_object_set( myObject, "myValue", cson_value_new_integer(42) );
</verbatim>

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:

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

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 <em>really</em> works vis-a-vis reference counting:

<verbatim>
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 );
}
</verbatim>

Upon successful insertion, <tt>myObject</tt> 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 <tt>cson_value_add_reference()</tt> to manually increase the reference count (again sharing ownership). If we do neither of these, <tt>myObject</tt> will clean the value when <tt>myObject</tt> is destroyed (i.e. when the value's reference count is decremented to 0).

When <tt>cson_value_free()</tt> 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.


<h1>Fully Buffering JSON Input</h1>

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

Here's a complete example, including error handling:

<verbatim>
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
}
</verbatim>

Z 1cf748b8c2c0f25c27005b66023ef8cd