[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