]> granicus.if.org Git - json-c/commitdiff
more efficient handling for smalls strings inside json_object
authorRainer Gerhards <rgerhards@adiscon.com>
Wed, 23 Sep 2015 13:56:48 +0000 (15:56 +0200)
committerRainer Gerhards <rgerhards@adiscon.com>
Wed, 23 Sep 2015 13:56:48 +0000 (15:56 +0200)
smalls strings inside json_objects had a high overhead because dynamic
memory allocation was needed for each of them. This also meant that the
pointer needed to be updated. This is now changed so that small strings
can directly be stored inside the json_object. Note that on the regular
64 bit machines a pointer takes 8 bytes. So even without increasing
memory, we could store string up to 7 bytes directly inside the object.
The max size is configurable. I have selected up to 31 bytes (which
means a buffer of 32 including the NUL byte). This brings a 24-bytes
memory overhead, but I consider that still useful because the memory
allocator usually also has quite some overhead (16 bytes) for
dyn alloced memory blocks. In any case, the max buffer size can be
tweaked via #define.

json_object.c
json_object_private.h

index f9502571b49f2a29a3047f41fd46274ecc43dc72..8dd60827ddcab5e0f0fbb0cb38467f238f85b841 100644 (file)
@@ -95,9 +95,18 @@ static void json_object_fini(void)
 #endif /* REFCOUNT_DEBUG */
 
 
+/* helper for accessing the optimized string data component in json_object
+ */
+static const char *
+get_string_component(struct json_object *jso)
+{
+       return (jso->o.c_string.len < LEN_DIRECT_STRING_DATA) ?
+                  jso->o.c_string.str.data : jso->o.c_string.str.ptr;
+}
+
 /* string escaping */
 
-static int json_escape_str(struct printbuf *pb, char *str, int len)
+static int json_escape_str(struct printbuf *pb, const char *str, int len)
 {
        int pos = 0, start_offset = 0;
        unsigned char c;
@@ -559,7 +568,7 @@ int32_t json_object_get_int(struct json_object *jso)
         * Parse strings into 64-bit numbers, then use the
         * 64-to-32-bit number handling below.
         */
-       if (json_parse_int64(jso->o.c_string.str, &cint64) != 0)
+       if (json_parse_int64(get_string_component(jso), &cint64) != 0)
                return 0; /* whoops, it didn't work. */
        o_type = json_type_int;
   }
@@ -607,7 +616,7 @@ int64_t json_object_get_int64(struct json_object *jso)
        case json_type_boolean:
                return jso->o.c_boolean;
        case json_type_string:
-               if (json_parse_int64(jso->o.c_string.str, &cint) == 0)
+               if (json_parse_int64(get_string_component(jso), &cint) == 0)
                        return cint;
        default:
                return 0;
@@ -714,10 +723,10 @@ double json_object_get_double(struct json_object *jso)
     return jso->o.c_boolean;
   case json_type_string:
     errno = 0;
-    cdouble = strtod(jso->o.c_string.str,&errPtr);
+    cdouble = strtod(get_string_component(jso), &errPtr);
 
     /* if conversion stopped at the first character, return 0.0 */
-    if (errPtr == jso->o.c_string.str)
+    if (errPtr == get_string_component(jso))
         return 0.0;
 
     /*
@@ -757,14 +766,15 @@ static int json_object_string_to_json_string(struct json_object* jso,
                                                 int flags)
 {
        sprintbuf(pb, "\"");
-       json_escape_str(pb, jso->o.c_string.str, jso->o.c_string.len);
+       json_escape_str(pb, get_string_component(jso), jso->o.c_string.len);
        sprintbuf(pb, "\"");
        return 0;
 }
 
 static void json_object_string_delete(struct json_object* jso)
 {
-       free(jso->o.c_string.str);
+       if(jso->o.c_string.len >= LEN_DIRECT_STRING_DATA)
+               free(jso->o.c_string.str.ptr);
        json_object_generic_delete(jso);
 }
 
@@ -775,33 +785,43 @@ struct json_object* json_object_new_string(const char *s)
                return NULL;
        jso->_delete = &json_object_string_delete;
        jso->_to_json_string = &json_object_string_to_json_string;
-       jso->o.c_string.str = strdup(s);
-       if (!jso->o.c_string.str)
-       {
-               json_object_generic_delete(jso);
-               errno = ENOMEM;
-               return NULL;
-       }
        jso->o.c_string.len = strlen(s);
+       if(jso->o.c_string.len < LEN_DIRECT_STRING_DATA) {
+               memcpy(jso->o.c_string.str.data, s, jso->o.c_string.len);
+       } else {
+               jso->o.c_string.str.ptr = strdup(s);
+               if (!jso->o.c_string.str.ptr)
+               {
+                       json_object_generic_delete(jso);
+                       errno = ENOMEM;
+                       return NULL;
+               }
+       }
        return jso;
 }
 
 struct json_object* json_object_new_string_len(const char *s, int len)
 {
+       char *dstbuf;
        struct json_object *jso = json_object_new(json_type_string);
        if (!jso)
                return NULL;
        jso->_delete = &json_object_string_delete;
        jso->_to_json_string = &json_object_string_to_json_string;
-       jso->o.c_string.str = (char*)malloc(len + 1);
-       if (!jso->o.c_string.str)
-       {
-               json_object_generic_delete(jso);
-               errno = ENOMEM;
-               return NULL;
+       if(len < LEN_DIRECT_STRING_DATA) {
+               dstbuf = jso->o.c_string.str.data;
+       } else {
+               jso->o.c_string.str.ptr = (char*)malloc(len + 1);
+               if (!jso->o.c_string.str.ptr)
+               {
+                       json_object_generic_delete(jso);
+                       errno = ENOMEM;
+                       return NULL;
+               }
+               dstbuf = jso->o.c_string.str.ptr;
        }
-       memcpy(jso->o.c_string.str, (void *)s, len);
-       jso->o.c_string.str[len] = '\0';
+       memcpy(dstbuf, (void *)s, len);
+       dstbuf[len] = '\0';
        jso->o.c_string.len = len;
        return jso;
 }
@@ -813,7 +833,7 @@ const char* json_object_get_string(struct json_object *jso)
        switch(jso->o_type)
        {
        case json_type_string:
-               return jso->o.c_string.str;
+               return get_string_component(jso);
        default:
                return json_object_to_json_string(jso);
        }
index 5ed791b58b3776ed00cedf760f8bf9a579bc79e5..67a77f889eb0260efcdabf40e1ccaf0d634d31b5 100644 (file)
@@ -16,6 +16,8 @@
 extern "C" {
 #endif
 
+#define LEN_DIRECT_STRING_DATA 32 /**< how many bytes are directly stored in json_object for strings? */
+
 typedef void (json_object_private_delete_fn)(struct json_object *o);
 
 struct json_object
@@ -32,7 +34,13 @@ struct json_object
     struct lh_table *c_object;
     struct array_list *c_array;
     struct {
-        char *str;
+       union {
+               /* optimize: if we have small strings, we can store them
+                * directly. This saves considerable CPU cycles AND memory.
+                */
+               char *ptr;
+               char data[LEN_DIRECT_STRING_DATA];
+       } str;
         int len;
     } c_string;
   } o;