[waffle] [PATCH 04/12] core: add JSON library

Frank Henigman fjhenigman at google.com
Wed Jan 6 11:56:33 PST 2016


A small library for building JSON strings.

Signed-off-by: Frank Henigman <fjhenigman at google.com>
---
 src/waffle/CMakeLists.txt |   1 +
 src/waffle/core/json.c    | 235 ++++++++++++++++++++++++++++++++++++++++++++++
 src/waffle/core/json.h    |  93 ++++++++++++++++++
 3 files changed, 329 insertions(+)
 create mode 100644 src/waffle/core/json.c
 create mode 100644 src/waffle/core/json.h

diff --git a/src/waffle/CMakeLists.txt b/src/waffle/CMakeLists.txt
index dd9fa11..99df29d 100644
--- a/src/waffle/CMakeLists.txt
+++ b/src/waffle/CMakeLists.txt
@@ -71,6 +71,7 @@ set(waffle_sources
     api/waffle_gl_misc.c
     api/waffle_init.c
     api/waffle_window.c
+    core/json.c
     core/wcore_attrib_list.c
     core/wcore_config_attrs.c
     core/wcore_display.c
diff --git a/src/waffle/core/json.c b/src/waffle/core/json.c
new file mode 100644
index 0000000..75ba35c
--- /dev/null
+++ b/src/waffle/core/json.c
@@ -0,0 +1,235 @@
+// Copyright 2015 Google
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// - Redistributions of source code must retain the above copyright notice, this
+//   list of conditions and the following disclaimer.
+//
+// - Redistributions in binary form must reproduce the above copyright notice,
+//   this list of conditions and the following disclaimer in the documentation
+//   and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "json.h"
+
+#include "wcore_error.h"
+#include "wcore_util.h"
+
+struct json {
+    char *buf;   // json string
+    size_t size; // amount of memory at 'buf'
+    char *pos;   // end of json string
+    bool comma;  // need a comma before next value
+};
+
+// Append 's' to json buffer, growing it as needed.
+static void
+put(struct json *jj, char *s)
+{
+    if (!jj->buf)
+        return;
+
+    for (;;) {
+        if (!(*jj->pos = *s++))
+            break;
+        if (++jj->pos == jj->buf + jj->size) {
+            size_t z = jj->size * 2;
+            jj->buf = realloc(jj->buf, z);
+            if (!jj->buf)
+                return;
+            jj->pos = jj->buf + jj->size;
+            jj->size = z;
+        }
+    }
+}
+
+// Format 's' as json and write to 'p'
+static char*
+string(char *p, const char *s)
+{
+    *p++ = '"';
+    for (;; ++s) {
+        char e;
+        switch (*s) {
+            default:
+                *p++ = *s;
+                continue;
+            case '\0':
+                *p++ = '"';
+                *p = '\0';
+                return p;
+            case '\b': e = 'b';  break;
+            case '\f': e = 'f';  break;
+            case '\n': e = 'n';  break;
+            case '\r': e = 'r';  break;
+            case '\t': e = 't';  break;
+            case  '"': e = '"';  break;
+            case '\\': e = '\\'; break;
+        }
+        *p++ = '\\';
+        *p++ = e;
+    }
+}
+
+struct json*
+json_init()
+{
+    struct json *jj;
+    jj = malloc(sizeof(*jj));
+    if (jj) {
+        jj->size = 1;
+        jj->buf = jj->pos = strdup("");
+        if (jj->buf)
+            jj->comma = false;
+    }
+    return jj;
+}
+
+char *
+json_destroy(struct json *jj)
+{
+    char *result = jj->buf;
+    free(jj);
+    return result;
+}
+
+char*
+json_key(const char *s)
+{
+    // If each character is escaped, we need double the length,
+    // plus quotes and " : " and final null.
+    char *buf = malloc(strlen(s) * 2 + 6);
+    if (buf)
+        strcpy(string(buf, s), " : ");
+    return buf;
+}
+
+char*
+json_str(const char *s)
+{
+    // If each character is escaped, we need double the length,
+    // plus quotes and final null.
+    char *buf = malloc(strlen(s) * 2 + 3);
+    if (buf)
+        string(buf, s);
+    return buf;
+}
+
+char*
+json_split(const char *s, const char *sep)
+{
+    char *dup = strdup(s);
+    if (!dup)
+        return NULL;
+
+    // The worst case space requirement is for a string of length L=2N+1
+    // which has N separators and N+1 1-character items.
+    // N separators each become 4 characters '", "' for 4N characters.
+    // If each of the N+1 items is escaped we get 2N+2 characters.
+    // Add 3 for quotes and final null to get 6N+5 = 3L+2.
+    char *buf = malloc(strlen(s) * 3 + 2);
+    if (!buf)
+        goto done;
+
+    char *str = dup;
+    char *p = buf;
+    bool comma = false;
+    char *token;
+    while ((token = strtok(str, sep))) {
+        str = NULL;
+        if (comma) {
+            *p++ = ',';
+            *p++ = '\n';
+        }
+        p = string(p, token);
+        comma = true;
+    }
+
+done:
+    free(dup);
+    return buf;
+}
+
+char*
+json_num(double n)
+{
+    const char * const fmt = "%.17g";
+    size_t len = snprintf(NULL, 0, fmt, n) + 1;
+    char *result = malloc(len);
+    if (result)
+        snprintf(result, len, fmt, n);
+    return result;
+}
+
+static bool
+isnum(const char *s)
+{
+    char *p;
+    strtod(s, &p);
+    return p == s + strlen(s);
+}
+
+void
+json_append(struct json *jj, char *s)
+{
+    if (!s) {
+error:
+        free(jj->buf);
+        jj->buf = NULL;
+        return;
+    }
+
+    bool isopen  = (s[0] == '[' || s[0] == '{') && !s[1];
+    bool isclose = (s[0] == ']' || s[0] == '}') && !s[1];
+    bool iskey = s[0] && s[strlen(s)-1] == ' ';
+    bool isstr = s[0] == '"' && s[strlen(s)-1] == '"';
+
+    if (!(isopen || isclose || iskey || isstr || isnum(s))) {
+        s = json_key(s);
+        if (!s)
+            goto error;
+        iskey = true;
+    }
+
+    if (!isclose && jj->comma)
+        put(jj, ",\n");
+    put(jj, s);
+    if (!(isopen || isclose))
+        free(s);
+    if (isopen)
+        put(jj, "\n");
+    jj->comma = !(isopen || iskey);
+}
+
+void
+json_appendv(struct json *jj, ...)
+{
+    va_list ap;
+    va_start(ap, jj);
+    for (;;) {
+        char *s = va_arg(ap, char *);
+        if (s && !s[0])
+            break;
+        json_append(jj, s);
+    }
+    va_end(ap);
+}
diff --git a/src/waffle/core/json.h b/src/waffle/core/json.h
new file mode 100644
index 0000000..8806208
--- /dev/null
+++ b/src/waffle/core/json.h
@@ -0,0 +1,93 @@
+// Copyright 2015 Google
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// - Redistributions of source code must retain the above copyright notice, this
+//   list of conditions and the following disclaimer.
+//
+// - Redistributions in binary form must reproduce the above copyright notice,
+//   this list of conditions and the following disclaimer in the documentation
+//   and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+struct json;
+
+/// @brief Create an empty json string.
+///
+/// Returns NULL on failure.
+struct json*
+json_init(void);
+
+/// @brief Get the json as a C string and free other memory.
+///
+/// If an error occured during construction of the json, NULL is returned.
+/// This allows all error checking to be deferred until the end.
+char*
+json_destroy(struct json *);
+
+/// @brief Append an item to the string.
+///
+/// The following strings are allowed: "[", "]", "{", "}", or the result
+/// of json_key(), json_str(), json_num(), or json_split().
+/// If the argument is none of the above it is assumed to be a key and
+/// passed through json_key() before appending.
+/// Commas are added automatically where needed.
+/// If the argument is not a brace or bracket it is free()-ed after copying
+/// into the json.
+/// If NULL is passed in, the json is cleared, further appends do nothing,
+/// and json_destroy() will return NULL.
+void
+json_append(struct json *, char *s);
+
+/// @brief Append multiple items to the string.
+///
+/// Same as calling json_append() with each argument.  Empty string marks
+/// end of list.
+void
+json_appendv(struct json *, ...);
+
+/// @brief Format given string as a json key.
+///
+/// Escape, quote, and add a colon.  Returns NULL on error.
+/// Pass to free() when no longer needed (json_append() does that for you).
+char*
+json_key(const char *s);
+
+/// @brief Format given string as a json string.
+///
+/// Escape and quote.  Returns NULL on error.
+/// Pass to free() when no longer needed (json_append() does that for you).
+char*
+json_str(const char *s);
+
+/// @brief Split given string into list of json strings.
+///
+/// The string is split at the given separators using strtok().
+/// Each token is formatted as a json string (quoted and escaped)
+/// and assembled into a comma-separated list.
+/// Returns NULL on error.
+/// Pass to free() when no longer needed (json_append() does that for you).
+char*
+json_split(const char *s, const char *sep);
+
+/// @brief Format given number as a json number.
+///
+/// Returns NULL on error.
+/// Pass to free() when no longer needed (json_append() does that for you).
+char*
+json_num(double d);
-- 
2.6.0.rc2.230.g3dd15c0



More information about the waffle mailing list