[master] 5739314 Add a tiny JSON parser for private use by libvarnishapi.

Poul-Henning Kamp phk at FreeBSD.org
Tue May 23 19:05:06 CEST 2017


commit 5739314c680ee7fb58ff394b4cd35acdb24411e7
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Tue May 23 17:03:33 2017 +0000

    Add a tiny JSON parser for private use by libvarnishapi.

diff --git a/lib/libvarnishapi/Makefile.am b/lib/libvarnishapi/Makefile.am
index 769e658..60a7661 100644
--- a/lib/libvarnishapi/Makefile.am
+++ b/lib/libvarnishapi/Makefile.am
@@ -12,6 +12,8 @@ lib_LTLIBRARIES = libvarnishapi.la
 libvarnishapi_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:6:0
 
 libvarnishapi_la_SOURCES = \
+	vjsn.c \
+	vjsn.h \
 	vsm_api.h \
 	vsl_api.h \
 	vxp.h \
diff --git a/lib/libvarnishapi/flint.lnt b/lib/libvarnishapi/flint.lnt
index 59ad7b6..2d2538e 100644
--- a/lib/libvarnishapi/flint.lnt
+++ b/lib/libvarnishapi/flint.lnt
@@ -1,5 +1,7 @@
 +fan
 
+-esym(759, VJSN_*)			// could be moved hdr->mod 
+-esym(765, VJSN_*)			// could be made static
 -esym(788, vex_rhs_e::*)
 -esym(788, VSL_transaction_e::*)
 -esym(769, vex_rhs_e::VEX__UNSET)
diff --git a/lib/libvarnishapi/vjsn.c b/lib/libvarnishapi/vjsn.c
new file mode 100644
index 0000000..d3d5f30
--- /dev/null
+++ b/lib/libvarnishapi/vjsn.c
@@ -0,0 +1,561 @@
+/*-
+ * Copyright (c) 2017 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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 AUTHOR 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 AUTHOR 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "vas.h"
+#include "miniobj.h"
+#include "vqueue.h"
+#include "vjsn.h"
+
+const char VJSN_OBJECT[] = "object";
+const char VJSN_ARRAY[] = "array";
+const char VJSN_NUMBER[] = "number";
+const char VJSN_STRING[] = "string";
+const char VJSN_TRUE[] = "true";
+const char VJSN_FALSE[] = "false";
+const char VJSN_NULL[] = "null";
+
+#define VJSN_EXPECT(js, xxx, ret)					\
+	do {								\
+		AZ(js->err);						\
+		if (*((js)->ptr) != xxx) {				\
+			js->err = "Expected " #xxx " not found.";	\
+			return (ret);					\
+		} else {						\
+			*js->ptr++ = '\0';				\
+		}							\
+	} while (0)
+
+static struct vjsn_val *vjsn_value(struct vjsn *);
+
+static struct vjsn_val *
+vjsn_val_new(const char *type)
+{
+	struct vjsn_val *jsv;
+
+	ALLOC_OBJ(jsv, VJSN_VAL_MAGIC);
+	AN(jsv);
+	VTAILQ_INIT(&jsv->children);
+	jsv->type = type;
+	return (jsv);
+}
+
+static void
+vjsn_val_delete(struct vjsn_val *jsv)
+{
+	struct vjsn_val *jsve;
+
+	CHECK_OBJ_NOTNULL(jsv, VJSN_VAL_MAGIC);
+	do {
+		jsve = VTAILQ_FIRST(&jsv->children);
+		if (jsve != NULL) {
+			VTAILQ_REMOVE(&jsv->children, jsve, list);
+			vjsn_val_delete(jsve);
+		}
+	} while (jsve != NULL);
+	FREE_OBJ(jsv);
+}
+
+static void
+vjsn_delete(struct vjsn **jp)
+{
+	struct vjsn *js;
+
+	AN(jp);
+	js = *jp;
+	*jp = NULL;
+	CHECK_OBJ_NOTNULL(js, VJSN_MAGIC);
+	if (js->value != NULL)
+		vjsn_val_delete(js->value);
+	free(js->raw);
+	FREE_OBJ(js);
+}
+
+static void
+vjsn_skip_ws(struct vjsn *js)
+{
+	char c;
+
+	while (1) {
+		c = js->ptr[0];
+		if (c == 0x09 || c == 0x0a || c == 0x0d || c == 0x20) {
+			*js->ptr++ = '\0';
+			continue;
+		}
+#ifdef VJSN_COMMENTS
+		if (c == '/' && js->ptr[1] == '*') {
+			js->ptr += 2;
+			while (js->ptr[0] != '*' || js->ptr[1] != '/')
+				js->ptr++;
+			js->ptr += 2;
+			continue;
+		}
+#endif
+		return;
+	}
+}
+
+static unsigned
+vjsn_unumber(struct vjsn *js)
+{
+	unsigned u = 0;
+	char c;
+	int i;
+
+	VJSN_EXPECT(js, '\\', 0);
+	VJSN_EXPECT(js, 'u', 0);
+	for(i = 0; i < 4; i++) {
+		u <<= 4;
+		c = *js->ptr;
+		if (c >= '0' && c <= '9')
+			u |= c - '0';			/*lint !e737 */
+		else if (c >= 'A' && c <= 'F')
+			u |= c - '7';			/*lint !e737 */
+		else if (c >= 'a' && c <= 'f')
+			u |= c - 'W';			/*lint !e737 */
+		else {
+			js->err = "Illegal \\uXXXX sequence";
+			return (0);
+		}
+		js->ptr++;
+	}
+	return (u);
+}
+
+static void
+vjsn_unicode(struct vjsn *js, char **d)
+{
+	unsigned u1, u2;
+
+	u1 = vjsn_unumber(js);
+
+	if (u1 >= 0xd800 && u1 <= 0xdc00) {
+		u2 = vjsn_unumber(js);
+		if (u2 < 0xdc00 || u2 > 0xdfff) {
+			js->err = "Bad UTF-16 Surrogate pair";
+			return;
+		}
+		u1 -= 0xd800;
+		u2 -= 0xdc00;
+		u1 <<= 10;
+		u1 |= u2;
+	}
+	/*lint -save -e734 -e713 */
+	if (u1 < 0x80)
+		*(*d)++ = u1;
+	else if (u1 < 0x800) {
+		*(*d)++ = 0xc0 + u1 / 64;
+		*(*d)++ = 0x80 + u1 % 64;
+	} else if (u1 - 0xd800u < 0x800) {
+		js->err = "Bad UNICODE point";
+	} else if (u1 < 0x10000) {
+		*(*d)++ = 0xe0 + u1 / 4096;
+		*(*d)++ = 0x80 + u1 / 64 % 64;
+		*(*d)++ = 0x80 + u1 % 64;
+	} else if (u1 < 0x110000) {
+		*(*d)++ = 0xf0 + u1 / 262144;
+		*(*d)++ = 0x80 + u1 / 4096 % 64;
+		*(*d)++ = 0x80 + u1 / 64 % 64;
+		*(*d)++ = 0x80 + u1 % 64;
+	} else {
+		js->err = "Bad UNICODE point";
+	}
+	/*lint -restore */
+}
+
+static char *
+vjsn_string(struct vjsn *js)
+{
+	char *p, *b;
+
+	vjsn_skip_ws(js);
+	VJSN_EXPECT(js, '"', NULL);
+	b = p = js->ptr;
+	while (*js->ptr != '"') {
+		if (*js->ptr == '\0') {
+			js->err = "Unterminate string";
+			return (NULL);
+		}
+		if (*js->ptr >= 0x00 && *js->ptr <= 0x1f) {
+			js->err = "unescaped control char in string";
+			return (NULL);
+		}
+		if (*js->ptr != '\\') {
+			*p++ = *js->ptr++;
+			continue;
+		}
+		switch(js->ptr[1]) {
+		case '\\':
+		case '/':
+		case '"': *p++ = js->ptr[1]; js->ptr += 2; break;
+		case 'b': *p++ = 0x08; js->ptr += 2; break;
+		case 'f': *p++ = 0x0c; js->ptr += 2; break;
+		case 't': *p++ = 0x09; js->ptr += 2; break;
+		case 'n': *p++ = 0x0a; js->ptr += 2; break;
+		case 'r': *p++ = 0x0d; js->ptr += 2; break;
+		case 'u':
+			vjsn_unicode(js, &p);
+			if(js->err != NULL)
+				return(NULL);
+			break;
+		default:
+			js->err = "Bad string escape";
+			return (NULL);
+		}
+	}
+	VJSN_EXPECT(js, '"', NULL);
+	*p = '\0';
+	return (b);
+}
+
+static struct vjsn_val *
+vjsn_object(struct vjsn *js)
+{
+	struct vjsn_val *jsv, *jsve;
+	char *s;
+
+	VJSN_EXPECT(js, '{', NULL);
+
+	jsv = vjsn_val_new(VJSN_OBJECT);
+	AN(jsv);
+
+	vjsn_skip_ws(js);
+	if (*js->ptr != '}') {
+		while (1) {
+			s = vjsn_string(js);
+			if (js->err != NULL)
+				return (jsv);
+			vjsn_skip_ws(js);
+			VJSN_EXPECT(js, ':', jsv);
+			jsve = vjsn_value(js);
+			if (js->err != NULL)
+				return (jsv);
+			CHECK_OBJ_NOTNULL(jsve, VJSN_VAL_MAGIC);
+			jsve->name = s;
+			VTAILQ_INSERT_TAIL(&jsv->children, jsve, list);
+			vjsn_skip_ws(js);
+			if(*js->ptr == '}')
+				break;
+			VJSN_EXPECT(js, ',', jsv);
+		}
+	}
+	VJSN_EXPECT(js, '}', jsv);
+	return (jsv);
+}
+
+static struct vjsn_val *
+vjsn_array(struct vjsn *js)
+{
+	struct vjsn_val *jsv, *jsve;
+
+	VJSN_EXPECT(js, '[', NULL);
+
+	jsv = vjsn_val_new(VJSN_ARRAY);
+	AN(jsv);
+
+	vjsn_skip_ws(js);
+	if (*js->ptr != ']') {
+		while (1) {
+			jsve = vjsn_value(js);
+			if (js->err != NULL)
+				return (jsv);
+			CHECK_OBJ_NOTNULL(jsve, VJSN_VAL_MAGIC);
+			VTAILQ_INSERT_TAIL(&jsv->children, jsve, list);
+			vjsn_skip_ws(js);
+			if(*js->ptr == ']')
+				break;
+			VJSN_EXPECT(js, ',', jsv);
+		}
+	}
+	VJSN_EXPECT(js, ']', jsv);
+	return (jsv);
+}
+
+static struct vjsn_val *
+vjsn_number(struct vjsn *js)
+{
+	struct vjsn_val *jsv;
+
+	jsv = vjsn_val_new(VJSN_NUMBER);
+	AN(jsv);
+
+	jsv->value = js->ptr;
+
+	if (*js->ptr == '-')
+		js->ptr++;
+	if (*js->ptr < '0' || *js->ptr > '9') {
+		js->err = "Bad number";
+		return (jsv);
+	}
+	if (*js->ptr == '0' && js->ptr[1] >= '0' && js->ptr[1] <= '9') {
+		js->err = "Bad number";
+		return (jsv);
+	}
+	while (*js->ptr >= '0' && *js->ptr <= '9')
+		js->ptr++;
+	if (*js->ptr == '.') {
+		js->ptr++;
+		if (*js->ptr < '0' || *js->ptr > '9') {
+			js->err = "Bad number";
+			return (jsv);
+		}
+		while (*js->ptr >= '0' && *js->ptr <= '9')
+			js->ptr++;
+	}
+	if (*js->ptr == 'e' || *js->ptr == 'E') {
+		js->ptr++;
+		if (*js->ptr == '-' || *js->ptr == '+')
+			js->ptr++;
+		if (*js->ptr < '0' || *js->ptr > '9') {
+			js->err = "Bad number";
+			return (jsv);
+		}
+		while (*js->ptr >= '0' && *js->ptr <= '9')
+			js->ptr++;
+	}
+	return (jsv);
+}
+
+static struct vjsn_val *
+vjsn_value(struct vjsn *js)
+{
+	struct vjsn_val *jsv;
+
+	AZ(js->err);
+	vjsn_skip_ws(js);
+	if (*js->ptr == '{')
+		return (vjsn_object(js));
+	if (*js->ptr== '[')
+		return (vjsn_array(js));
+	if (*js->ptr== '"') {
+		jsv = vjsn_val_new(VJSN_STRING);
+		jsv->value = vjsn_string(js);
+		if (js->err != NULL)
+			return (jsv);
+		AN(jsv->value);
+		return (jsv);
+	}
+	if (!memcmp(js->ptr, "true", 4)) {
+		js->ptr += 4;
+		return (vjsn_val_new(VJSN_TRUE));
+	}
+	if (!memcmp(js->ptr, "false", 4)) {
+		js->ptr += 5;
+		return (vjsn_val_new(VJSN_FALSE));
+	}
+	if (!memcmp(js->ptr, "null", 4)) {
+		js->ptr += 4;
+		return (vjsn_val_new(VJSN_NULL));
+	}
+	if (*js->ptr == '-' || (*js->ptr >= '0' && *js->ptr <= '9'))
+		return (vjsn_number(js));
+	js->err = "Unrecognized value";
+	return (NULL);
+}
+
+struct vjsn *
+vjsn_parse(const char *src, const char **err)
+{
+	struct vjsn *js;
+	char *p, *e;
+
+	AN(src);
+
+	AN(err);
+	*err = NULL;
+
+	p = strdup(src);
+	AN(p);
+	e = strchr(p, '\0');
+	AN(e);
+
+	ALLOC_OBJ(js, VJSN_MAGIC);
+	AN(js);
+	js->raw = p;
+	js->ptr = p;
+
+	js->value = vjsn_value(js);
+	if (js->err != NULL) {
+		*err = js->err;
+		vjsn_delete(&js);
+		return (NULL);
+	}
+
+	vjsn_skip_ws(js);
+	if (js->ptr != e) {
+		*err = "Garbage after value";
+		vjsn_delete(&js);
+		return (NULL);
+	}
+	return (js);
+}
+
+static void
+vjsn_dump_i(const struct vjsn_val *jsv, FILE *fo, int indent)
+{
+	struct vjsn_val *jsve;
+
+	printf("%*s", indent, "");
+	if (jsv->name != NULL)
+		printf("[\"%s\"]: ", jsv->name);
+	printf("{%s}", jsv->type);
+	if (jsv->value != NULL) {
+		if (strlen(jsv->value) < 20)
+			printf(" <%s", jsv->value);
+		else
+			printf(" <%.10s[...#%zu]",
+			    jsv->value, strlen(jsv->value) - 10L);
+		printf(">");
+	}
+	printf("\n");
+	VTAILQ_FOREACH(jsve, &jsv->children, list)
+		vjsn_dump_i(jsve, fo, indent + 2);
+}
+
+void
+vjsn_dump(const struct vjsn *js, FILE *fo)
+{
+
+	CHECK_OBJ_NOTNULL(js, VJSN_MAGIC);
+	AN(fo);
+	vjsn_dump_i(js->value, fo, 0);
+}
+
+#ifdef VJSN_TEST
+
+static struct vjsn *
+vjsn_file(const char *fn, const char **err)
+{
+	int fd;
+	struct stat st;
+	char *p, *e;
+	ssize_t sz;
+	struct vjsn *js;
+
+	AN(fn);
+	AN(err);
+	*err = NULL;
+	fd = open(fn, O_RDONLY);
+	if (fd < 0) {
+		*err = strerror(errno);
+		return (NULL);
+	}
+	AZ(fstat(fd, &st));
+	if (!S_ISREG(st.st_mode)) {
+		AZ(close(fd));
+		*err = "Not a regular file";
+		return (NULL);
+	}
+
+	if (st.st_size == 0) {
+		AZ(close(fd));
+		*err = "Empty file";
+		return (NULL);
+	}
+
+	p = malloc(st.st_size + 1);
+	AN(p);
+
+	sz = read(fd, p, st.st_size + 1);
+	if (sz < 0) {
+		*err = strerror(errno);
+		AZ(close(fd));
+		free(p);
+		return (NULL);
+	}
+	AZ(close(fd));
+
+	if (sz < st.st_size) {
+		free(p);
+		*err = "Short read";
+		return (NULL);
+	}
+
+	if (sz > st.st_size) {
+		free(p);
+		*err = "Long read";
+		return (NULL);
+	}
+
+	p[sz] = '\0';
+	e = p + sz;
+
+	ALLOC_OBJ(js, VJSN_MAGIC);
+	AN(js);
+	js->raw = p;
+	js->ptr = p;
+
+	js->value = vjsn_value(js);
+	if (js->err != NULL) {
+		*err = js->err;
+		vjsn_delete(&js);
+		return (NULL);
+	}
+
+	vjsn_skip_ws(js);
+	if (js->ptr != e) {
+		*err = "Garbage after value";
+		vjsn_delete(&js);
+		return (NULL);
+	}
+	return (js);
+}
+
+
+static const char * const fnx = "tst.vjsn";
+
+int
+main(int argc, char **argv)
+{
+	struct vjsn *js;
+	const char *err;
+
+	if (argc == 1)
+		js = vjsn_file(fnx, &err);
+	else
+		js = vjsn_file(argv[1], &err);
+	if (err != NULL) {
+		fprintf(stderr, "ERROR: %s\n", err);
+		AZ(js);
+		return (1);
+	} else {
+		if (0)
+			vjsn_dump(js, stdout);
+		vjsn_delete(&js);
+	}
+	return (0);
+}
+
+#endif
diff --git a/lib/libvarnishapi/vjsn.h b/lib/libvarnishapi/vjsn.h
new file mode 100644
index 0000000..91f0e4f
--- /dev/null
+++ b/lib/libvarnishapi/vjsn.h
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 2017 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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 AUTHOR 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 AUTHOR 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.
+ */
+
+extern const char VJSN_OBJECT[];
+extern const char VJSN_ARRAY[];
+extern const char VJSN_NUMBER[];
+extern const char VJSN_STRING[];
+extern const char VJSN_TRUE[];
+extern const char VJSN_FALSE[];
+extern const char VJSN_NULL[];
+
+struct vjsn_val {
+	unsigned		magic;
+#define VJSN_VAL_MAGIC		0x08a06b80
+	const char		*type;
+	const char		*name;
+	VTAILQ_ENTRY(vjsn_val)	list;
+	VTAILQ_HEAD(,vjsn_val)	children;
+	char			*value;
+};
+
+struct vjsn {
+	unsigned		magic;
+#define VJSN_MAGIC		0x86a7f02b
+
+	char			*raw;
+	char			*ptr;
+	struct vjsn_val		*value;
+	const char		*err;
+};
+
+struct vjsn *vjsn_parse(const char *, const char **);
+void vjsn_dump(const struct vjsn *js, FILE *fo);



More information about the varnish-commit mailing list