Browse Source

Merge pull request #299 from qlyoung/perf-improvements

Improve json_object -> string performance
tags/json-c-0.13-20171207
Eric Haszlakiewicz GitHub 8 years ago
parent
commit
3fab117e4e
4 changed files with 84 additions and 33 deletions
  1. +35
    -29
      json_object.c
  2. +41
    -4
      printbuf.h
  3. +7
    -0
      tests/test_printbuf.c
  4. +1
    -0
      tests/test_printbuf.expected

+ 35
- 29
json_object.c View File

@@ -147,9 +147,12 @@ static int json_escape_str(struct printbuf *pb, const char *str, int len, int fl
printbuf_memappend(pb,
str + start_offset,
pos - start_offset);
sprintbuf(pb, "\\u00%c%c",
json_hex_chars[c >> 4],
json_hex_chars[c & 0xf]);
static char sbuf[7];
snprintf(sbuf, sizeof(sbuf),
"\\u00%c%c",
json_hex_chars[c >> 4],
json_hex_chars[c & 0xf]);
printbuf_memappend_fast(pb, sbuf, (int) sizeof(sbuf) - 1);
start_offset = ++pos;
} else
pos++;
@@ -360,29 +363,29 @@ static int json_object_object_to_json_string(struct json_object* jso,
int had_children = 0;
struct json_object_iter iter;

sprintbuf(pb, "{" /*}*/);
printbuf_strappend(pb, "{" /*}*/);
if (flags & JSON_C_TO_STRING_PRETTY)
sprintbuf(pb, "\n");
printbuf_strappend(pb, "\n");
json_object_object_foreachC(jso, iter)
{
if (had_children)
{
sprintbuf(pb, ",");
printbuf_strappend(pb, ",");
if (flags & JSON_C_TO_STRING_PRETTY)
sprintbuf(pb, "\n");
printbuf_strappend(pb, "\n");
}
had_children = 1;
if (flags & JSON_C_TO_STRING_SPACED)
sprintbuf(pb, " ");
printbuf_strappend(pb, " ");
indent(pb, level+1, flags);
sprintbuf(pb, "\"");
printbuf_strappend(pb, "\"");
json_escape_str(pb, iter.key, strlen(iter.key), flags);
if (flags & JSON_C_TO_STRING_SPACED)
sprintbuf(pb, "\": ");
printbuf_strappend(pb, "\": ");
else
sprintbuf(pb, "\":");
printbuf_strappend(pb, "\":");
if(iter.val == NULL)
sprintbuf(pb, "null");
printbuf_strappend(pb, "null");
else
if (iter.val->_to_json_string(iter.val, pb, level+1,flags) < 0)
return -1;
@@ -390,13 +393,13 @@ static int json_object_object_to_json_string(struct json_object* jso,
if (flags & JSON_C_TO_STRING_PRETTY)
{
if (had_children)
sprintbuf(pb, "\n");
printbuf_strappend(pb, "\n");
indent(pb,level,flags);
}
if (flags & JSON_C_TO_STRING_SPACED)
return sprintbuf(pb, /*{*/ " }");
return printbuf_strappend(pb, /*{*/ " }");
else
return sprintbuf(pb, /*{*/ "}");
return printbuf_strappend(pb, /*{*/ "}");
}


@@ -537,8 +540,8 @@ static int json_object_boolean_to_json_string(struct json_object* jso,
int flags)
{
if (jso->o.c_boolean)
return sprintbuf(pb, "true");
return sprintbuf(pb, "false");
return printbuf_strappend(pb, "true");
return printbuf_strappend(pb, "false");
}

struct json_object* json_object_new_boolean(json_bool b)
@@ -585,7 +588,10 @@ static int json_object_int_to_json_string(struct json_object* jso,
int level,
int flags)
{
return sprintbuf(pb, "%" PRId64, jso->o.c_int64);
/* room for 19 digits, the sign char, and a null term */
static char sbuf[21];
snprintf(sbuf, sizeof(sbuf), "%"PRId64, jso->o.c_int64);
return printbuf_memappend (pb, sbuf, strlen(sbuf));
}

struct json_object* json_object_new_int(int32_t i)
@@ -853,9 +859,9 @@ static int json_object_string_to_json_string(struct json_object* jso,
int level,
int flags)
{
sprintbuf(pb, "\"");
printbuf_strappend(pb, "\"");
json_escape_str(pb, get_string_component(jso), jso->o.c_string.len, flags);
sprintbuf(pb, "\"");
printbuf_strappend(pb, "\"");
return 0;
}

@@ -972,25 +978,25 @@ static int json_object_array_to_json_string(struct json_object* jso,
int had_children = 0;
size_t ii;

sprintbuf(pb, "[");
printbuf_strappend(pb, "[");
if (flags & JSON_C_TO_STRING_PRETTY)
sprintbuf(pb, "\n");
printbuf_strappend(pb, "\n");
for(ii=0; ii < json_object_array_length(jso); ii++)
{
struct json_object *val;
if (had_children)
{
sprintbuf(pb, ",");
printbuf_strappend(pb, ",");
if (flags & JSON_C_TO_STRING_PRETTY)
sprintbuf(pb, "\n");
printbuf_strappend(pb, "\n");
}
had_children = 1;
if (flags & JSON_C_TO_STRING_SPACED)
sprintbuf(pb, " ");
printbuf_strappend(pb, " ");
indent(pb, level + 1, flags);
val = json_object_array_get_idx(jso, ii);
if(val == NULL)
sprintbuf(pb, "null");
printbuf_strappend(pb, "null");
else
if (val->_to_json_string(val, pb, level+1, flags) < 0)
return -1;
@@ -998,13 +1004,13 @@ static int json_object_array_to_json_string(struct json_object* jso,
if (flags & JSON_C_TO_STRING_PRETTY)
{
if (had_children)
sprintbuf(pb, "\n");
printbuf_strappend(pb, "\n");
indent(pb,level,flags);
}

if (flags & JSON_C_TO_STRING_SPACED)
return sprintbuf(pb, " ]");
return sprintbuf(pb, "]");
return printbuf_strappend(pb, " ]");
return printbuf_strappend(pb, "]");
}

static void json_object_array_entry_free(void *data)


+ 41
- 4
printbuf.h View File

@@ -29,12 +29,13 @@ struct printbuf {
extern struct printbuf*
printbuf_new(void);

/* As an optimization, printbuf_memappend_fast is defined as a macro
/* As an optimization, printbuf_memappend_fast() is defined as a macro
* that handles copying data if the buffer is large enough; otherwise
* it invokes printbuf_memappend_real() which performs the heavy
* it invokes printbuf_memappend() which performs the heavy
* lifting of realloc()ing the buffer and copying data.
* Your code should not use printbuf_memappend directly--use
* printbuf_memappend_fast instead.
*
* Your code should not use printbuf_memappend() directly unless it
* checks the return code. Use printbuf_memappend_fast() instead.
*/
extern int
printbuf_memappend(struct printbuf *p, const char *buf, int size);
@@ -50,6 +51,28 @@ do { \

#define printbuf_length(p) ((p)->bpos)

/**
* Results in a compile error if the argument is not a string literal.
*/
#define _printbuf_check_literal(mystr) ("" mystr)

/**
* This is an optimization wrapper around printbuf_memappend() that is useful
* for appending string literals. Since the size of string constants is known
* at compile time, using this macro can avoid a costly strlen() call. This is
* especially helpful when a constant string must be appended many times. If
* you got here because of a compilation error caused by passing something
* other than a string literal, use printbuf_memappend_fast() in conjunction
* with strlen().
*
* See also:
* printbuf_memappend_fast()
* printbuf_memappend()
* sprintbuf()
*/
#define printbuf_strappend(pb, str) \
printbuf_memappend ((pb), _printbuf_check_literal(str), sizeof(str) - 1)

/**
* Set len bytes of the buffer to charvalue, starting at offset offset.
* Similar to calling memset(x, charvalue, len);
@@ -61,6 +84,20 @@ do { \
extern int
printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len);

/**
* Formatted print to printbuf.
*
* This function is the most expensive of the available functions for appending
* string data to a printbuf and should be used only where convenience is more
* important than speed. Avoid using this function in high performance code or
* tight loops; in these scenarios, consider using snprintf() with a static
* buffer in conjunction with one of the printbuf_*append() functions.
*
* See also:
* printbuf_memappend_fast()
* printbuf_memappend()
* printbuf_strappend()
*/
extern int
sprintbuf(struct printbuf *p, const char *msg, ...);



+ 7
- 0
tests/test_printbuf.c View File

@@ -106,8 +106,15 @@ static void test_printbuf_memappend(int *before_resize)
printf("Append to just after resize: %d, [%s]\n", printbuf_length(pb), pb->buf);

free(data);
printbuf_free(pb);

#define SA_TEST_STR "XXXXXXXXXXXXXXXX"
pb = printbuf_new();
printbuf_strappend(pb, SA_TEST_STR);
printf("Buffer size after printbuf_strappend(): %d, [%s]\n", printbuf_length(pb), pb->buf);
printbuf_free(pb);
#undef SA_TEST_STR

printf("%s: end test\n", __func__);
}



+ 1
- 0
tests/test_printbuf.expected View File

@@ -18,6 +18,7 @@ Partial append: 3, [blu]
With embedded \0 character: 4, [ab]
Append to just before resize: 31, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]
Append to just after resize: 32, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]
Buffer size after printbuf_strappend(): 16, [XXXXXXXXXXXXXXXX]
test_printbuf_memappend: end test
========================================
test_sprintbuf: starting test


Loading…
Cancel
Save