]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-json: Add definitions and functions for JSON types, values, and nodes
authorStephan Bosch <stephan.bosch@open-xchange.com>
Wed, 7 Aug 2019 18:29:25 +0000 (20:29 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Sat, 18 Nov 2023 18:58:04 +0000 (18:58 +0000)
src/lib-json/Makefile.am
src/lib-json/json-types.c [new file with mode: 0644]
src/lib-json/json-types.h [new file with mode: 0644]

index 71589c657331277c9e4f4f9e2d885eab5cfc3a80..a1d011aa44bf4a3d7eabc30f1e4bbf5c64a516dd 100644 (file)
@@ -5,11 +5,13 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib-test
 
 libjson_la_SOURCES = \
-       json-syntax.c
+       json-syntax.c \
+       json-types.c
 libjson_la_LIBADD = -lm
 
 headers = \
-       json-syntax.h
+       json-syntax.h \
+       json-types.h
 
 test_programs =
 
diff --git a/src/lib-json/json-types.c b/src/lib-json/json-types.c
new file mode 100644 (file)
index 0000000..9a2f37a
--- /dev/null
@@ -0,0 +1,82 @@
+/* Copyright (c) 2017-2023 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+
+#include "json-types.h"
+
+static const char *json_type_names[] = {
+       [JSON_TYPE_NONE] = "<unassigned>",
+       [JSON_TYPE_OBJECT] = "object",
+       [JSON_TYPE_ARRAY] = "array",
+       [JSON_TYPE_STRING] = "string",
+       [JSON_TYPE_NUMBER] = "number",
+       [JSON_TYPE_TRUE] = "true",
+       [JSON_TYPE_FALSE] = "false",
+       [JSON_TYPE_NULL] = "null",
+       [JSON_TYPE_TEXT] = "<JSON-text>",
+};
+static_assert_array_size(json_type_names, JSON_TYPE_TEXT + 1);
+
+static const char *json_content_type_names[] = {
+       [JSON_CONTENT_TYPE_NONE] = "<NONE>",
+       [JSON_CONTENT_TYPE_LIST] = "<LIST>",
+       [JSON_CONTENT_TYPE_STRING] = "<STRING>",
+       [JSON_CONTENT_TYPE_DATA] = "<DATA>",
+       [JSON_CONTENT_TYPE_INTEGER] = "<INTEGER>",
+};
+static_assert_array_size(json_content_type_names,
+                        JSON_CONTENT_TYPE_INTEGER + 1);
+
+const char *json_type_get_name(enum json_type type)
+{
+       i_assert(type <= JSON_TYPE_TEXT);
+       return json_type_names[type];
+}
+
+const char *json_content_type_get_name(enum json_content_type ctype)
+{
+       i_assert(ctype <= JSON_CONTENT_TYPE_INTEGER);
+       return json_content_type_names[ctype];
+}
+
+const char *json_node_get_label(const struct json_node *jnode)
+{
+       switch (jnode->type) {
+       case JSON_TYPE_NONE:
+               return "<unassigned>";
+       case JSON_TYPE_OBJECT:
+               switch (jnode->value.content_type) {
+               case JSON_CONTENT_TYPE_NONE:
+                       return "object end";
+               case JSON_CONTENT_TYPE_LIST:
+                       return "object";
+               default:
+                       break;
+               }
+               break;
+       case JSON_TYPE_ARRAY:
+               switch (jnode->value.content_type) {
+               case JSON_CONTENT_TYPE_NONE:
+                       return "array end";
+               case JSON_CONTENT_TYPE_LIST:
+                       return "array";
+               default:
+                       break;
+               }
+               break;
+       case JSON_TYPE_STRING:
+       case JSON_TYPE_NUMBER:
+       case JSON_TYPE_TEXT:
+               return t_strconcat(
+                       json_type_get_name(jnode->type), " (",
+                       json_content_type_get_name(jnode->value.content_type),
+                       ")", NULL);
+       case JSON_TYPE_TRUE:
+               return "true";
+       case JSON_TYPE_FALSE:
+               return "false";
+       case JSON_TYPE_NULL:
+               return "null";
+       }
+       i_unreached();
+}
diff --git a/src/lib-json/json-types.h b/src/lib-json/json-types.h
new file mode 100644 (file)
index 0000000..42d28b4
--- /dev/null
@@ -0,0 +1,533 @@
+#ifndef JSON_TYPES_H
+#define JSON_TYPES_H
+
+struct json_data;
+struct json_value;
+struct json_node;
+
+/*
+ * Value
+ */
+
+/* The type of the JSON value */
+enum json_type {
+       /* no value */
+       JSON_TYPE_NONE = 0,
+       /* object */
+       JSON_TYPE_OBJECT,
+       /* array */
+       JSON_TYPE_ARRAY,
+       /* string */
+       JSON_TYPE_STRING,
+       /* number */
+       JSON_TYPE_NUMBER,
+       /* true */
+       JSON_TYPE_TRUE,
+       /* false */
+       JSON_TYPE_FALSE,
+       /* null */
+       JSON_TYPE_NULL,
+       /* JSON-text */
+       JSON_TYPE_TEXT,
+};
+
+/* The content representation of the JSON value */
+enum json_content_type {
+       /* true, false, null */
+       JSON_CONTENT_TYPE_NONE = 0,
+       /* object, array */
+       JSON_CONTENT_TYPE_LIST,
+       /* literal/string/text */
+       JSON_CONTENT_TYPE_STRING,
+       /* data buffer (for string/text containing \u0000) */
+       JSON_CONTENT_TYPE_DATA,
+       /* integer number */
+       JSON_CONTENT_TYPE_INTEGER,
+};
+
+struct json_data {
+       /* data[size] == 0x00 */
+       const unsigned char *data;
+       size_t size;
+
+       /* data contains 0x00 */
+       bool contains_nul:1;
+       /* data contains control character */
+       bool contains_control:1;
+};
+
+struct json_value {
+       enum json_content_type content_type;
+       union {
+               /* JSON_CONTENT_TYPE_STRING */
+               const char *str;
+               /* JSON_CONTENT_TYPE_DATA */
+               struct json_data *data;
+               /* JSON_CONTENT_TYPE_INTEGER */
+               intmax_t intnum;
+       } content;
+};
+
+const char *json_type_get_name(enum json_type type);
+const char *json_content_type_get_name(enum json_content_type ctype);
+
+/* number */
+
+static inline int
+json_value_get_uinteger(const struct json_value *jvalue, uintmax_t *num_r)
+{
+       i_assert(jvalue->content_type == JSON_CONTENT_TYPE_INTEGER);
+       if (jvalue->content.intnum < 0)
+               return -1;
+       *num_r = (uintmax_t)jvalue->content.intnum;
+       return 0;
+}
+
+static inline int
+json_value_get_integer(const struct json_value *jvalue, intmax_t *num_r)
+{
+       i_assert(jvalue->content_type == JSON_CONTENT_TYPE_INTEGER);
+       *num_r = jvalue->content.intnum;
+       return 0;
+}
+
+#define JSON_VALUE_GET_U__TEMPLATE(name, type, uint_max)                \
+static inline int                                                       \
+name(const struct json_value *jvalue, type *num_r)                      \
+{                                                                       \
+       intmax_t l;                                                     \
+       i_assert(jvalue->content_type == JSON_CONTENT_TYPE_INTEGER);    \
+       l = jvalue->content.intnum;                                     \
+       if (l < 0 || (uintmax_t)l > uint_max)                           \
+               return -1;                                              \
+       *num_r = (type)l;                                               \
+       return 0;                                                       \
+}
+
+JSON_VALUE_GET_U__TEMPLATE(json_value_get_uint,
+                           unsigned int, UINT_MAX)
+JSON_VALUE_GET_U__TEMPLATE(json_value_get_ulong,
+                           unsigned long, ULONG_MAX)
+JSON_VALUE_GET_U__TEMPLATE(json_value_get_ullong,
+                           unsigned long long, ULLONG_MAX)
+JSON_VALUE_GET_U__TEMPLATE(json_value_get_uint32,
+                           uint32_t, UINT32_MAX)
+JSON_VALUE_GET_U__TEMPLATE(json_value_get_uint64,
+                           uint64_t, UINT64_MAX)
+
+#define JSON_VALUE_GET_S__TEMPLATE(name, type, int_min, int_max)        \
+static inline int                                                       \
+name(const struct json_value *jvalue, type *num_r)                      \
+{                                                                       \
+       intmax_t l;                                                     \
+       i_assert(jvalue->content_type == JSON_CONTENT_TYPE_INTEGER);    \
+       l = jvalue->content.intnum;                                     \
+       if (l < int_min || l > int_max)                                 \
+               return -1;                                              \
+       *num_r = (type)l;                                               \
+       return 0;                                                       \
+}
+
+JSON_VALUE_GET_S__TEMPLATE(json_value_get_int,
+                           int, INT_MIN, INT_MAX)
+JSON_VALUE_GET_S__TEMPLATE(json_value_get_long,
+                           long, LONG_MIN, LONG_MAX)
+JSON_VALUE_GET_S__TEMPLATE(json_value_get_llong,
+                           long long, LLONG_MIN, LLONG_MAX)
+JSON_VALUE_GET_S__TEMPLATE(json_value_get_int32,
+                           int32_t, INT32_MIN, INT32_MAX)
+JSON_VALUE_GET_S__TEMPLATE(json_value_get_int64,
+                           int64_t, INT64_MIN, INT64_MAX)
+
+#define JSON_VALUE_GET_F__TEMPLATE(name, type)                          \
+static inline int                                                       \
+name(const struct json_value *jvalue, type *num_r)                      \
+{                                                                       \
+       if (jvalue->content_type == JSON_CONTENT_TYPE_INTEGER)          \
+               *num_r = (type)jvalue->content.intnum;                  \
+       else                                                            \
+               i_unreached();                                          \
+       return 0;                                                       \
+}
+
+/* string */
+
+static inline ATTR_PURE const char *
+json_value_get_str(const struct json_value *jvalue)
+{
+       i_assert(jvalue->content_type == JSON_CONTENT_TYPE_STRING);
+       return jvalue->content.str;
+}
+static inline ATTR_PURE const char *
+json_value_as_str(const struct json_value *jvalue)
+{
+       switch (jvalue->content_type) {
+       case JSON_CONTENT_TYPE_STRING:
+               break;
+       case JSON_CONTENT_TYPE_INTEGER:
+               return t_strdup_printf("%"PRIdMAX, jvalue->content.intnum);
+       default:
+               i_unreached();
+       }
+       return jvalue->content.str;
+}
+static inline ATTR_PURE const unsigned char *
+json_value_get_data(const struct json_value *jvalue, size_t *size_r)
+{
+       switch (jvalue->content_type) {
+       case JSON_CONTENT_TYPE_STRING:
+               *size_r = strlen(jvalue->content.str);
+               return (const unsigned char *)jvalue->content.str;
+       case JSON_CONTENT_TYPE_DATA:
+               *size_r = jvalue->content.data->size;
+               return jvalue->content.data->data;
+       default:
+               break;
+       }
+       i_unreached();
+}
+
+/*
+ * Node
+ */
+
+struct json_node {
+       /* object member name */
+       const char *name;
+       /* node type */
+       enum json_type type;
+       /* node value */
+       struct json_value value;
+};
+
+static inline ATTR_PURE bool
+json_node_is_none(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_NONE);
+}
+
+const char *json_node_get_label(const struct json_node *jnode);
+
+/* object, array */
+
+static inline ATTR_PURE bool
+json_node_is_object(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_OBJECT &&
+               jnode->value.content_type != JSON_CONTENT_TYPE_NONE);
+}
+
+static inline ATTR_PURE bool
+json_node_is_array(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_ARRAY &&
+               jnode->value.content_type != JSON_CONTENT_TYPE_NONE);
+}
+
+static inline ATTR_PURE bool
+json_node_is_object_end(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_OBJECT &&
+               jnode->value.content_type == JSON_CONTENT_TYPE_NONE);
+}
+
+static inline ATTR_PURE bool
+json_node_is_array_end(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_ARRAY &&
+               jnode->value.content_type == JSON_CONTENT_TYPE_NONE);
+}
+
+static inline ATTR_PURE bool
+json_node_is_end(const struct json_node *jnode)
+{
+       switch (jnode->type) {
+       case JSON_TYPE_OBJECT:
+       case JSON_TYPE_ARRAY:
+               return (jnode->value.content_type == JSON_CONTENT_TYPE_NONE);
+       default:
+               break;
+       }
+       return FALSE;
+}
+
+/* string */
+
+static inline ATTR_PURE bool
+json_node_is_string(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_STRING);
+}
+
+static inline ATTR_PURE const char *
+json_node_get_str(const struct json_node *jnode)
+{
+       switch (jnode->type) {
+       case JSON_TYPE_STRING:
+       case JSON_TYPE_NUMBER:
+       case JSON_TYPE_TEXT:
+               break;
+       case JSON_TYPE_TRUE:
+               return "true";
+       case JSON_TYPE_FALSE:
+               return "false";
+       case JSON_TYPE_NULL:
+               return "null";
+       default:
+               i_unreached();
+       }
+       return json_value_get_str(&jnode->value);
+}
+static inline ATTR_PURE const char *
+json_node_as_str(const struct json_node *jnode)
+{
+       switch (jnode->type) {
+       case JSON_TYPE_STRING:
+       case JSON_TYPE_NUMBER:
+       case JSON_TYPE_TEXT:
+               break;
+       case JSON_TYPE_TRUE:
+               return "true";
+       case JSON_TYPE_FALSE:
+               return "false";
+       case JSON_TYPE_NULL:
+               return "null";
+       default:
+               i_unreached();
+       }
+       return json_value_as_str(&jnode->value);
+}
+
+static inline ATTR_PURE const unsigned char *
+json_node_get_data(const struct json_node *jnode, size_t *size_r)
+{
+       const char *literal;
+
+       switch (jnode->type) {
+       case JSON_TYPE_STRING:
+       case JSON_TYPE_NUMBER:
+       case JSON_TYPE_TEXT:
+               break;
+       case JSON_TYPE_TRUE:
+               literal = "true";
+               *size_r = strlen(literal);
+               return (const unsigned char *)literal;
+       case JSON_TYPE_FALSE:
+               literal = "false";
+               *size_r = strlen(literal);
+               return (const unsigned char *)literal;
+       case JSON_TYPE_NULL:
+               literal = "null";
+               *size_r = strlen(literal);
+               return (const unsigned char *)literal;
+       default:
+               i_unreached();
+       }
+       return json_value_get_data(&jnode->value, size_r);
+}
+
+/* number */
+
+static inline ATTR_PURE bool
+json_node_is_number(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_NUMBER);
+}
+
+static inline int
+json_node_get_intmax(const struct json_node *jnode, intmax_t *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_integer(&jnode->value, num_r);
+}
+static inline int
+json_node_get_int(const struct json_node *jnode, int *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_int(&jnode->value, num_r);
+}
+static inline int
+json_node_get_long(const struct json_node *jnode, long *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_long(&jnode->value, num_r);
+}
+static inline int
+json_node_get_llong(const struct json_node *jnode, long long *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_llong(&jnode->value, num_r);
+}
+static inline int
+json_node_get_int32(const struct json_node *jnode, int32_t *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_int32(&jnode->value, num_r);
+}
+static inline int
+json_node_get_int64(const struct json_node *jnode, int64_t *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_int64(&jnode->value, num_r);
+}
+
+static inline int
+json_node_get_uintmax(const struct json_node *jnode, uintmax_t *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_uinteger(&jnode->value, num_r);
+}
+static inline int
+json_node_get_uint(const struct json_node *jnode, unsigned int *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_uint(&jnode->value, num_r);
+}
+static inline int
+json_node_get_ulong(const struct json_node *jnode, unsigned long *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_ulong(&jnode->value, num_r);
+}
+static inline int
+json_node_get_ullong(const struct json_node *jnode, unsigned long long *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_ullong(&jnode->value, num_r);
+}
+static inline int
+json_node_get_uint32(const struct json_node *jnode, uint32_t *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_uint32(&jnode->value, num_r);
+}
+static inline int
+json_node_get_uint64(const struct json_node *jnode, uint64_t *num_r)
+{
+       if (jnode->type != JSON_TYPE_NUMBER)
+               return -1;
+       if (jnode->value.content_type != JSON_CONTENT_TYPE_INTEGER)
+               return -1;
+       return json_value_get_uint64(&jnode->value, num_r);
+}
+
+/* true, false */
+
+static inline ATTR_PURE bool
+json_node_is_true(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_TRUE);
+}
+
+static inline ATTR_PURE bool
+json_node_is_false(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_FALSE);
+}
+
+static inline ATTR_PURE bool
+json_node_is_boolean(const struct json_node *jnode)
+{
+       switch (jnode->type) {
+       case JSON_TYPE_TRUE:
+       case JSON_TYPE_FALSE:
+               return TRUE;
+       default:
+               break;
+       }
+       return FALSE;
+}
+
+static inline ATTR_PURE int
+json_node_get_boolean(const struct json_node *jnode, bool *bool_r)
+{
+       switch (jnode->type) {
+       case JSON_TYPE_TRUE:
+               *bool_r = TRUE;
+               return 0;
+       case JSON_TYPE_FALSE:
+               *bool_r = FALSE;
+               return 0;
+       default:
+               break;
+       }
+       return -1;
+}
+
+/* null */
+
+static inline ATTR_PURE bool
+json_node_is_null(const struct json_node *jnode)
+{
+       return (jnode->type == JSON_TYPE_NULL);
+}
+
+/* utility */
+
+static inline ATTR_PURE bool
+json_node_is_singular(const struct json_node *jnode)
+{
+       switch (jnode->type) {
+       case JSON_TYPE_OBJECT:
+       case JSON_TYPE_ARRAY:
+               return FALSE;
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+/*
+ * Limits
+ */
+
+#define JSON_DEFAULT_MAX_NAME_SIZE 1024
+#define JSON_DEFAULT_MAX_STRING_SIZE 32*1024
+#define JSON_DEFAULT_MAX_NESTING 32
+#define JSON_DEFAULT_MAX_LIST_ITEMS 1024
+
+struct json_limits {
+       /* Maximum size of object member name */
+       size_t max_name_size;
+       /* Maximum length of a string */
+       size_t max_string_size;
+       /* Maximum depth of object/array nesting */
+       unsigned int max_nesting;
+       /* Maximum number of object/array items */
+       unsigned int max_list_items;
+};
+
+#endif