Actually put the JSON doc into the user manual 63/4363/5
authorE. Scott Daniels <daniels@research.att.com>
Thu, 9 Jul 2020 15:56:10 +0000 (11:56 -0400)
committerE. Scott Daniels <daniels@research.att.com>
Mon, 13 Jul 2020 14:59:59 +0000 (10:59 -0400)
The user manual include statment wasn't pulling the json section.
This change fixes that.

This change also closes some memory leaks in the json wrapper.

Signed-off-by: E. Scott Daniels <daniels@research.att.com>
Change-Id: I54849c67960fd945a85327a3ff308d7d7651b673

15 files changed:
doc/src/lib/front_junk.im
doc/src/lib/rst.im
doc/src/lib/setup.im
doc/src/user/Makefile
doc/src/user/cpp_frame.im
doc/src/user/example1.im
doc/src/user/example2.im
doc/src/user/example3.im
doc/src/user/jhash.im
doc/src/user/user_guide.xfm
docs/user-guide.rst
src/json/jhash.cpp
src/json/jwrapper.c
test/Makefile
test/jhash_test.cpp

index 718cc6a..1109537 100644 (file)
                .** ensure these happen after toc is rendered
                .if pfm
                        .pn on 0 noline center f=%d
-                       .cd 1 6.0i
+                       .cd 1 180i
                .fi
        .ei
                .if "&ot" "rst" =
                        .dv raw_comment_sym ^..
-                       .cd 1 26i
+                       .cd 1 18i m=0i
                        .ll 18i
                        .im &{lib}/raw_license.im
+                       .cd 1 6.5i m=0i
                        .ll 6i
 
+.if false
                        .if doc_title
                                &many_equals
                                &doc_title
@@ -95,6 +97,7 @@
                                &doc_subtitle
                                &many_dashes
                        .fi
+.fi
                .fi
 
        .dv _front_junk 1
index 5d6da09..437ec8a 100644 (file)
@@ -67,7 +67,7 @@
     .fi
        .dv h4 **$1**
 
-       .** bloody rst won't allow breaks in a bullet list so we have to allow the column to go wide.
+       .** initially need a wide column to allow copyright to not break
        .dv cd 1 180i m=0i
 
 
index 07d4f7c..c04a8ca 100644 (file)
 
        .**  imbed output type specific macro file
        .gv e OUTPUT_TYPE ot
-       .im  &{lib}/&{ot}.im
+       .dv output_type &{ot!txt}
+
+       .im cmd_master.im
+       .** .im  &{lib}/&{ot}.im
 
        .gv e XFM_PASS pass
        .dv pass &{pass!1}
index ca3c252..70ab002 100644 (file)
@@ -17,6 +17,8 @@
 
 # this uses {X}fm which can be cloned from: https://gitlab.com/rouxware/xfm
 
+XPATH=XFM_PATH=.:/usr/local/share/xfm TFM_PATH=.:/usr/local/share/xfm
+
 docs = user_guide
 src = user_guide.xfm
 imbed_src = cpp_frame.im example1.im example2.im example3.im jhash.im
@@ -28,20 +30,20 @@ desired_out = rst ps
 # references etc.
 #
 %.ps: %.xfm
-       OUTPUT_TYPE=generic_ps XFM_PASS=1 pfm $< /dev/null
-       OUTPUT_TYPE=generic_ps XFM_PASS=2 pfm $< $@
+       $(XPATH) OUTPUT_TYPE=generic_ps XFM_PASS=1 pfm $< /dev/null
+       $(XPATH) OUTPUT_TYPE=generic_ps XFM_PASS=2 pfm $< $@
 
 %.md: %.xfm
-       OUTPUT_TYPE=markdown XFM_PASS=1 tfm $< /dev/null
-       OUTPUT_TYPE=markdown XFM_PASS=2 tfm $< | sed 's/^ //' >$@
+       $(XPATH) OUTPUT_TYPE=markdown XFM_PASS=1 tfm $< /dev/null
+       $(XPATH) OUTPUT_TYPE=markdown XFM_PASS=2 tfm $< | sed 's/^ //' >$@
 
 %.rst: %.xfm
-       OUTPUT_TYPE=rst XFM_PASS=1 tfm $< /dev/null
-       GEN_TITLE=1 OUTPUT_TYPE=rst XFM_PASS=2 tfm $< | sed 's/^ //; s/ *$$//' >$@
+       $(XPATH) OUTPUT_TYPE=rst XFM_PASS=1 tfm $< /dev/null
+       $(XPATH) GEN_TITLE=1 OUTPUT_TYPE=rst XFM_PASS=2 tfm $< | sed 's/^ //; s/ *$$//' >$@
 
 %.txt: %.xfm
-       OUTPUT_TYPE=txt XFM_PASS=1 tfm $< /dev/null
-       OUTPUT_TYPE=txt XFM_PASS=2 tfm $< $@
+       $(XPATH) OUTPUT_TYPE=txt XFM_PASS=1 tfm $< /dev/null
+       $(XPATH) OUTPUT_TYPE=txt XFM_PASS=2 tfm $< $@
 
 # -----------------------------------------------------------------------------------
 all:    $(desired_out:%=user_guide.%)
index 9481b5a..0c7a560 100644 (file)
@@ -41,10 +41,10 @@ parameters:
 &half_space
 &indent
 &beg_dlist(.5i:&ditemtext)
-    &ditem(port) A C string (pointer to char) which defines the port that RMR will open to listen for connections.
+    &di(port) A C string (pointer to char) which defines the port that RMR will open to listen for connections.
     &half_space
 
-    &ditem(wait) A Boolean value which indicates whether or not the initialization process should wait for
+    &di(wait) A Boolean value which indicates whether or not the initialization process should wait for
         the arrival of a valid route table before completing.
         When true is supplied, the initialization will not complete until RMR has received a valid route table
         (or one is located via the &cw(RMR_SEED_RT) environment variable).
@@ -67,7 +67,7 @@ The following code sample illustrates the simplicity of creating the instance of
               new Xapp( listen_port, wait4table ) );
     }
 &ex_end
-&figure( Creating an xAPP instance.)
+&fig( Creating an xAPP instance.)
 &space
 
 From a compilation perspective, the following is the simple compiler invocation string needed to compile
@@ -106,31 +106,31 @@ The following is the prototype which callback functions must be defined with:
           int payload_len, Msg_component payload,
           void* usr_data );
 &ex_end
-&figure( Callback function signature)
+&fig( Callback function signature)
 &space
 
 The parameters passed to the callback function are as follows:
-&multi_space( .1 )
+&mult_space( .1 )
 
 &indent
 &beg_dlist(1i:&ditemtext)
-    &ditem(m) A reference to the Message that was received.
+    &di(m) A reference to the Message that was received.
     &half_space
 
-    &ditem(mtype) The message type (allows for disambiguation if the callback is registered for multiple message types).
+    &di(mtype) The message type (allows for disambiguation if the callback is registered for multiple message types).
     &half_space
 
-    &ditem(subid) The subscription ID from the message.
+    &di(subid) The subscription ID from the message.
     &half_space
 
-    &ditem(payload len) The number of bytes which the sender has placed into the payload.
+    &di(payload len) The number of bytes which the sender has placed into the payload.
     &half_space
 
-    &ditem(payload) A direct reference (smart pointer) to the payload. (The smart pointer is wrapped in a
+    &di(payload) A direct reference (smart pointer) to the payload. (The smart pointer is wrapped in a
             special class in order to provide a custom destruction function without burdening the xApp developer
             with that knowledge.)
     &half_space
-    &ditem(user data) A pointer to user data. This is the pointer that was provided when the function was registered.
+    &di(user data) A pointer to user data. This is the pointer that was provided when the function was registered.
 &end_dlist
 &uindent
 &space
@@ -170,7 +170,7 @@ framework (explained in the next section).
         xapp->Run( 1 );        // start the callback driver
     }
 &ex_end
-&figure( Callback function example.)
+&fig( Callback function example.)
 &space
 
 As before, the program does nothing useful, but now it will execute and receive messages.
@@ -209,10 +209,10 @@ Sending the message is a function of the message object itself and can take one
 &half_space
 &indent
 &beg_list(&lic1)
-    &item Replying to the sender of a received message
+    &li Replying to the sender of a received message
     &half_space
 
-    &item Sending a message (routed based on the message type and subscription ID)
+    &li Sending a message (routed based on the message type and subscription ID)
 &end_list
 &uindent
 &space
@@ -232,7 +232,7 @@ with the following prototypes.
 
    bool Send_response(  int response_len, std::shared_ptr<unsigned char> response );
 &ex_end
-&figure( Reply function prototypes. )
+&fig( Reply function prototypes. )
 &space
 
 In the first prototype the xApp must supply the new message type and subscription ID values, where the
@@ -248,7 +248,7 @@ constant can be used as illustrated below.
     msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID,
         pl_length, (unsigned char *) payload );
 &ex_end
-&figure( Send response prototype. )
+&fig( Send response prototype. )
 &space
 
 In addition to the two function prototypes for &cw(Send_response()) there are two additional prototypes
@@ -273,7 +273,7 @@ functions and are shown below.
 
     bool Send_msg( int payload_len, unsigned char* payload );
 &ex_end
-&figure( Send function prototypes. )
+&fig( Send function prototypes. )
 &space
 
 Each send function accepts the message, copies in the payload provided, sets the message type and subscription
@@ -314,7 +314,7 @@ for the response string, the response is just not sent).
       msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
     }
 &ex_end
-&figure( Send message without buffer copy. )
+&fig( Send message without buffer copy. )
 &space
 
 &h2(Sending Multiple Responses)
index f54af6d..8ca90b6 100644 (file)
@@ -52,6 +52,6 @@ the framework, have been omitted. The full code is in the framework repository.
 &ex_start
 .im  j=start-example ../../../examples/rmr_dump.cpp
 &ex_end
-&figure( Simple callback application. )
+&fig( Simple callback application. )
 &uindent
 
index 90a7f51..bc1a9fc 100644 (file)
@@ -40,6 +40,6 @@ are not locked when incremented.
 &ex_start
 .im  j=start-example ../../../examples/xapp_t1.cpp
 &ex_end
-&figure( Simple callback application. )
+&fig( Simple callback application. )
 &uindent
 &space
index e25f03e..09d2117 100644 (file)
@@ -30,6 +30,6 @@ been made in order to keep the example small and to the point.
 &ex_start
 .im  j=start-example ../../../examples/xapp_t2.cpp
 &ex_end
-&figure( Simple looping sender application. )
+&fig( Simple looping sender application. )
 &space
 
index d381542..ec30b0c 100644 (file)
@@ -46,7 +46,7 @@ The Jhash object is created simply by passing a json string to the constructor.
 
     jh =  new Jhash( jstring.c_str() );
 &ex_end
-&figure(The creation of the Jhash object.)
+&fig(The creation of the Jhash object.)
 &space
 
 
@@ -85,7 +85,7 @@ two &ital(sub-blobs) (address and lease_info).
 .gv fig
 .sv _fig
 &set_fref(jblob_fig:&_fig)
-&figure(Sample json with a root and too blobs.)
+&fig(Sample json with a root and too blobs.)
 
 &space
 Upon creation of the Jhash object, the &ital(root) fields, &cw(lodge_name,) &cw(member_count,) and
@@ -101,7 +101,7 @@ The code sample in figure &fig_blob_sample illustrates how  a &ital(sub-blob) is
 .gv fig
 .sv _fig
 &set_fref(fig_blob_sample:&_fig)
-&figure(Blob selection example.)
+&fig(Blob selection example.)
 &space
 
 Currently, the selected blob must be unset in order to select a blob at the root
@@ -227,7 +227,7 @@ values from the json in figure &array_blob_json_fig.
 &ex_end
 .gv fig
 &set_fref(array_blob_code_fig:&_fig)
-&figure(Json array containing blobs.)
+&fig(Json array containing blobs.)
 &space
 
 
@@ -250,6 +250,6 @@ values from the json in figure &array_blob_json_fig.
 &ex_end
 .gv fig
 &set_fref(array_blob_json_fig:&_fig)
-&figure(Code to process the array of blobs.)
+&fig(Code to process the array of blobs.)
 &space
 
index 6df8248..4a469f7 100644 (file)
@@ -54,7 +54,7 @@
 
 .**  ------ major sections --------
 .im cpp_frame.im
-.im hash.im
+.im jhash.im
 
 &h1(Example Programmes)
 The following sections contain several example programmes which are written on
index 58ccd58..e0ac2e1 100644 (file)
@@ -1,3 +1,6 @@
+============
+USER'S GUIDE
+============
 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
 .. SPDX-License-Identifier: CC-BY-4.0
 ..
@@ -6,9 +9,6 @@
 .. Do NOT make changes directly to .rst or .md files.
 
 
-============================================================================================
-User's Guide
-============================================================================================
 
 
 INTRODUCTION
@@ -43,855 +43,1164 @@ The creation of the xApp instance is as simple as invoking
 the object's constructor with two required parameters:
 
 
-      port
-         A C string (pointer to char) which defines the port that
-         RMR will open to listen for connections.
+      .. list-table::
+        :widths: auto
+        :header-rows: 0
+        :class: borderless
 
-      wait
-         A Boolean value which indicates whether or not the
-         initialization process should wait for the arrival of a
-         valid route table before completing. When true is
-         supplied, the initialization will not complete until RMR
-         has received a valid route table (or one is located via
-         the RMR_SEED_RT environment variable).
+        * - **port**
+          -
+            A C string (pointer to char) which defines the port that
+            RMR will open to listen for connections.
 
-      The following code sample illustrates the simplicity of
-      creating the instance of the xApp object.
 
+            |
 
-      ::
+        * - **wait**
+          -
+            A Boolean value which indicates whether or not the
+            initialization process should wait for the arrival of a
+            valid route table before completing. When true is
+            supplied, the initialization will not complete until RMR
+            has received a valid route table (or one is located via
+            the ``RMR_SEED_RT`` environment variable).
 
-            #include <memory>
-            #include <ricxfcpp/xapp.hpp>
-            int main( ) {
-                std::unique_ptr<Xapp> xapp;
-                char* listen_port = (char *) "4560";    //RMR listen port
-                bool  wait4table = true;            // wait for a route table
 
-                xapp = std::unique_ptr<Xapp>(
-                      new Xapp( listen_port, wait4table ) );
-            }
 
-      Figure 1: Creating an xAPP instance.
+The following code sample illustrates the simplicity of
+creating the instance of the xApp object.
+
+
+::
+
+     #include <memory>
+     #include <ricxfcpp/xapp.hpp>
+     int main( ) {
+         std::unique_ptr<Xapp> xapp;
+         char* listen_port = (char *) "4560";    //RMR listen port
+         bool  wait4table = true;            // wait for a route table
 
-      From a compilation perspective, the following is the simple
-      compiler invocation string needed to compile and link the
-      above program (assuming that the sample code exists in a file
-      called man_ex1.cpp.
+         xapp = std::unique_ptr<Xapp>(
+               new Xapp( listen_port, wait4table ) );
+     }
 
+Figure 1: Creating an xAPP instance.
 
-      ::
+From a compilation perspective, the following is the simple
+compiler invocation string needed to compile and link the
+above program (assuming that the sample code exists in a file
+called ``man_ex1.cpp``.
 
-           g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
 
+::
 
-      The above program, while complete and capable of being
-      compiled, does nothing useful. When invoked, RMR will be
-      initialized and will begin listening for a route table;
-      blocking the return to the main program until one is
-      received. When a valid route table arrives, initialization
-      will complete and the program will exit as there is no code
-      following the instruction to create the object.
+    g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
+
+
+The above program, while complete and capable of being
+compiled, does nothing useful. When invoked, RMR will be
+initialized and will begin listening for a route table;
+blocking the return to the main program until one is
+received. When a valid route table arrives, initialization
+will complete and the program will exit as there is no code
+following the instruction to create the object.
 
 
 LISTENING FOR MESSAGES
 ======================
 
-      The program in the previous example can be extended with just
-      a few lines of code to enable it to receive and process
-      messages. The application needs to register a callback
-      function for each message type which it desires to process.
+The program in the previous example can be extended with just
+a few lines of code to enable it to receive and process
+messages. The application needs to register a callback
+function for each message type which it desires to process.
 
-      Once registered, each time a message is received the
-      registered callback for the message type will be invoked by
-      the framework.
+Once registered, each time a message is received the
+registered callback for the message type will be invoked by
+the framework.
 
 
 Callback Signature
 ------------------
 
-      As with most callback related systems, a callback must have a
-      well known function signature which generally passes event
-      related information and a "user" data pointer which was
-      registered with the function. The following is the prototype
-      which callback functions must be defined with:
+As with most callback related systems, a callback must have a
+well known function signature which generally passes event
+related information and a "user" data pointer which was
+registered with the function. The following is the prototype
+which callback functions must be defined with:
 
 
-      ::
+::
 
-            void cb_name( Message& m, int mtype, int subid,
-                  int payload_len, Msg_component payload,
-                  void* usr_data );
+     void cb_name( Message& m, int mtype, int subid,
+           int payload_len, Msg_component payload,
+           void* usr_data );
 
-      Figure 2: Callback function signature
+Figure 2: Callback function signature
 
-      The parameters passed to the callback function are as
-      follows: &multi_space
+The parameters passed to the callback function are as
+follows:
 
-      m
-         A reference to the Message that was received.
 
-      mtype
-         The message type (allows for disambiguation if the
-         callback is registered for multiple message types).
+      .. list-table::
+        :widths: auto
+        :header-rows: 0
+        :class: borderless
 
-      subid
-         The subscription ID from the message.
+        * - **m**
+          -
+            A reference to the Message that was received.
 
-      payload len
-         The number of bytes which the sender has placed into the
-         payload.
 
-      payload
-         A direct reference (smart pointer) to the payload. (The
-         smart pointer is wrapped in a special class in order to
-         provide a custom destruction function without burdening
-         the xApp developer with that knowledge.)
+            |
 
-      user data
-         A pointer to user data. This is the pointer that was
-         provided when the function was registered.
+        * - **mtype**
+          -
+            The message type (allows for disambiguation if the
+            callback is registered for multiple message types).
 
-      To illustrate the use of a callback function, the previous
-      code example has been extended to add the function, register
-      it for message types 1000 and 1001, and to invoke the Run()
-      function in the framework (explained in the next section).
 
-      ::
+            |
 
-            #include <memory>
-            #include <ricxfcpp/xapp.hpp>
-            long m1000_count = 0;    // message counters, one for each type
-            long m1001_count = 0;
+        * - **subid**
+          -
+            The subscription ID from the message.
 
-            // callback function that will increase the appropriate counter
-            void cbf( Message& mbuf, int mtype, int subid, int len,
-                        Msg_component payload,  void* data ) {
-                long* counter;
 
-                if( (counter = (long *) data) != NULL ) {
-                    (*counter)++;
-                }
-            }
+            |
 
-            int main( ) {
-                std::unique_ptr<Xapp> xapp;
-                char* listen_port = (char *) "4560";
-                bool  wait4table = false;
+        * - **payload len**
+          -
+            The number of bytes which the sender has placed into the
+            payload.
 
-                xapp = std::unique_ptr<Xapp>(
-                      new Xapp( listen_port, wait4table ) );
 
-                // register the same callback function for both msg types
-                xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
-                xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
+            |
 
-                xapp->Run( 1 );        // start the callback driver
-            }
+        * - **payload**
+          -
+            A direct reference (smart pointer) to the payload. (The
+            smart pointer is wrapped in a special class in order to
+            provide a custom destruction function without burdening
+            the xApp developer with that knowledge.)
+
+
+            |
+
+        * - **user data**
+          -
+            A pointer to user data. This is the pointer that was
+            provided when the function was registered.
+
+
+
+To illustrate the use of a callback function, the previous
+code example has been extended to add the function, register
+it for message types 1000 and 1001, and to invoke the
+``Run()`` function in the framework (explained in the next
+section).
+
+::
+
+     #include <memory>
+     #include <ricxfcpp/xapp.hpp>
+     long m1000_count = 0;    // message counters, one for each type
+     long m1001_count = 0;
+
+     // callback function that will increase the appropriate counter
+     void cbf( Message& mbuf, int mtype, int subid, int len,
+                 Msg_component payload,  void* data ) {
+         long* counter;
+
+         if( (counter = (long *) data) != NULL ) {
+             (*counter)++;
+         }
+     }
+
+     int main( ) {
+         std::unique_ptr<Xapp> xapp;
+         char* listen_port = (char *) "4560";
+         bool  wait4table = false;
+
+         xapp = std::unique_ptr<Xapp>(
+               new Xapp( listen_port, wait4table ) );
 
-      Figure 3: Callback function example.
+         // register the same callback function for both msg types
+         xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
+         xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
 
-      As before, the program does nothing useful, but now it will
-      execute and receive messages. For this example, the same
-      function can be used to increment the appropriate counter
-      simply by providing a pointer to the counter as the user data
-      when the callback function is registered. In addition, a
-      subtle change from the previous example has been to set the
-      wait for table flag to false.
+         xapp->Run( 1 );        // start the callback driver
+     }
 
-      For an xApp that is a receive only application (never sends)
-      it is not necessary to wait for RMR to receive a table from
-      the Route Manager.
+Figure 3: Callback function example.
+
+As before, the program does nothing useful, but now it will
+execute and receive messages. For this example, the same
+function can be used to increment the appropriate counter
+simply by providing a pointer to the counter as the user data
+when the callback function is registered. In addition, a
+subtle change from the previous example has been to set the
+wait for table flag to ``false.``
+
+For an xApp that is a receive only application (never sends)
+it is not necessary to wait for RMR to receive a table from
+the Route Manager.
 
 
 Registering A Default Callback
 ------------------------------
 
-      The xApp may also register a default callback function such
-      that the function will be invoked for any message that does
-      not have a registered callback. If the xAPP does not register
-      a default callback, any message which cannot be mapped to a
-      known callback function is silently dropped. A default
-      callback is registered by providing a *generic* message type
-      of xapp->DEFAULT_CALLBACK on an Add_msg_cb call.
+The xApp may also register a default callback function such
+that the function will be invoked for any message that does
+not have a registered callback. If the xAPP does not register
+a default callback, any message which cannot be mapped to a
+known callback function is silently dropped. A default
+callback is registered by providing a *generic* message type
+of ``xapp->DEFAULT_CALLBACK`` on an ``Add_msg_cb`` call.
 
 
 The Framework Callback Driver
 -----------------------------
 
-      The Run() function within the Xapp object is invoked to start
-      the callback driver, and the xApp should not expect the
-      function to return under most circumstances. The only
-      parameter that the Run() function expects is the number of
-      threads to start. For each thread requested, the framework
-      will start a listener thread which will allow received
-      messages to be processed in parallel. If supplying a value
-      greater than one, the xApp must ensure that the callback
-      functions are thread safe as it is very likely that the same
-      callback function will be invoked concurrently from multiple
-      threads.
+The ``Run()`` function within the Xapp object is invoked to
+start the callback driver, and the xApp should not expect the
+function to return under most circumstances. The only
+parameter that the ``Run()`` function expects is the number
+of threads to start. For each thread requested, the framework
+will start a listener thread which will allow received
+messages to be processed in parallel. If supplying a value
+greater than one, the xApp must ensure that the callback
+functions are thread safe as it is very likely that the same
+callback function will be invoked concurrently from multiple
+threads.
 
 
 SENDING MESSAGES
 ================
 
-      It is very likely that most xApps will need to send messages
-      and will not operate in "receive only" mode. Sending the
-      message is a function of the message object itself and can
-      take one of two forms:
+It is very likely that most xApps will need to send messages
+and will not operate in "receive only" mode. Sending the
+message is a function of the message object itself and can
+take one of two forms:
 
 
-      + Replying to the sender of a received message
+  * Replying to the sender of a received message
 
-      + Sending a message (routed based on the message type and subscription ID)
+  * Sending a message (routed based on the message type and
+    subscription ID)
 
 
-      When replying to the sender, the message type and
-      subscription ID are not used to determine the destination of
-      the message; RMR ensures that the message is sent back to the
-      originating xApp. The xApp may still need to change the
-      message type and/or the subscription ID in the message prior
-      to using the reply function.
+When replying to the sender, the message type and
+subscription ID are not used to determine the destination of
+the message; RMR ensures that the message is sent back to the
+originating xApp. The xApp may still need to change the
+message type and/or the subscription ID in the message prior
+to using the reply function.
 
-      To provide for both situations, two reply functions are
-      supported by the Message object as illustrated with the
-      following prototypes.
+To provide for both situations, two reply functions are
+supported by the Message object as illustrated with the
+following prototypes.
 
 
-      ::
+::
 
-           bool Send_response(  int mtype, int subid, int response_len,
-                std:shared_ptr<unsigned char> response );
+    bool Send_response(  int mtype, int subid, int response_len,
+         std:shared_ptr<unsigned char> response );
 
-           bool Send_response(  int response_len, std::shared_ptr<unsigned char> response );
+    bool Send_response(  int response_len, std::shared_ptr<unsigned char> response );
 
-      Figure 4: Reply function prototypes.
+Figure 4: Reply function prototypes.
 
-      In the first prototype the xApp must supply the new message
-      type and subscription ID values, where the second function
-      uses the values which are currently set in the message.
-      Further, the new payload contents, and length, are supplied
-      to both functions; the framework ensures that the message is
-      large enough to accommodate the payload, reallocating it if
-      necessary, and copies the response into the message payload
-      prior to sending. Should the xApp need to change either the
-      message type, or the subscription ID, but not both, the
-      NO_CHANGE constant can be used as illustrated below.
+In the first prototype the xApp must supply the new message
+type and subscription ID values, where the second function
+uses the values which are currently set in the message.
+Further, the new payload contents, and length, are supplied
+to both functions; the framework ensures that the message is
+large enough to accommodate the payload, reallocating it if
+necessary, and copies the response into the message payload
+prior to sending. Should the xApp need to change either the
+message type, or the subscription ID, but not both, the
+``NO_CHANGE`` constant can be used as illustrated below.
 
 
-      ::
+::
 
-            msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID,
-                pl_length, (unsigned char *) payload );
+     msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID,
+         pl_length, (unsigned char *) payload );
 
-      Figure 5: Send response prototype.
+Figure 5: Send response prototype.
 
-      In addition to the two function prototypes for
-      Send_response() there are two additional prototypes which
-      allow the new payload to be supplied as a shared smart
-      pointer. The other parameters to these functions are
-      identical to those illustrated above, and thus are not
-      presented here.
+In addition to the two function prototypes for
+``Send_response()`` there are two additional prototypes which
+allow the new payload to be supplied as a shared smart
+pointer. The other parameters to these functions are
+identical to those illustrated above, and thus are not
+presented here.
 
-      The Send_msg() set of functions supported by the Message
-      object are identical to the Send_response() functions and are
-      shown below.
+The ``Send_msg()`` set of functions supported by the Message
+object are identical to the ``Send_response()`` functions and
+are shown below.
 
 
-      ::
+::
 
-            bool Send_msg( int mtype, int subid, int payload_len,
-                std::shared_ptr<unsigned char> payload );
+     bool Send_msg( int mtype, int subid, int payload_len,
+         std::shared_ptr<unsigned char> payload );
 
-            bool Send_msg( int mtype, int subid, int payload_len,
-                unsigned char* payload );
+     bool Send_msg( int mtype, int subid, int payload_len,
+         unsigned char* payload );
 
-            bool Send_msg( int payload_len,
-                std::shared_ptr<unsigned char> payload );
+     bool Send_msg( int payload_len,
+         std::shared_ptr<unsigned char> payload );
 
-            bool Send_msg( int payload_len, unsigned char* payload );
+     bool Send_msg( int payload_len, unsigned char* payload );
 
-      Figure 6: Send function prototypes.
+Figure 6: Send function prototypes.
 
-      Each send function accepts the message, copies in the payload
-      provided, sets the message type and subscription ID (if
-      provided), and then causes the message to be sent. The only
-      difference between the Send_msg() and Send_response()
-      functions is that the destination of the message is selected
-      based on the mapping of the message type and subscription ID
-      using the current routing table known to RMR.
+Each send function accepts the message, copies in the payload
+provided, sets the message type and subscription ID (if
+provided), and then causes the message to be sent. The only
+difference between the ``Send_msg()`` and
+``Send_response()`` functions is that the destination of the
+message is selected based on the mapping of the message type
+and subscription ID using the current routing table known to
+RMR.
 
 
 Direct Payload Manipulation
 ---------------------------
 
-      For some applications, it might be more efficient to
-      manipulate the payload portion of an Xapp Message in place,
-      rather than creating it and relying on a buffer copy when the
-      message is finally sent. To achieve this, the xApp must
-      either use the smart pointer to the payload passed to the
-      callback function, or retrieve one from the message using
-      Get_payload() when working with a message outside of a
-      callback function. Once the smart pointer is obtained, the
-      pointer's get() function can be used to directly reference
-      the payload (unsigned char) bytes.
-
-      When working directly with the payload, the xApp must take
-      care not to write more than the actual payload size which can
-      be extracted from the Message object using the
-      Get_available_size() function.
-
-      When sending a message where the payload has been directly
-      altered, and no extra buffer copy is needed, a NULL pointer
-      should be passed to the Message send function. The following
-      illustrates how the payload can be directly manipulated and
-      returned to the sender (for simplicity, there is no error
-      handling if the payload size of the received message isn't
-      large enough for the response string, the response is just
-      not sent).
-
-
-      ::
-
-            Msg_component payload;  // smart reference
-            int pl_size;            // max size of payload
+For some applications, it might be more efficient to
+manipulate the payload portion of an Xapp Message in place,
+rather than creating it and relying on a buffer copy when the
+message is finally sent. To achieve this, the xApp must
+either use the smart pointer to the payload passed to the
+callback function, or retrieve one from the message using
+``Get_payload()`` when working with a message outside of a
+callback function. Once the smart pointer is obtained, the
+pointer's get() function can be used to directly reference
+the payload (unsigned char) bytes.
+
+When working directly with the payload, the xApp must take
+care not to write more than the actual payload size which can
+be extracted from the Message object using the
+``Get_available_size()`` function.
+
+When sending a message where the payload has been directly
+altered, and no extra buffer copy is needed, a NULL pointer
+should be passed to the Message send function. The following
+illustrates how the payload can be directly manipulated and
+returned to the sender (for simplicity, there is no error
+handling if the payload size of the received message isn't
+large enough for the response string, the response is just
+not sent).
 
-            payload = msg->Get_payload();
-            pl_size = msg->Get_available_size();
-            if( snprintf( (char *) payload.get(), pl_size,
-                "Msg Received\\n" ) < pl_size ) {
-              msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
-            }
 
-      Figure 7: Send message without buffer copy.
+::
+
+     Msg_component payload;  // smart reference
+     int pl_size;            // max size of payload
+
+     payload = msg->Get_payload();
+     pl_size = msg->Get_available_size();
+     if( snprintf( (char *) payload.get(), pl_size,
+         "Msg Received\\n" ) < pl_size ) {
+       msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
+     }
+
+Figure 7: Send message without buffer copy.
 
 
 
 Sending Multiple Responses
 --------------------------
 
-      It is likely that the xApp will wish to send multiple
-      responses back to the process that sent a message that
-      triggered the callback. The callback function may invoke the
-      Send_response() function multiple times before returning.
+It is likely that the xApp will wish to send multiple
+responses back to the process that sent a message that
+triggered the callback. The callback function may invoke the
+``Send_response()`` function multiple times before returning.
 
-      After each call, the Message retains the necessary
-      information to allow for a subsequent invocation to send more
-      data. It should be noted though, that after the first call to
-      {Send_response() the original payload will be lost; if
-      necessary, the xApp must make a copy of the payload before
-      the first response call is made.
+After each call, the Message retains the necessary
+information to allow for a subsequent invocation to send more
+data. It should be noted though, that after the first call to
+``{Send_response()`` the original payload will be lost; if
+necessary, the xApp must make a copy of the payload before
+the first response call is made.
 
 
 Message Allocation
 ------------------
 
-      Not all xApps will be "responders," meaning that some xApps
-      will need to send one or more messages before they can expect
-      to receive any messages back. To accomplish this, the xApp
-      must first allocate a message buffer, optionally initialising
-      the payload, and then using the message's Send_msg() function
-      to send a message out. The framework's Alloc_msg() function
-      can be used to create a Message object with a desired payload
-      size.
+Not all xApps will be "responders," meaning that some xApps
+will need to send one or more messages before they can expect
+to receive any messages back. To accomplish this, the xApp
+must first allocate a message buffer, optionally initialising
+the payload, and then using the message's ``Send_msg()``
+function to send a message out. The framework's
+``Alloc_msg()`` function can be used to create a Message
+object with a desired payload size.
 
 
 FRAMEWORK PROVIDED CALLBACKS
 ============================
 
-      The framework itself may provide message handling via the
-      driver such that the xApp might not need to implement some
-      message processing functionality. Initially, the C++
-      framework will provide a default callback function to handle
-      the RMR based health check messages. This callback function
-      will assume that if the message was received, and the
-      callback invoked, that all is well and will reply with an OK
-      state. If the xApp should need to override this simplistic
-      response, all it needs to do is to register its own callback
-      function for the health check message type.
+The framework itself may provide message handling via the
+driver such that the xApp might not need to implement some
+message processing functionality. Initially, the C++
+framework will provide a default callback function to handle
+the RMR based health check messages. This callback function
+will assume that if the message was received, and the
+callback invoked, that all is well and will reply with an OK
+state. If the xApp should need to override this simplistic
+response, all it needs to do is to register its own callback
+function for the health check message type.
 
 
-EXAMPLE PROGRAMMES
-==================
+JSON SUPPORT
+============
 
-      The following sections contain several example programmes
-      which are written on top of the C++ framework.
+The C++ xAPP framework provides a very lightweight json
+parser and data hash facility. Briefly, a json hash (Jhash)
+can be established by creating an instance of the Jhash
+object with a string of valid json. The resulting object's
+functions can then be used to read values from the resulting
+hash.
 
 
-RMR Dump xAPP
--------------
+Creating The Jhash Object
+-------------------------
 
-      The RMR dump application is an example built on top of the
-      C++ xApp framework to both illustrate the use of the
-      framework, and to provide a useful diagnostic tool when
-      testing and troubleshooting xApps.
-
-      The RMR dump xApp isn't a traditional xApp inasmuch as its
-      goal is to listen for message types and to dump information
-      about the messages received to the TTY much as tcpdump does
-      for raw packet traffic. The full source code, and Makefile,
-      are in the examples directory of the C++ framework repo.
-
-      When invoked, the RMR dump program is given one or more
-      message types to listen for. A callback function is
-      registered for each, and the framework Run() function is
-      invoked to drive the process. For each recognised message,
-      and depending on the verbosity level supplied at program
-      start, information about the received message(s) is written
-      to the TTY. If the forwarding option, -f, is given on the
-      command line, and an appropriate route table is provided,
-      each received message is forwarded without change. This
-      allows for the insertion of the RMR dump program into a flow,
-      however if the ultimate receiver of a message needs to reply
-      to that message, the reply will not reach the original
-      sender, so RMR dump is not a complete "middle box"
-      application.
-
-      The following is the code for this xAPP. Several functions,
-      which provide logic unrelated to the framework, have been
-      omitted. The full code is in the framework repository.
-
-
-
-      ::
-
-        #include <stdio.h>
-        #include <unistd.h>
-        #include <atomic>
-
-        #include "ricxfcpp/xapp.hpp"
-
-        /*
-            Information that the callback needs outside
-            of what is given to it via parms on a call
-            by the framework.
-        */
-        typedef struct {
-            int        vlevel;             // verbosity level
-            bool    forward;            // if true, message is forwarded
-            int        stats_freq;         // header/stats after n messages
-            std::atomic<long>    pcount; // messages processed
-            std::atomic<long>    icount; // messages ignored
-            std::atomic<int>    hdr;    // number of messages before next header
-        } cb_info_t;
-
-        // ----------------------------------------------------------------------
-
-        /*
-            Dump bytes to tty.
-        */
-        void dump( unsigned const char* buf, int len ) {
-            int        i;
-            int        j;
-            char    cheater[17];
-
-            fprintf( stdout, "<RD> 0000 | " );
-            j = 0;
-            for( i = 0; i < len; i++ ) {
-                cheater[j++] =  isprint( buf[i] ) ? buf[i] : '.';
-                fprintf( stdout, "%02x ", buf[i] );
-
-                if( j == 16 ) {
-                    cheater[j] = 0;
-                    fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
-                    j = 0;
-                }
-            }
+The Jhash object is created simply by passing a json string
+to the constructor.
 
-            if( j ) {
-                i = 16 - (i % 16);
-                for( ; i > 0; i-- ) {
-                    fprintf( stdout, "   " );
-                }
-                cheater[j] = 0;
-                fprintf( stdout, " | %s\\n", cheater );
-            }
-        }
+::
 
-        /*
-            generate stats when the hdr count reaches 0. Only one active
-            thread will ever see it be exactly 0, so this is thread safe.
-        */
-        void stats( cb_info_t& cbi ) {
-            int curv;                    // current stat trigger value
+     #include <ricxfcpp/Jhash>
 
-            curv = cbi.hdr--;
+     std::string jstring = "{ \\"tag\\": \\"Hello World\\" }";
+     Jhash*  jh;
 
-            if( curv == 0 ) {                    // stats when we reach 0
-                fprintf( stdout, "ignored: %ld  processed: %ld\\n",
-                    cbi.icount.load(), cbi.pcount.load() );
-                if( cbi.vlevel > 0 ) {
-                    fprintf( stdout, "\\n     %5s %5s %2s %5s\\n",
-                        "MTYPE", "SUBID", "ST", "PLLEN" );
-                }
+     jh =  new Jhash( jstring.c_str() );
 
-                cbi.hdr = cbi.stats_freq;        // reset must be last
-            }
-        }
+Figure 8: The creation of the Jhash object.
 
-        void cb1( Message& mbuf, int mtype, int subid, int len,
-                        Msg_component payload,  void* data ) {
-            cb_info_t*    cbi;
-            long total_count;
+Once the Jhash object has been created any of the methods
+described in the following paragraphs can be used to retrieve
+the data:
 
-            if( (cbi = (cb_info_t *) data) == NULL ) {
-                return;
-            }
 
-            cbi->pcount++;
-            stats( *cbi );            // gen stats & header if needed
+Json Blobs
+----------
 
-            if( cbi->vlevel > 0 ) {
-                fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
-                        mtype, subid, mbuf.Get_state(), len );
+Json objects can be nested, and the nesting is supported by
+this representation. The approach taken by Jhash is a
+"directory view" approach, where the "current directory," or
+current *blob,* limits the scope of visible fields.
 
-                if( cbi->vlevel > 1 ) {
-                    dump(  payload.get(), len > 64 ? 64 : len );
-                }
-            }
+As an example, the json contained in figure jblob_fig,
+contains a "root" blob and two *sub-blobs* (address and
+lease_info).
 
-            if( cbi->forward ) {
-                // forward with no change to len or payload
-                mbuf.Send_msg( Message::NO_CHANGE, NULL );
-            }
-        }
+::
 
-        /*
-            registered as the default callback; it counts the
-            messages that we aren't giving details about.
-        */
-        void cbd( Message& mbuf, int mtype, int subid, int len,
-                        Msg_component payload,  void* data ) {
-            cb_info_t*    cbi;
+     {
+         "lodge_name": "Water Buffalo Lodge 714",
+         "member_count": 41,
+         "grand_poobah": "Larry K. Slate",
+         "attendance":   [ 23, 14, 41, 38, 24 ],
+         "address": {
+             "street":    "16801 Stonway Lane",
+             "suite":     null,
+             "city":      "Bedrock",
+             "post_code": "45701"
+         },
+         "lease_info": {
+             "owner":    "Stonegate Properties",
+             "amount":   216.49,
+             "due":      "monthly",
+             "contact:"  "Kyle Limestone"
+         }
+     }
 
-            if( (cbi = (cb_info_t *) data) == NULL ) {
-                return;
-            }
+Figure 9: Sample json with a root and too blobs.
 
-            cbi->icount++;
-            stats( *cbi );
+Upon creation of the Jhash object, the *root* fields,
+``lodge_name,`` ``member_count,`` and ``grand_poobah`` are
+immediately available. The fields in the *sub-blobs* are
+avalable only when the correct blob is selected. The code
+sample in figure 10 illustrates how a *sub-blob* is selected.
 
-            if( cbi->forward ) {
-                // forward with no change to len or payload
-                mbuf.Send_msg( Message::NO_CHANGE, NULL );
-            }
-        }
+::
 
-        int main( int argc, char** argv ) {
-            std::unique_ptr<Xapp> x;
-            char*    port = (char *) "4560";
-            int ai = 1;                    // arg processing index
-            cb_info_t*    cbi;
-            int        ncb = 0;            // number of callbacks registered
-            int        mtype;
-            int        nthreads = 1;
-
-            cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
-            cbi->pcount = 0;
-            cbi->icount = 0;
-            cbi->stats_freq = 10;
-
-            ai = 1;
-            // very simple flag parsing (no error/bounds checking)
-            while( ai < argc ) {
-                if( argv[ai][0] != '-' )  {        // break on first non-flag
-                    break;
-                }
-
-                // very simple arg parsing; each must be separate -x -y not -xy.
-                switch( argv[ai][1] ) {
-                    case 'f':                    // enable packet forwarding
-                        cbi->forward = true;
-                        break;
-
-                    case 'p':                     // define port
-                        port = argv[ai+1];
-                        ai++;
-                        break;
-
-                    case 's':                        // stats frequency
-                        cbi->stats_freq = atoi( argv[ai+1] );
-                        if( cbi->stats_freq < 5 ) {    // enforce sanity
-                            cbi->stats_freq = 5;
-                        }
-                        ai++;
-                        break;
-
-                    case 't':                        // thread count
-                        nthreads = atoi( argv[ai+1] );
-                        if( nthreads < 1 ) {
-                            nthreads = 1;
-                        }
-                        ai++;
-                        break;
-
-                    case 'v':            // simple verbose bump
-                        cbi->vlevel++;
-                        break;
-
-                    case 'V':            // explicit verbose level
-                        cbi->vlevel = atoi( argv[ai+1] );
-                        ai++;
-                        break;
-
-                    default:
-                        fprintf( stderr, "unrecognised option: %s\\n", argv[ai] );
-                        fprintf( stderr, "usage: %s [-f] [-p port] "
-                                        "[-s stats-freq]  [-t thread-count] "
-                                        "[-v | -V n] msg-type1 ... msg-typen\\n",
-                                        argv[0] );
-                        fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" );
-                        fprintf( stderr, "\\tverbose levels (-V) 0 counts only, "
-                                        "1 message info 2 payload dump\\n" );
-                        exit( 1 );
-                }
-
-                ai++;
-            }
+     jh->Set_blob( (char *) "address" );     // select address
+     jh->Unset_blob();                       // return to root
+     jh->Set_blob( (char *) "lease_info" );  // slect the lease blob
 
-            cbi->hdr = cbi->stats_freq;
-            fprintf( stderr, "<RD> listening on port: %s\\n", port );
+Figure 10: Blob selection example.
 
-            // create xapp, wait for route table if forwarding
-            x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
+Currently, the selected blob must be unset in order to select
+a blob at the root level; unset always sets the root blob.
+Attempting to use the ``Set_blob`` function will attempt to
+select the named blob from the current blob, and not the
+root.
 
-            // register callback for each type on the command line
-            while( ai < argc ) {
-                mtype = atoi( argv[ai] );
-                ai++;
-                fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
-                x->Add_msg_cb( mtype, cb1, cbi );
-                ncb++;
-            }
 
-            if( ncb < 1 ) {
-                fprintf( stderr, "<RD> no message types specified on the command line\\n" );
-                exit( 1 );
-            }
+Simple Value Extraction
+-----------------------
 
-            x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi );        // register default cb
+Simple values are the expected data types *string, value,*
+and *boolean.* This lightweight json parser treats all values
+as floating point numbers and does not attempt to maintain a
+separate integer type. A fourth type, *null,* is supported to
+allow the user to expressly check for a field which is
+defined but has no value; as opposed to a field that was
+completely missing from the data. The following are the
+prototypes for the functions which allow values to be
+extracted:
 
-            fprintf( stderr, "<RD> starting driver\\n" );
-            x->Run( nthreads );
 
-            // return from run() is not expected, but some compilers might
-            // compilain if there isn't a return value here.
-            return 0;
-        }
+::
 
-      Figure 8: Simple callback application.
+     std::string String( const char* name );
+     float Value( const char* name );
+     bool Bool( const char* name );
 
 
-Callback Receiver
------------------
+Each of these funcitons returns the value associated with the
+field with the given *name.* If the value is missing, the
+following default values are returned:
 
-      This sample programme implements a simple message listener
-      which registers three callback functions to process two
-      specific message types and a default callback to handle
-      unrecognised messages.
 
-      When a message of type 1 is received, it will send two
-      response messages back to the sender. Two messages are sent
-      in order to illustrate that it is possible to send multiple
-      responses using the same received message.
+      .. list-table::
+        :widths: 15,80
+        :header-rows: 0
+        :class: borderless
 
-      The programme illustrates how multiple listening threads can
-      be used, but the programme is **not** thread safe; to keep
-      this example as simple as possible, the counters are not
-      locked when incremented.
+        * - **String**
+          -
+            An empty string (.e.g "").
 
+            |
 
-      ::
+        * - **Value**
+          -
+            Zero (e.g 0.0)
 
-        #include <stdio.h>
+            |
 
-        #include "ricxfcpp/message.hpp"
-        #include "ricxfcpp/msg_component.hpp"
-        #include "ricxfcpp/xapp.hpp"
+        * - **bool**
+          -
+            false
 
-        // counts; not thread safe
-        long cb1_count = 0;
-        long cb2_count = 0;
-        long cbd_count = 0;
 
-        long cb1_lastts = 0;
-        long cb1_lastc = 0;
 
-        // respond with 2 messages for each type 1 received
-        void cb1( Message& mbuf, int mtype, int subid, int len,
-                    Msg_component payload,  void* data ) {
-            long now;
-            long total_count;
+If the user needs to disambiguate between a missing value and
+the default value either the ``Missing`` or ``Exists``
+function should be used first.
+
+
+Testing For Existing and Missing Fields
+---------------------------------------
+
+Two functions allow the developer to determine whether or not
+a field is included in the json. Both of these functions work
+on the current *blob,* therefore it is important to ensure
+that the correct blob is selected before using either of
+these funcitons. The prototpyes for the ``Exists`` and
+``Missing`` functions are below:
+
+::
+
+     bool Exists( const char* name );
+     bool Is_missing( const char* name );
+
+The ``Exists`` function returns *true* if the field name
+exists in the json and *false* otherwise. Conversly, the
+``Missing`` funciton returns *true* when the field name does
+not exist in the json.
+
+
+Testing Field Type
+------------------
+
+The ``Exists`` and ``Missing`` functions might not be enough
+for the user code to validate the data that it has. To assist
+with this, several functions allow direct type testing on a
+field in the current blob. The following are the prototypes
+for these functions:
+
+::
+
+     bool Is_bool( const char* name );
+     bool Is_null( const char* name );
+     bool Is_string( const char* name );
+     bool Is_value( const char* name );
+
+
+Each of these funcitons return *true* if the field with the
+given name is of the type being tested for.
+
+
+Arrays
+------
+
+Arrays are supported in the same manner as simple field
+values with the addition of the need to supply an array index
+when fetching values from the object. In addition, there is a
+*length* function which can be used to determine the number
+of elements in the named array. The prototypes for the array
+based functions are below:
+
+::
+
+     int Array_len( const char* name );
+
+     bool Is_bool_ele( const char* name, int eidx );
+     bool Is_null_ele( const char* name, int eidx );
+     bool Is_string_ele( const char* name, int eidx );
+     bool Is_value_ele( const char* name, int eidx );
+
+     bool Bool_ele( const char* name, int eidx );
+     std::string String_ele( const char* name, int eidx );
+     float Value_ele( const char* name, int eidx );
+
+
+For each of these functions the ``eidx`` is the zero based
+element index which is to be tested or selected.
+
+
+Arrays of Blobs
+---------------
+
+An array containing blobs, rather than simiple field value
+pairs, the blob must be selected prior to using it, just as a
+sub-blob needed to be selected. The ``Set_blob_ele`` function
+is used to do this and has the following prototype:
+
+::
+
+     bool Set_blob_ele( const char* name, int eidx );
+
 
-            // illustrate that we can use the same buffer for 2 rts calls
-            mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" );
-            mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
+As with selecting a sub-blob, an unset must be preformed
+before selecting the next blob. Figure 11 illustrates how
+these functions can be used to read and print values from the
+json in figure 12.
 
-            cb1_count++;
+::
+
+     "members": [
+         { "name": "Fred Flinstone", "member_num": 42 },
+         { "name": "Barney Rubble", "member_num": 48 },
+         { "name": "Larry K Slate", "member_num": 22 },
+         { "name": "Kyle Limestone", "member_num": 49 }
+     ]
+
+Figure 11: Json array containing blobs.
+
+
+::
+
+     std::string mname;
+     float mnum;
+     int len;
+
+     len = jh->Array_len( (char *) "members" );
+     for( i = 0; i < len; i++ ) {
+         jh->Set_blob_ele( (char *) "members", i );  // select blob
+
+         mname = jh->String( (char *) "name" );      // read values
+         mnum = jh->Value( (char *) "member_num" );
+         fprintf( stdout, "%s is member %d\\n", mname.c_str(), (int) mnum );
+
+         jh->Unset_blob();                           // back to root
+     }
+
+Figure 12: Code to process the array of blobs.
+
+
+
+EXAMPLE PROGRAMMES
+==================
+
+The following sections contain several example programmes
+which are written on top of the C++ framework.
+
+
+RMR Dump xAPP
+-------------
+
+The RMR dump application is an example built on top of the
+C++ xApp framework to both illustrate the use of the
+framework, and to provide a useful diagnostic tool when
+testing and troubleshooting xApps.
+
+The RMR dump xApp isn't a traditional xApp inasmuch as its
+goal is to listen for message types and to dump information
+about the messages received to the TTY much as
+``tcpdump`` does for raw packet traffic. The full source
+code, and Makefile, are in the ``examples`` directory of the
+C++ framework repo.
+
+When invoked, the RMR dump program is given one or more
+message types to listen for. A callback function is
+registered for each, and the framework ``Run()`` function is
+invoked to drive the process. For each recognised message,
+and depending on the verbosity level supplied at program
+start, information about the received message(s) is written
+to the TTY. If the forwarding option, -f, is given on the
+command line, and an appropriate route table is provided,
+each received message is forwarded without change. This
+allows for the insertion of the RMR dump program into a flow,
+however if the ultimate receiver of a message needs to reply
+to that message, the reply will not reach the original
+sender, so RMR dump is not a complete "middle box"
+application.
+
+The following is the code for this xAPP. Several functions,
+which provide logic unrelated to the framework, have been
+omitted. The full code is in the framework repository.
+
+
+
+  ::
+
+    #include <stdio.h>
+    #include <unistd.h>
+    #include <atomic>
+
+    #include "ricxfcpp/xapp.hpp"
+
+    /*
+        Information that the callback needs outside
+        of what is given to it via parms on a call
+        by the framework.
+    */
+    typedef struct {
+        int        vlevel;             // verbosity level
+        bool    forward;            // if true, message is forwarded
+        int        stats_freq;         // header/stats after n messages
+        std::atomic<long>    pcount; // messages processed
+        std::atomic<long>    icount; // messages ignored
+        std::atomic<int>    hdr;    // number of messages before next header
+    } cb_info_t;
+
+    // ----------------------------------------------------------------------
+
+    /*
+        Dump bytes to tty.
+    */
+    void dump( unsigned const char* buf, int len ) {
+        int        i;
+        int        j;
+        char    cheater[17];
+
+        fprintf( stdout, "<RD> 0000 | " );
+        j = 0;
+        for( i = 0; i < len; i++ ) {
+            cheater[j++] =  isprint( buf[i] ) ? buf[i] : '.';
+            fprintf( stdout, "%02x ", buf[i] );
+
+            if( j == 16 ) {
+                cheater[j] = 0;
+                fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
+                j = 0;
+            }
         }
 
-        // just count messages
-        void cb2( Message& mbuf, int mtype, int subid, int len,
+        if( j ) {
+            i = 16 - (i % 16);
+            for( ; i > 0; i-- ) {
+                fprintf( stdout, "   " );
+            }
+            cheater[j] = 0;
+            fprintf( stdout, " | %s\\n", cheater );
+        }
+    }
+
+    /*
+        generate stats when the hdr count reaches 0. Only one active
+        thread will ever see it be exactly 0, so this is thread safe.
+    */
+    void stats( cb_info_t& cbi ) {
+        int curv;                    // current stat trigger value
+
+        curv = cbi.hdr--;
+
+        if( curv == 0 ) {                    // stats when we reach 0
+            fprintf( stdout, "ignored: %ld  processed: %ld\\n",
+                cbi.icount.load(), cbi.pcount.load() );
+            if( cbi.vlevel > 0 ) {
+                fprintf( stdout, "\\n     %5s %5s %2s %5s\\n",
+                    "MTYPE", "SUBID", "ST", "PLLEN" );
+            }
+
+            cbi.hdr = cbi.stats_freq;        // reset must be last
+        }
+    }
+
+    void cb1( Message& mbuf, int mtype, int subid, int len,
                     Msg_component payload,  void* data ) {
-            cb2_count++;
+        cb_info_t*    cbi;
+        long total_count;
+
+        if( (cbi = (cb_info_t *) data) == NULL ) {
+            return;
         }
 
-        // default to count all unrecognised messages
-        void cbd( Message& mbuf, int mtype, int subid, int len,
+        cbi->pcount++;
+        stats( *cbi );            // gen stats & header if needed
+
+        if( cbi->vlevel > 0 ) {
+            fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
+                    mtype, subid, mbuf.Get_state(), len );
+
+            if( cbi->vlevel > 1 ) {
+                dump(  payload.get(), len > 64 ? 64 : len );
+            }
+        }
+
+        if( cbi->forward ) {
+            // forward with no change to len or payload
+            mbuf.Send_msg( Message::NO_CHANGE, NULL );
+        }
+    }
+
+    /*
+        registered as the default callback; it counts the
+        messages that we aren't giving details about.
+    */
+    void cbd( Message& mbuf, int mtype, int subid, int len,
                     Msg_component payload,  void* data ) {
-            cbd_count++;
+        cb_info_t*    cbi;
+
+        if( (cbi = (cb_info_t *) data) == NULL ) {
+            return;
+        }
+
+        cbi->icount++;
+        stats( *cbi );
+
+        if( cbi->forward ) {
+            // forward with no change to len or payload
+            mbuf.Send_msg( Message::NO_CHANGE, NULL );
         }
+    }
+
+    int main( int argc, char** argv ) {
+        std::unique_ptr<Xapp> x;
+        char*    port = (char *) "4560";
+        int ai = 1;                    // arg processing index
+        cb_info_t*    cbi;
+        int        ncb = 0;            // number of callbacks registered
+        int        mtype;
+        int        nthreads = 1;
+
+        cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
+        cbi->pcount = 0;
+        cbi->icount = 0;
+        cbi->stats_freq = 10;
+
+        ai = 1;
+        // very simple flag parsing (no error/bounds checking)
+        while( ai < argc ) {
+            if( argv[ai][0] != '-' )  {        // break on first non-flag
+                break;
+            }
+
+            // very simple arg parsing; each must be separate -x -y not -xy.
+            switch( argv[ai][1] ) {
+                case 'f':                    // enable packet forwarding
+                    cbi->forward = true;
+                    break;
 
-        int main( int argc, char** argv ) {
-            Xapp* x;
-            char*    port = (char *) "4560";
-            int ai = 1;                            // arg processing index
-            int nthreads = 1;
+                case 'p':                     // define port
+                    port = argv[ai+1];
+                    ai++;
+                    break;
+
+                case 's':                        // stats frequency
+                    cbi->stats_freq = atoi( argv[ai+1] );
+                    if( cbi->stats_freq < 5 ) {    // enforce sanity
+                        cbi->stats_freq = 5;
+                    }
+                    ai++;
+                    break;
 
-            // very simple flag processing (no bounds/error checking)
-            while( ai < argc ) {
-                if( argv[ai][0] != '-' )  {
+                case 't':                        // thread count
+                    nthreads = atoi( argv[ai+1] );
+                    if( nthreads < 1 ) {
+                        nthreads = 1;
+                    }
+                    ai++;
                     break;
-                }
 
-                switch( argv[ai][1] ) {            // we only support -x so -xy must be -x -y
-                    case 'p':
-                        port = argv[ai+1];
-                        ai++;
-                        break;
+                case 'v':            // simple verbose bump
+                    cbi->vlevel++;
+                    break;
 
-                    case 't':
-                        nthreads = atoi( argv[ai+1] );
-                        ai++;
-                        break;
-                }
+                case 'V':            // explicit verbose level
+                    cbi->vlevel = atoi( argv[ai+1] );
+                    ai++;
+                    break;
 
-                ai++;
+                default:
+                    fprintf( stderr, "unrecognised option: %s\\n", argv[ai] );
+                    fprintf( stderr, "usage: %s [-f] [-p port] "
+                                    "[-s stats-freq]  [-t thread-count] "
+                                    "[-v | -V n] msg-type1 ... msg-typen\\n",
+                                    argv[0] );
+                    fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" );
+                    fprintf( stderr, "\\tverbose levels (-V) 0 counts only, "
+                                    "1 message info 2 payload dump\\n" );
+                    exit( 1 );
             }
 
-            fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
-            fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
+            ai++;
+        }
 
-            x = new Xapp( port, true );
-            x->Add_msg_cb( 1, cb1, NULL );                // register callbacks
-            x->Add_msg_cb( 2, cb2, NULL );
-            x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
+        cbi->hdr = cbi->stats_freq;
+        fprintf( stderr, "<RD> listening on port: %s\\n", port );
 
-            x->Run( nthreads );                // let framework drive
-            // control should not return
+        // create xapp, wait for route table if forwarding
+        x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
+
+        // register callback for each type on the command line
+        while( ai < argc ) {
+            mtype = atoi( argv[ai] );
+            ai++;
+            fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
+            x->Add_msg_cb( mtype, cb1, cbi );
+            ncb++;
+        }
+
+        if( ncb < 1 ) {
+            fprintf( stderr, "<RD> no message types specified on the command line\\n" );
+            exit( 1 );
         }
 
-      Figure 9: Simple callback application.
+        x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi );        // register default cb
+
+        fprintf( stderr, "<RD> starting driver\\n" );
+        x->Run( nthreads );
+
+        // return from run() is not expected, but some compilers might
+        // compilain if there isn't a return value here.
+        return 0;
+    }
+
+  Figure 13: Simple callback application.
+
+
+Callback Receiver
+-----------------
+
+This sample programme implements a simple message listener
+which registers three callback functions to process two
+specific message types and a default callback to handle
+unrecognised messages.
+
+When a message of type 1 is received, it will send two
+response messages back to the sender. Two messages are sent
+in order to illustrate that it is possible to send multiple
+responses using the same received message.
+
+The programme illustrates how multiple listening threads can
+be used, but the programme is **not** thread safe; to keep
+this example as simple as possible, the counters are not
+locked when incremented.
+
+
+  ::
+
+    #include <stdio.h>
+
+    #include "ricxfcpp/message.hpp"
+    #include "ricxfcpp/msg_component.hpp"
+    #include "ricxfcpp/xapp.hpp"
+
+    // counts; not thread safe
+    long cb1_count = 0;
+    long cb2_count = 0;
+    long cbd_count = 0;
+
+    long cb1_lastts = 0;
+    long cb1_lastc = 0;
+
+    // respond with 2 messages for each type 1 received
+    void cb1( Message& mbuf, int mtype, int subid, int len,
+                Msg_component payload,  void* data ) {
+        long now;
+        long total_count;
+
+        // illustrate that we can use the same buffer for 2 rts calls
+        mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" );
+        mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
+
+        cb1_count++;
+    }
+
+    // just count messages
+    void cb2( Message& mbuf, int mtype, int subid, int len,
+                Msg_component payload,  void* data ) {
+        cb2_count++;
+    }
+
+    // default to count all unrecognised messages
+    void cbd( Message& mbuf, int mtype, int subid, int len,
+                Msg_component payload,  void* data ) {
+        cbd_count++;
+    }
+
+    int main( int argc, char** argv ) {
+        Xapp* x;
+        char*    port = (char *) "4560";
+        int ai = 1;                            // arg processing index
+        int nthreads = 1;
+
+        // very simple flag processing (no bounds/error checking)
+        while( ai < argc ) {
+            if( argv[ai][0] != '-' )  {
+                break;
+            }
+
+            switch( argv[ai][1] ) {            // we only support -x so -xy must be -x -y
+                case 'p':
+                    port = argv[ai+1];
+                    ai++;
+                    break;
+
+                case 't':
+                    nthreads = atoi( argv[ai+1] );
+                    ai++;
+                    break;
+            }
+
+            ai++;
+        }
+
+        fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
+        fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
+
+        x = new Xapp( port, true );
+        x->Add_msg_cb( 1, cb1, NULL );                // register callbacks
+        x->Add_msg_cb( 2, cb2, NULL );
+        x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
+
+        x->Run( nthreads );                // let framework drive
+        // control should not return
+    }
+
+  Figure 14: Simple callback application.
 
 
 
 Looping Sender
 --------------
 
-      This is another very simple application which demonstrates
-      how an application can control its own listen loop while
-      sending messages. As with the other examples, some error
-      checking is skipped, and short cuts have been made in order
-      to keep the example small and to the point.
+This is another very simple application which demonstrates
+how an application can control its own listen loop while
+sending messages. As with the other examples, some error
+checking is skipped, and short cuts have been made in order
+to keep the example small and to the point.
+
 
+  ::
 
-      ::
 
+    #include <stdio.h>
+    #include <string.h>
+    #include <unistd.h>
 
-        #include <stdio.h>
-        #include <string.h>
-        #include <unistd.h>
+    #include <iostream>
+    #include <memory>
 
-        #include <iostream>
-        #include <memory>
+    #include "ricxfcpp/xapp.hpp"
 
-        #include "ricxfcpp/xapp.hpp"
+    extern int main( int argc, char** argv ) {
+        std::unique_ptr<Xapp> xfw;
+        std::unique_ptr<Message> msg;
+        Msg_component payload;                // special type of unique pointer to the payload
 
-        extern int main( int argc, char** argv ) {
-            std::unique_ptr<Xapp> xfw;
-            std::unique_ptr<Message> msg;
-            Msg_component payload;                // special type of unique pointer to the payload
+        int    sz;
+        int len;
+        int i;
+        int ai;
+        int response_to = 0;                // max timeout wating for a response
+        char*    port = (char *) "4555";
+        int    mtype = 0;
+        int rmtype;                            // received message type
+        int delay = 1000000;                // mu-sec delay; default 1s
 
-            int    sz;
-            int len;
-            int i;
-            int ai;
-            int response_to = 0;                // max timeout wating for a response
-            char*    port = (char *) "4555";
-            int    mtype = 0;
-            int rmtype;                            // received message type
-            int delay = 1000000;                // mu-sec delay; default 1s
 
+        // very simple flag processing (no bounds/error checking)
+        while( ai < argc ) {
+            if( argv[ai][0] != '-' )  {
+                break;
+            }
+
+            // we only support -x so -xy must be -x -y
+            switch( argv[ai][1] ) {
+                // delay between messages (mu-sec)
+                case 'd':
+                    delay = atoi( argv[ai+1] );
+                    ai++;
+                    break;
 
-            // very simple flag processing (no bounds/error checking)
-            while( ai < argc ) {
-                if( argv[ai][0] != '-' )  {
+                case 'p':
+                    port = argv[ai+1];
+                    ai++;
+                    break;
+
+                // timeout in seconds; we need to convert to ms for rmr calls
+                case 't':
+                    response_to = atoi( argv[ai+1] ) * 1000;
+                    ai++;
                     break;
-                }
-
-                // we only support -x so -xy must be -x -y
-                switch( argv[ai][1] ) {
-                    // delay between messages (mu-sec)
-                    case 'd':
-                        delay = atoi( argv[ai+1] );
-                        ai++;
-                        break;
-
-                    case 'p':
-                        port = argv[ai+1];
-                        ai++;
-                        break;
-
-                    // timeout in seconds; we need to convert to ms for rmr calls
-                    case 't':
-                        response_to = atoi( argv[ai+1] ) * 1000;
-                        ai++;
-                        break;
-                }
-                ai++;
             }
+            ai++;
+        }
+
+        fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
+        fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
+
+        // get an instance and wait for a route table to be loaded
+        xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
+        msg = xfw->Alloc_msg( 2048 );
 
-            fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
-            fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
+        for( i = 0; i < 100; i++ ) {
+            mtype++;
+            if( mtype > 10 ) {
+                mtype = 0;
+            }
 
-            // get an instance and wait for a route table to be loaded
-            xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
-            msg = xfw->Alloc_msg( 2048 );
+            // we'll reuse a received message; get max size
+            sz = msg->Get_available_size();
 
-            for( i = 0; i < 100; i++ ) {
-                mtype++;
-                if( mtype > 10 ) {
-                    mtype = 0;
-                }
+            // direct access to payload; add something silly
+            payload = msg->Get_payload();
+            len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
 
-                // we'll reuse a received message; get max size
-                sz = msg->Get_available_size();
+            // payload updated in place, prevent copy by passing nil
+            if ( ! msg->Send_msg( mtype, Message::NO_SUBID,  len, NULL )) {
+                fprintf( stderr, "<SNDR> send failed: %d\\n", i );
+            }
 
-                // direct access to payload; add something silly
+            // receive anything that might come back
+            msg = xfw->Receive( response_to );
+            if( msg != NULL ) {
+                rmtype = msg->Get_mtype();
                 payload = msg->Get_payload();
-                len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
-
-                // payload updated in place, prevent copy by passing nil
-                if ( ! msg->Send_msg( mtype, Message::NO_SUBID,  len, NULL )) {
-                    fprintf( stderr, "<SNDR> send failed: %d\\n", i );
-                }
-
-                // receive anything that might come back
-                msg = xfw->Receive( response_to );
-                if( msg != NULL ) {
-                    rmtype = msg->Get_mtype();
-                    payload = msg->Get_payload();
-                    fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
-                        rmtype, (char *) payload.get() );
-                } else {
-                    msg = xfw->Alloc_msg( 2048 );
-                }
-
-                if( delay > 0 ) {
-                    usleep( delay );
-                }
+                fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
+                    rmtype, (char *) payload.get() );
+            } else {
+                msg = xfw->Alloc_msg( 2048 );
+            }
+
+            if( delay > 0 ) {
+                usleep( delay );
             }
         }
+    }
 
-      Figure 10: Simple looping sender application.
+  Figure 15: Simple looping sender application.
 
index 14a0486..7f4a446 100644 (file)
@@ -45,8 +45,8 @@
        suss out the values.
 */
 Jhash::Jhash( const char* jbuf ) :
-       st( jw_new( jbuf ) ),
-       master_st( NULL )
+       master_st( NULL ),
+       st( jw_new( jbuf ) )
 { /* empty body */ }
 
 //Jhash::Jhash( Jhash&& soi );                                         // mover
index f6c32c3..3f85a59 100644 (file)
@@ -58,6 +58,7 @@
 #define PT_STRING              4
 
 #define OBJ_SPACE              1                       // space in the symbol table where json bits are stashed
+#define MGT_SPACE              2                       // non-json objects in the hash (management things)
 
 extern void jw_nuke( void* st );
 
@@ -180,17 +181,31 @@ static void nix_things( void* st, void* se, const char* name,  void* ele, void *
                                        }
 
                                        free( j->v.pv );                        // must free the array (arrays aren't nested, so all things in the array don't reference allocated mem)
+                                       free( j );
                                }
                                break;
 
-                       case JSMN_OBJECT:
+                       case JSMN_OBJECT:                                                       // delete the sub symtab
                                jw_nuke( j->v.pv );
                                j->jsmn_type = JSMN_UNDEFINED;                  // prevent a double free
+                               free( j );
+                               break;
+
+                       case JSMN_STRING:
+                       case JSMN_PRIMITIVE:
+                               free( j );
                                break;
                }
        }
 }
 
+/*
+       Nix non-json things that are also in the hash.
+*/
+static void nix_mgt( void* st, void* se, const char* name,  void* ele, void *data ) {
+       free( ele );
+}
+
 /*
        Invoked for each thing and prints what we can to stderr.
 */
@@ -239,6 +254,8 @@ void* parse_jobject( void* st, char *json, char* prefix ) {
 
        if( jtokens[0].type != JSMN_OBJECT ) {                          // if it's not an object then we can't parse it.
                fprintf( stderr, "[WARN] jwrapper: badly formed json; initial opening bracket ({) not detected\n" );
+               rmr_sym_free( st );
+               free( jtokens );
                return NULL;
        }
 
@@ -252,6 +269,7 @@ void* parse_jobject( void* st, char *json, char* prefix ) {
                if( jtokens[i].type != JSMN_STRING ) {
                        fprintf( stderr, "[WARN] jwrapper: badly formed json [%d]; expected name (string) found type=%d %s\n", i, jtokens[i].type, extract( json, &jtokens[i] ) );
                        rmr_sym_free( st );
+                       free( jtokens );
                        return NULL;
                }
                name = extract( json, &jtokens[i] );
@@ -267,7 +285,7 @@ void* parse_jobject( void* st, char *json, char* prefix ) {
                        case JSMN_OBJECT:                               // save object in two ways: as an object 'blob' and in the current symtab using name as a base (original)
                                dstr = strdup( extract( json, &jtokens[i] ) );
                                snprintf( wbuf, sizeof( wbuf ), "%s_json", name );      // must stash the json string in the symtab for clean up during nuke
-                               rmr_sym_put( st, wbuf, OBJ_SPACE, dstr );
+                               rmr_sym_put( st, wbuf, MGT_SPACE, dstr );
 
                                parse_jobject( st, dstr, name );                                        // recurse to add the object as objectname.xxxx elements
 
@@ -275,7 +293,7 @@ void* parse_jobject( void* st, char *json, char* prefix ) {
                                        (jtp->v.pv = (void *) rmr_sym_alloc( 255 ) ) != NULL ) {                // object is just a blob
 
                                        dstr = strdup( extract( json, &jtokens[i] ) );
-                                       rmr_sym_put( jtp->v.pv, JSON_SYM_NAME, OBJ_SPACE, dstr );               // must stash json so it is freed during nuke()
+                                       rmr_sym_put( jtp->v.pv, JSON_SYM_NAME, MGT_SPACE, dstr );               // must stash json so it is freed during nuke()
                                        parse_jobject( jtp->v.pv,  dstr, "" );                                                  // recurse acorss the string and build a new symtab
 
                                        size = jtokens[i].end;                                                                                  // done with them, we need to skip them
@@ -300,6 +318,7 @@ void* parse_jobject( void* st, char *json, char* prefix ) {
                                if( jtp == NULL ) {
                                        fprintf( stderr, "[WARN] jwrapper: memory alloc error processing element [%d] in json\n", i );
                                        rmr_sym_free( st );
+                                       free( jtokens );
                                        return NULL;
                                }
                                jarray = jtp->v.pv = (jsmntok_t *) malloc( sizeof( *jarray ) * size );          // allocate the array
@@ -327,6 +346,9 @@ void* parse_jobject( void* st, char *json, char* prefix ) {
                                                case JSMN_ARRAY:
                                                        fprintf( stderr, "[WARN] jwrapper: [%d] array element %d is not valid type (array) is not string or primative\n", i, n );
                                                        n += jtokens[i+n].size;                 // this should skip the nested array
+                                                       free( jtp );
+                                                       free( jarray );
+                                                       jarray = NULL;
                                                        break;
 
                                                case JSMN_STRING:
@@ -371,6 +393,7 @@ void* parse_jobject( void* st, char *json, char* prefix ) {
                                                default:
                                                        fprintf( stderr, "[WARN] jwrapper: [%d] array element %d is not valid type (unknown=%d) is not string or primative\n", i, n, jtokens[i].type  );
                                                        rmr_sym_free( st );
+                                                       free( jtokens );
                                                        return NULL;
                                                        break;
                                        }
@@ -436,7 +459,8 @@ extern void jw_nuke( void* st ) {
        char*   buf;                                    // pointer to the original json to free
 
        if( st != NULL ) {
-               rmr_sym_foreach_class( st, OBJ_SPACE, nix_things, NULL );       // free anything that the symtab references
+               rmr_sym_foreach_class( st, OBJ_SPACE, nix_things, NULL );       // free any json thing that the symtab references
+               rmr_sym_foreach_class( st, MGT_SPACE, nix_mgt, NULL );          // free management things
                rmr_sym_free( st );                                                                                     // free the symtab itself
        }
 }
@@ -462,15 +486,18 @@ extern void jw_dump( void* st ) {
        if present.
 */
 extern void* jw_new( const char* json ) {
-       void    *st;                            // symbol table
+       void    *st = NULL;                     // symbol table
        char*   djson;                          // dup so we can save it
        void*   rp = NULL;                      // return value
 
        if( json != NULL && (st = rmr_sym_alloc( MAX_THINGS )) != NULL ) {
                djson = strdup( json );                                                                                                 // allows user to free/overlay their buffer as needed
-               rmr_sym_put( st, (unsigned char *) JSON_SYM_NAME, OBJ_SPACE, djson );   // must have a reference to the string until symtab is trashed
-
-               rp =  parse_jobject( st,  djson, "" );                                                                  // empty prefix for the root object
+               rp =  parse_jobject( st,  djson, "" );                  // empty prefix for the root object; parse_jo will clean up and free st
+               if( rp == NULL ) {
+                       free( djson );
+               } else {
+                       rmr_sym_put( st, (unsigned char *) JSON_SYM_NAME, MGT_SPACE, djson );   // must have a reference to the string until symtab is trashed
+               }
        }
 
        return rp;
index 0abcc17..4245d3b 100644 (file)
@@ -15,7 +15,7 @@ unit_test:: unit_test.cpp rmr_em.o
 
 # build a special jwrapper object with coverage settings
 jwrapper_test.o:: ../src/json/jwrapper.c ../src/json/jwrapper.h
-       cc $(coverage_opts)  -I  ../src/json -I ../ext/jsmn  ../src/json/jwrapper.c -c -o jwrapper_test.o
+       cc $(coverage_opts)  -g -I  ../src/json -I ../ext/jsmn  ../src/json/jwrapper.c -c -o jwrapper_test.o
 
 jhash_test:: jwrapper_test.o jhash_test.cpp
        # do NOT link the xapp lib; we include all modules in the test programme
index be4171c..58d283f 100644 (file)
@@ -95,6 +95,7 @@ int main( int argc, char** argv ) {
        fprintf( stderr, "read: (%s)\n", jstr );
 
        jh = new Jhash( jstr );
+       free( jstr );
 
        if( jh == NULL ) {
                fprintf( stderr, "<FAIL> could not parse json string from: test.json\n" );