[syslog-ng] [PATCH 6/7] json: Use a hand-coded JSON formatter

Gergely Nagy algernon at balabit.hu
Fri Sep 14 11:52:00 CEST 2012


Instead of relying on json-c to format our JSON, do it ourselves,
skipping quite a bit of object allocations that we do not need.

Signed-off-by: Gergely Nagy <algernon at balabit.hu>
---
 modules/json/tfjson.c |  152 +++++++++++++++++++++++++++++++++----------------
 1 file changed, 103 insertions(+), 49 deletions(-)

diff --git a/modules/json/tfjson.c b/modules/json/tfjson.c
index c8a2472..e46cbd4 100644
--- a/modules/json/tfjson.c
+++ b/modules/json/tfjson.c
@@ -25,10 +25,6 @@
 #include "cfg.h"
 #include "value-pairs.h"
 
-#include <printbuf.h>
-#include <json.h>
-#include <json_object_private.h>
-
 typedef struct _TFJsonState
 {
   TFSimpleFuncState super;
@@ -49,33 +45,70 @@ tf_json_prepare(LogTemplateFunction *self, gpointer s, LogTemplate *parent,
   return TRUE;
 }
 
-static int
-tf_json_object_to_string (struct json_object *jso,
-			  struct printbuf *pb)
+typedef struct
+{
+  gboolean need_comma;
+  GString *buffer;
+} json_state_t;
+
+static inline void
+g_string_append_escaped(GString *dest, const char *str)
 {
-  int i = 0;
-  struct json_object_iter iter;
-  sprintbuf (pb, "{");
+  /* Assumes ASCII!  Keep in sync with the switch! */
+  static const unsigned char json_exceptions[UCHAR_MAX + 1] =
+    {
+      [0x01] = 1, [0x02] = 1, [0x03] = 1, [0x04] = 1, [0x05] = 1, [0x06] = 1,
+      [0x07] = 1, [0x08] = 1, [0x09] = 1, [0x0a] = 1, [0x0b] = 1, [0x0c] = 1,
+      [0x0d] = 1, [0x0e] = 1, [0x0f] = 1, [0x10] = 1, [0x11] = 1, [0x12] = 1,
+      [0x13] = 1, [0x14] = 1, [0x15] = 1, [0x16] = 1, [0x17] = 1, [0x18] = 1,
+      [0x19] = 1, [0x1a] = 1, [0x1b] = 1, [0x1c] = 1, [0x1d] = 1, [0x1e] = 1,
+      [0x1f] = 1, ['\\'] = 1, ['"'] = 1
+    };
+
+  const unsigned char *p;
 
-  json_object_object_foreachC (jso, iter)
+  p = (unsigned char *)str;
+
+  while (*p)
     {
-      gchar *esc;
-      
-      if (i)
-	sprintbuf (pb, ",");
-      sprintbuf (pb, "\"");
-      esc = g_strescape (iter.key, NULL);
-      sprintbuf (pb, esc);
-      g_free (esc);
-      sprintbuf (pb, "\":");
-      if (iter.val == NULL)
-	sprintbuf (pb, "null");
+      if (json_exceptions[*p] == 0)
+        g_string_append_c(dest, *p);
       else
-	iter.val->_to_json_string (iter.val, pb);
-      i++;
+        {
+          /* Keep in sync with json_exceptions! */
+          switch (*p)
+            {
+            case '\b':
+              g_string_append(dest, "\\b");
+              break;
+            case '\n':
+              g_string_append(dest, "\\n");
+              break;
+            case '\r':
+              g_string_append(dest, "\\r");
+              break;
+            case '\t':
+              g_string_append(dest, "\\t");
+              break;
+            case '\\':
+              g_string_append(dest, "\\\\");
+              break;
+            case '"':
+              g_string_append(dest, "\\\"");
+              break;
+            default:
+              {
+                static const char json_hex_chars[16] = "0123456789abcdef";
+
+                g_string_append(dest, "\\u00");
+                g_string_append_c(dest, json_hex_chars[(*p) >> 4]);
+                g_string_append_c(dest, json_hex_chars[(*p) & 0xf]);
+                break;
+              }
+            }
+        }
+      p++;
     }
-
-  return sprintbuf(pb, "}");
 }
 
 static gboolean
@@ -84,14 +117,30 @@ tf_json_obj_start(const gchar *name,
                   const gchar *prev, gpointer *prev_data,
                   gpointer user_data)
 {
-  struct json_object *o;
+  json_state_t *state = (json_state_t *)user_data;
+  gboolean need_comma = FALSE;
 
   if (prefix_data)
+    need_comma = GPOINTER_TO_INT(*prefix_data);
+  else
+    need_comma = state->need_comma;
+
+  if (need_comma)
+    g_string_append_c(state->buffer, ',');
+
+  if (name)
     {
-      o = json_object_new_object();
-      *prefix_data = o;
-      o->_to_json_string = tf_json_object_to_string;
+      g_string_append_c(state->buffer, '"');
+      g_string_append_escaped(state->buffer, name);
+      g_string_append(state->buffer, "\":{");
+      state->need_comma = TRUE;
     }
+  else
+    g_string_append_c(state->buffer, '{');
+
+  if (prefix_data)
+    *prefix_data=GINT_TO_POINTER(0);
+
   return FALSE;
 }
 
@@ -101,15 +150,13 @@ tf_json_obj_end(const gchar *name,
                 const gchar *prev, gpointer *prev_data,
                 gpointer user_data)
 {
-  struct json_object *root;
+  json_state_t *state = (json_state_t *)user_data;
 
   if (prev_data)
-    root = (struct json_object *)*prev_data;
-  else
-    root = (struct json_object *)user_data;
+    *prev_data = GINT_TO_POINTER(1);
+
+  g_string_append_c(state->buffer, '}');
 
-  if (prefix_data)
-    json_object_object_add (root, (gchar *) name, (struct json_object *)*prefix_data);
   return FALSE;
 }
 
@@ -117,16 +164,26 @@ static gboolean
 tf_json_value(const gchar *name, const gchar *prefix, const gchar *value,
               gpointer *prefix_data, gpointer user_data)
 {
-  struct json_object *root;
-  struct json_object *this;
+  gboolean need_comma = FALSE;
+  json_state_t *state = (json_state_t *)user_data;
 
   if (prefix_data)
-    root = (struct json_object *)*prefix_data;
+    need_comma = GPOINTER_TO_INT(*prefix_data);
   else
-    root = (struct json_object *)user_data;
+    need_comma = state->need_comma;
+
+  if (need_comma)
+    g_string_append_c(state->buffer, ',');
+  else if (prefix_data)
+    *prefix_data = GINT_TO_POINTER(1);
 
-  this = json_object_new_string ((gchar *) value);
-  json_object_object_add (root, (gchar *) name, this);
+  g_string_append_c(state->buffer, '"');
+  g_string_append_escaped(state->buffer, name);
+  g_string_append(state->buffer, "\":\"");
+  g_string_append_escaped(state->buffer, value);
+  g_string_append_c(state->buffer, '"');
+
+  state->need_comma = TRUE;
 
   return FALSE;
 }
@@ -134,17 +191,14 @@ tf_json_value(const gchar *name, const gchar *prefix, const gchar *value,
 static void
 tf_json_append(GString *result, ValuePairs *vp, LogMessage *msg)
 {
-  struct json_object *json;
+  json_state_t state;
 
-  json = json_object_new_object();
-  json->_to_json_string = tf_json_object_to_string;
+  state.need_comma = FALSE;
+  state.buffer = result;
 
   value_pairs_walk(vp,
                    tf_json_obj_start, tf_json_value, tf_json_obj_end,
-                   msg, 0, json);
-
-  g_string_append(result, json_object_to_json_string (json));
-  json_object_put(json);
+                   msg, 0, &state);
 }
 
 static void
-- 
1.7.10.4




More information about the syslog-ng mailing list