diff --git a/Makefile b/Makefile
index e2f389cf0de..c95128f3cec 100644
--- a/Makefile
+++ b/Makefile
@@ -91,6 +91,7 @@ $(ARCH_DISTDIR)/%: %
$(INSTALL_DATA) $< $@
LIBDIRS = \
+ lib/external/parson \
lib/external/shapelib \
lib/datetime \
lib/gis \
diff --git a/grasslib.dox b/grasslib.dox
index ee2323ddb30..25d6ee9179d 100644
--- a/grasslib.dox
+++ b/grasslib.dox
@@ -122,7 +122,7 @@ href="https://grass.osgeo.org">https://grass.osgeo.org
\subsection misclibs Miscellaneous Libraries
- datetime: \ref datetime (DateTime library)
- - external: \ref external (External libraries from other projects such as shapelib and \ref ccmathlib)
+ - external: \ref external (External libraries from other projects such as shapelib, parson and \ref ccmathlib)
- fonts: \ref fonts (GRASS fonts library)
- init: \ref init (GRASS initialization code + scripts)
- iostream: \ref iostream (fast I/O library)
@@ -131,6 +131,22 @@ href="https://grass.osgeo.org">https://grass.osgeo.org
- manage: \ref managelib
- symbol: \ref symbol (Drawing symbols for %point %vector data library)
+\subsection external Adding External Libraries
+
+ The following files must be added or updated when including a new external library.
+
+ - add: lib/external/newlib
+ - add: lib/external/newlib/*.c
+ - add: lib/external/newlib/*.h
+ - add: lib/external/newlib/LICENSE
+ - add: lib/external/newlib/Makefile
+ - update: lib/external/Makefile
+ - update: lib/external/README.license
+ - update: lib/README.md
+ - update: include/Make/Install.make
+ - update: include/Make/Grass.make
+ - update: Makefile
+
\section location File structure of GRASS Location
A GRASS raster map consists of several files in several subdirectories in a mapset,
diff --git a/include/Make/Grass.make b/include/Make/Grass.make
index 09657591479..137a45d1955 100644
--- a/include/Make/Grass.make
+++ b/include/Make/Grass.make
@@ -152,6 +152,7 @@ libs = \
NVIZ:nviz \
OGSF:ogsf \
OPTRI:optri \
+ PARSON:parson \
PNGDRIVER:pngdriver \
PSDRIVER:psdriver \
QTREE:qtree \
diff --git a/include/Make/Install.make b/include/Make/Install.make
index cef4f810ffe..b9a5e45e0fc 100644
--- a/include/Make/Install.make
+++ b/include/Make/Install.make
@@ -212,6 +212,7 @@ srclibsdist: distclean
-cp -rL utils ./grass-lib-$(GRASS_VERSION_NUMBER)
-cp -rL demolocation ./grass-lib-$(GRASS_VERSION_NUMBER)
-cp -rL include ./grass-lib-$(GRASS_VERSION_NUMBER)
+ -cp -rL --parents lib/external/parson ./grass-lib-$(GRASS_VERSION_NUMBER)
-cp -rL --parents lib/external/shapelib ./grass-lib-$(GRASS_VERSION_NUMBER)
-cp -rL --parents lib/external/bwidget ./grass-lib-$(GRASS_VERSION_NUMBER)
-cp -rL --parents lib/datetime ./grass-lib-$(GRASS_VERSION_NUMBER)
diff --git a/lib/README b/lib/README
index e5f72ec1f74..f694dd267ab 100644
--- a/lib/README
+++ b/lib/README
@@ -11,6 +11,7 @@ Current directory layout
- display: library for CELL driver
- edit: curses library
- external: external libraries
+ - external/parson: JSON serialization and deserialization functions
- external/shapelib: SHAPE file management functions
- fonts: fonts
- fonts/fonts
diff --git a/lib/external/Makefile b/lib/external/Makefile
index 5453bb931f0..6c28110144c 100644
--- a/lib/external/Makefile
+++ b/lib/external/Makefile
@@ -3,6 +3,7 @@ MODULE_TOPDIR = ../..
SUBDIRS = \
ccmath \
+ parson \
shapelib
include $(MODULE_TOPDIR)/include/Make/Dir.make
diff --git a/lib/external/README.license b/lib/external/README.license
index d36c4b5b03e..071160af0cc 100644
--- a/lib/external/README.license
+++ b/lib/external/README.license
@@ -17,3 +17,7 @@ comments carefully.
* shapelib/ (MIT/X)
Shapefile read/write functions.
Copyright (c) 2007, Frank Warmerdam
+
+ * parson/ (MIT/X)
+ JSON parsing and encoding functions version 1.5.2.
+ Copyright (c) 2022, Krzysztof Gabis
diff --git a/lib/external/README.md b/lib/external/README.md
new file mode 100644
index 00000000000..ef26276acff
--- /dev/null
+++ b/lib/external/README.md
@@ -0,0 +1,120 @@
+# How to add a new external library
+
+The following procedure will walk you through adding a new third-party
+library to GRASS GIS.
+
+## License
+
+Before you start, make sure that the library you want to include is in
+compliance with the GRASS GIS licence.
+
+### Approved Third-Party Licenses
+
+* MIT/X
+* GPLv2
+* LGPL 2.1
+* MPL 2.0
+* BSD-3-Clause
+* Public Domain
+
+## Procedure
+
+* Create a directory that will contain the new library's files
+in `lib/external/`
+
+* Add the new libraries header and source files, license, and README to the newly
+created directory.
+
+* Add a new `Makefile` to the directory.
+
+```make
+MODULE_TOPDIR = ../../..
+
+# replace SHAPE with new library name
+LIB = SHAPE
+
+include $(MODULE_TOPDIR)/include/Make/Lib.make
+
+default: headers
+ $(MAKE) lib
+
+# Update header file reference to the new library
+headers: $(ARCH_INCDIR)/shapefil.h
+
+$(ARCH_INCDIR)/%.h: %.h
+ $(INSTALL_DATA) $< $@
+```
+
+* Update `lib/external/Makefile` to include the new subdirectory.
+
+```make
+MODULE_TOPDIR = ../..
+
+SUBDIRS = \
+ ccmath \
+ parson \
+ shapelib \
+ # Add new directory here
+
+include $(MODULE_TOPDIR)/include/Make/Dir.make
+
+default: parsubdirs
+```
+
+* Update the `lib/external/README.license` with a new entry containing
+ the *library name*, *license*, *description*, *version*, *copyright*,
+ and *authors name*.
+
+```txt
+* parson/ (MIT/X)
+ JSON parsing and encoding functions version 1.5.2.
+ Copyright (c) 2022, Krzysztof Gabis
+```
+
+* Update `lib/README` with a new entry under external libraries.
+
+```md
+ external: external libraries
+ - external/parson: JSON serialization and deserialization functions
+ - external/shapelib: SHAPE file management functions
+
+```
+
+* Update `include/Make/Install.make`
+
+```make
+-cp -rL --parents lib/external/ ./grass-lib-$(GRASS_VERSION_NUMBER)
+```
+
+* Add reference to library in `include/Make/Grass.make` in alphabetical order.
+
+```make
+:
+
+# example for the parson library
+PARSON:parson \
+```
+
+* Add reference to library in the root `Makefile`.
+
+```make
+LIBDIRS = \
+ lib/external/parson \
+ lib/external/shapelib \
+ # New reference
+ ...
+```
+
+* The library should now be able to successfully compile.
+
+To test run the `make` command.
+
+```bash
+make
+```
+
+* If no errors are found the library should now be able to be used in development.
+
+```c
+#include
+```
diff --git a/lib/external/parson/LICENSE b/lib/external/parson/LICENSE
new file mode 100644
index 00000000000..4431a611d24
--- /dev/null
+++ b/lib/external/parson/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2012 - 2022 Krzysztof Gabis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lib/external/parson/Makefile b/lib/external/parson/Makefile
new file mode 100644
index 00000000000..43aca3bc351
--- /dev/null
+++ b/lib/external/parson/Makefile
@@ -0,0 +1,13 @@
+MODULE_TOPDIR = ../../..
+
+LIB = PARSON
+
+include $(MODULE_TOPDIR)/include/Make/Lib.make
+
+default: headers
+ $(MAKE) lib
+
+headers: $(ARCH_INCDIR)/parson.h
+
+$(ARCH_INCDIR)/%.h: %.h
+ $(INSTALL_DATA) $< $@
diff --git a/lib/external/parson/README.md b/lib/external/parson/README.md
new file mode 100644
index 00000000000..2557a8f7943
--- /dev/null
+++ b/lib/external/parson/README.md
@@ -0,0 +1,189 @@
+# Parson
+
+## About
+
+Parson is a lightweight [json](http://json.org) library written in C.
+
+## Features
+
+* Lightweight (only 2 files)
+* Simple API
+* Addressing json values with dot notation (similar to C structs or objects
+in most OO languages, e.g. "objectA.objectB.value")
+* C89 compatible
+* Test suites
+
+## Installation
+
+Run:
+
+```bash
+git clone https://github.com/kgabis/parson.git
+```
+
+and copy parson.h and parson.c to you source code tree.
+
+Run ```bash make test``` to compile and run tests.
+
+## Examples
+
+### Parsing JSON
+
+Here is a function, which prints basic commit info (date, sha and author) from
+a github repository.
+
+```c
+void print_commits_info(const char *username, const char *repo) {
+ JSON_Value *root_value;
+ JSON_Array *commits;
+ JSON_Object *commit;
+ size_t i;
+
+ char curl_command[512];
+ char cleanup_command[256];
+ char output_filename[] = "commits.json";
+
+ /* it ain't pretty, but it's not a libcurl tutorial */
+ sprintf(curl_command,
+ "curl -s \"https://api.github.com/repos/%s/%s/commits\" > %s",
+ username, repo, output_filename);
+ sprintf(cleanup_command, "rm -f %s", output_filename);
+ system(curl_command);
+
+ /* parsing json and validating output */
+ root_value = json_parse_file(output_filename);
+ if (json_value_get_type(root_value) != JSONArray) {
+ system(cleanup_command);
+ return;
+ }
+
+ /* getting array from root value and printing commit info */
+ commits = json_value_get_array(root_value);
+ printf("%-10.10s %-10.10s %s\n", "Date", "SHA", "Author");
+ for (i = 0; i < json_array_get_count(commits); i++) {
+ commit = json_array_get_object(commits, i);
+ printf("%.10s %.10s %s\n",
+ json_object_dotget_string(commit, "commit.author.date"),
+ json_object_get_string(commit, "sha"),
+ json_object_dotget_string(commit, "commit.author.name"));
+ }
+
+ /* cleanup code */
+ json_value_free(root_value);
+ system(cleanup_command);
+}
+```
+
+Calling
+
+```c
+print_commits_info("torvalds", "linux");
+
+```
+
+prints:
+
+```csv
+Date SHA Author
+2012-10-15 dd8e8c4a2c David Rientjes
+2012-10-15 3ce9e53e78 Michal Marek
+2012-10-14 29bb4cc5e0 Randy Dunlap
+2012-10-15 325adeb55e Ralf Baechle
+2012-10-14 68687c842c Russell King
+2012-10-14 ddffeb8c4d Linus Torvalds
+...
+```
+
+### Persistence
+
+In this example I'm using parson to save user information to a file and then
+load it and validate later.
+
+```c
+void persistence_example(void) {
+ JSON_Value *schema = json_parse_string("{\"name\":\"\"}");
+ JSON_Value *user_data = json_parse_file("user_data.json");
+ char buf[256];
+ const char *name = NULL;
+ if (user_data == NULL || json_validate(schema, user_data) != JSONSuccess) {
+ puts("Enter your name:");
+ scanf("%s", buf);
+ user_data = json_value_init_object();
+ json_object_set_string(json_object(user_data), "name", buf);
+ json_serialize_to_file(user_data, "user_data.json");
+ }
+ name = json_object_get_string(json_object(user_data), "name");
+ printf("Hello, %s.", name);
+ json_value_free(schema);
+ json_value_free(user_data);
+ return;
+}
+```
+
+### Serialization
+
+Creating JSON values is very simple thanks to the dot notation.
+Object hierarchy is automatically created when addressing specific fields.
+In the following example I create a simple JSON value containing basic
+information about a person.
+
+```c
+void serialization_example(void) {
+ JSON_Value *root_value = json_value_init_object();
+ JSON_Object *root_object = json_value_get_object(root_value);
+ char *serialized_string = NULL;
+ json_object_set_string(root_object, "name", "John Smith");
+ json_object_set_number(root_object, "age", 25);
+ json_object_dotset_string(root_object, "address.city", "Cupertino");
+ json_object_dotset_value(
+ root_object,
+ "contact.emails",
+ json_parse_string(
+ "[\"email@example.com\",
+ \"email2@example.com\"]"
+ )
+ );
+ serialized_string = json_serialize_to_string_pretty(root_value);
+ puts(serialized_string);
+ json_free_serialized_string(serialized_string);
+ json_value_free(root_value);
+}
+
+```
+
+Output:
+
+```json
+{
+ "name": "John Smith",
+ "age": 25,
+ "address": {
+ "city": "Cupertino"
+ },
+ "contact": {
+ "emails": [
+ "email@example.com",
+ "email2@example.com"
+ ]
+ }
+}
+```
+
+## Contributing
+
+I will always merge *working* bug fixes. However, if you want to add something
+new to the API, please create an "issue" on github for this first so we can
+discuss if it should end up in the library before you start implementing it.
+Remember to follow parson's code style and write appropriate tests.
+
+## My other projects
+
+* [ape](https://github.com/kgabis/ape) - simple programming language
+implemented in C library
+* [kgflags](https://github.com/kgabis/kgflags) - easy to use command-line flag
+parsing library
+* [agnes](https://github.com/kgabis/agnes) - header-only NES emulation library
+
+## License
+
+[The MIT License (MIT)](http://opensource.org/licenses/mit-license.php)
diff --git a/lib/external/parson/parson.c b/lib/external/parson/parson.c
new file mode 100644
index 00000000000..21490938bb7
--- /dev/null
+++ b/lib/external/parson/parson.c
@@ -0,0 +1,2855 @@
+/*
+ SPDX-License-Identifier: MIT
+
+ Parson 1.5.3 (https://github.com/kgabis/parson)
+ Copyright (c) 2012 - 2023 Krzysztof Gabis
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+#ifdef _MSC_VER
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif /* _CRT_SECURE_NO_WARNINGS */
+#endif /* _MSC_VER */
+
+#include "parson.h"
+
+#define PARSON_IMPL_VERSION_MAJOR 1
+#define PARSON_IMPL_VERSION_MINOR 5
+#define PARSON_IMPL_VERSION_PATCH 3
+
+#if (PARSON_VERSION_MAJOR != PARSON_IMPL_VERSION_MAJOR) || \
+ (PARSON_VERSION_MINOR != PARSON_IMPL_VERSION_MINOR) || \
+ (PARSON_VERSION_PATCH != PARSON_IMPL_VERSION_PATCH)
+#error "parson version mismatch between parson.c and parson.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Apparently sscanf is not implemented in some "standard" libraries, so don't
+ * use it, if you don't have to. */
+#ifdef sscanf
+#undef sscanf
+#define sscanf THINK_TWICE_ABOUT_USING_SSCANF
+#endif
+
+/* strcpy is unsafe */
+#ifdef strcpy
+#undef strcpy
+#endif
+#define strcpy USE_MEMCPY_INSTEAD_OF_STRCPY
+
+#define STARTING_CAPACITY 16
+#define MAX_NESTING 2048
+
+#ifndef PARSON_DEFAULT_FLOAT_FORMAT
+#define PARSON_DEFAULT_FLOAT_FORMAT \
+ "%1.17g" /* do not increase precision without incresing NUM_BUF_SIZE */
+#endif
+
+#ifndef PARSON_NUM_BUF_SIZE
+#define PARSON_NUM_BUF_SIZE \
+ 64 /* double printed with "%1.17g" shouldn't be longer than 25 bytes so \
+ let's be paranoid and use 64 */
+#endif
+
+#ifndef PARSON_INDENT_STR
+#define PARSON_INDENT_STR " "
+#endif
+
+#define SIZEOF_TOKEN(a) (sizeof(a) - 1)
+#define SKIP_CHAR(str) ((*str)++)
+#define SKIP_WHITESPACES(str) \
+ while (isspace((unsigned char)(**str))) { \
+ SKIP_CHAR(str); \
+ }
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#undef malloc
+#undef free
+
+#if defined(isnan) && defined(isinf)
+#define IS_NUMBER_INVALID(x) (isnan((x)) || isinf((x)))
+#else
+#define IS_NUMBER_INVALID(x) (((x)*0.0) != 0.0)
+#endif
+
+#define OBJECT_INVALID_IX ((size_t)-1)
+
+static JSON_Malloc_Function parson_malloc = malloc;
+static JSON_Free_Function parson_free = free;
+
+static int parson_escape_slashes = 1;
+
+static char *parson_float_format = NULL;
+
+static JSON_Number_Serialization_Function parson_number_serialization_function =
+ NULL;
+
+#define IS_CONT(b) \
+ (((unsigned char)(b)&0xC0) == 0x80) /* is utf-8 continuation byte */
+
+typedef int parson_bool_t;
+
+#define PARSON_TRUE 1
+#define PARSON_FALSE 0
+
+typedef struct json_string {
+ char *chars;
+ size_t length;
+} JSON_String;
+
+/* Type definitions */
+typedef union json_value_value {
+ JSON_String string;
+ double number;
+ JSON_Object *object;
+ JSON_Array *array;
+ int boolean;
+ int null;
+} JSON_Value_Value;
+
+struct json_value_t {
+ JSON_Value *parent;
+ JSON_Value_Type type;
+ JSON_Value_Value value;
+};
+
+struct json_object_t {
+ JSON_Value *wrapping_value;
+ size_t *cells;
+ unsigned long *hashes;
+ char **names;
+ JSON_Value **values;
+ size_t *cell_ixs;
+ size_t count;
+ size_t item_capacity;
+ size_t cell_capacity;
+};
+
+struct json_array_t {
+ JSON_Value *wrapping_value;
+ JSON_Value **items;
+ size_t count;
+ size_t capacity;
+};
+
+/* Various */
+static char *read_file(const char *filename);
+static void remove_comments(char *string, const char *start_token,
+ const char *end_token);
+static char *parson_strndup(const char *string, size_t n);
+static char *parson_strdup(const char *string);
+static int parson_sprintf(char *s, const char *format, ...);
+static int hex_char_to_int(char c);
+static JSON_Status parse_utf16_hex(const char *string, unsigned int *result);
+static int num_bytes_in_utf8_sequence(unsigned char c);
+static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len);
+static parson_bool_t is_valid_utf8(const char *string, size_t string_len);
+static parson_bool_t is_decimal(const char *string, size_t length);
+static unsigned long hash_string(const char *string, size_t n);
+
+/* JSON Object */
+static JSON_Object *json_object_make(JSON_Value *wrapping_value);
+static JSON_Status json_object_init(JSON_Object *object, size_t capacity);
+static void json_object_deinit(JSON_Object *object, parson_bool_t free_keys,
+ parson_bool_t free_values);
+static JSON_Status json_object_grow_and_rehash(JSON_Object *object);
+static size_t json_object_get_cell_ix(const JSON_Object *object,
+ const char *key, size_t key_len,
+ unsigned long hash,
+ parson_bool_t *out_found);
+static JSON_Status json_object_add(JSON_Object *object, char *name,
+ JSON_Value *value);
+static JSON_Value *json_object_getn_value(const JSON_Object *object,
+ const char *name, size_t name_len);
+static JSON_Status json_object_remove_internal(JSON_Object *object,
+ const char *name,
+ parson_bool_t free_value);
+static JSON_Status json_object_dotremove_internal(JSON_Object *object,
+ const char *name,
+ parson_bool_t free_value);
+static void json_object_free(JSON_Object *object);
+
+/* JSON Array */
+static JSON_Array *json_array_make(JSON_Value *wrapping_value);
+static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value);
+static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity);
+static void json_array_free(JSON_Array *array);
+
+/* JSON Value */
+static JSON_Value *json_value_init_string_no_copy(char *string, size_t length);
+static const JSON_String *json_value_get_string_desc(const JSON_Value *value);
+
+/* Parser */
+static JSON_Status skip_quotes(const char **string);
+static JSON_Status parse_utf16(const char **unprocessed, char **processed);
+static char *process_string(const char *input, size_t input_len,
+ size_t *output_len);
+static char *get_quoted_string(const char **string, size_t *output_string_len);
+static JSON_Value *parse_object_value(const char **string, size_t nesting);
+static JSON_Value *parse_array_value(const char **string, size_t nesting);
+static JSON_Value *parse_string_value(const char **string);
+static JSON_Value *parse_boolean_value(const char **string);
+static JSON_Value *parse_number_value(const char **string);
+static JSON_Value *parse_null_value(const char **string);
+static JSON_Value *parse_value(const char **string, size_t nesting);
+
+/* Serialization */
+static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf,
+ int level, parson_bool_t is_pretty,
+ char *num_buf);
+static int json_serialize_string(const char *string, size_t len, char *buf);
+
+/* Various */
+static char *read_file(const char *filename)
+{
+ FILE *fp = fopen(filename, "r");
+ size_t size_to_read = 0;
+ size_t size_read = 0;
+ long pos;
+ char *file_contents;
+ if (!fp) {
+ return NULL;
+ }
+ fseek(fp, 0L, SEEK_END);
+ pos = ftell(fp);
+ if (pos < 0) {
+ fclose(fp);
+ return NULL;
+ }
+ size_to_read = pos;
+ rewind(fp);
+ file_contents = (char *)parson_malloc(sizeof(char) * (size_to_read + 1));
+ if (!file_contents) {
+ fclose(fp);
+ return NULL;
+ }
+ size_read = fread(file_contents, 1, size_to_read, fp);
+ if (size_read == 0 || ferror(fp)) {
+ fclose(fp);
+ parson_free(file_contents);
+ return NULL;
+ }
+ fclose(fp);
+ file_contents[size_read] = '\0';
+ return file_contents;
+}
+
+static void remove_comments(char *string, const char *start_token,
+ const char *end_token)
+{
+ parson_bool_t in_string = PARSON_FALSE, escaped = PARSON_FALSE;
+ size_t i;
+ char *ptr = NULL, current_char;
+ size_t start_token_len = strlen(start_token);
+ size_t end_token_len = strlen(end_token);
+ if (start_token_len == 0 || end_token_len == 0) {
+ return;
+ }
+ while ((current_char = *string) != '\0') {
+ if (current_char == '\\' && !escaped) {
+ escaped = PARSON_TRUE;
+ string++;
+ continue;
+ }
+ else if (current_char == '\"' && !escaped) {
+ in_string = !in_string;
+ }
+ else if (!in_string &&
+ strncmp(string, start_token, start_token_len) == 0) {
+ for (i = 0; i < start_token_len; i++) {
+ string[i] = ' ';
+ }
+ string = string + start_token_len;
+ ptr = strstr(string, end_token);
+ if (!ptr) {
+ return;
+ }
+ for (i = 0; i < (ptr - string) + end_token_len; i++) {
+ string[i] = ' ';
+ }
+ string = ptr + end_token_len - 1;
+ }
+ escaped = PARSON_FALSE;
+ string++;
+ }
+}
+
+static char *parson_strndup(const char *string, size_t n)
+{
+ /* We expect the caller has validated that 'n' fits within the input buffer.
+ */
+ char *output_string = (char *)parson_malloc(n + 1);
+ if (!output_string) {
+ return NULL;
+ }
+ output_string[n] = '\0';
+ memcpy(output_string, string, n);
+ return output_string;
+}
+
+static char *parson_strdup(const char *string)
+{
+ return parson_strndup(string, strlen(string));
+}
+
+static int parson_sprintf(char *s, const char *format, ...)
+{
+ int result;
+ va_list args;
+ va_start(args, format);
+
+#if defined(__APPLE__) && defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ result = vsprintf(s, format, args);
+#if defined(__APPLE__) && defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+ va_end(args);
+ return result;
+}
+
+static int hex_char_to_int(char c)
+{
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ }
+ else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ }
+ else if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ }
+ return -1;
+}
+
+static JSON_Status parse_utf16_hex(const char *s, unsigned int *result)
+{
+ int x1, x2, x3, x4;
+ if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0' || s[3] == '\0') {
+ return JSONFailure;
+ }
+ x1 = hex_char_to_int(s[0]);
+ x2 = hex_char_to_int(s[1]);
+ x3 = hex_char_to_int(s[2]);
+ x4 = hex_char_to_int(s[3]);
+ if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1) {
+ return JSONFailure;
+ }
+ *result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4);
+ return JSONSuccess;
+}
+
+static int num_bytes_in_utf8_sequence(unsigned char c)
+{
+ if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) {
+ return 0;
+ }
+ else if ((c & 0x80) == 0) { /* 0xxxxxxx */
+ return 1;
+ }
+ else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */
+ return 2;
+ }
+ else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */
+ return 3;
+ }
+ else if ((c & 0xF8) == 0xF0) { /* 11110xxx */
+ return 4;
+ }
+ return 0; /* won't happen */
+}
+
+static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len)
+{
+ unsigned int cp = 0;
+ *len = num_bytes_in_utf8_sequence(string[0]);
+
+ if (*len == 1) {
+ cp = string[0];
+ }
+ else if (*len == 2 && IS_CONT(string[1])) {
+ cp = string[0] & 0x1F;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ }
+ else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {
+ cp = ((unsigned char)string[0]) & 0xF;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ cp = (cp << 6) | (string[2] & 0x3F);
+ }
+ else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) &&
+ IS_CONT(string[3])) {
+ cp = string[0] & 0x7;
+ cp = (cp << 6) | (string[1] & 0x3F);
+ cp = (cp << 6) | (string[2] & 0x3F);
+ cp = (cp << 6) | (string[3] & 0x3F);
+ }
+ else {
+ return JSONFailure;
+ }
+
+ /* overlong encodings */
+ if ((cp < 0x80 && *len > 1) || (cp < 0x800 && *len > 2) ||
+ (cp < 0x10000 && *len > 3)) {
+ return JSONFailure;
+ }
+
+ /* invalid unicode */
+ if (cp > 0x10FFFF) {
+ return JSONFailure;
+ }
+
+ /* surrogate halves */
+ if (cp >= 0xD800 && cp <= 0xDFFF) {
+ return JSONFailure;
+ }
+
+ return JSONSuccess;
+}
+
+static int is_valid_utf8(const char *string, size_t string_len)
+{
+ int len = 0;
+ const char *string_end = string + string_len;
+ while (string < string_end) {
+ if (verify_utf8_sequence((const unsigned char *)string, &len) !=
+ JSONSuccess) {
+ return PARSON_FALSE;
+ }
+ string += len;
+ }
+ return PARSON_TRUE;
+}
+
+static parson_bool_t is_decimal(const char *string, size_t length)
+{
+ if (length > 1 && string[0] == '0' && string[1] != '.') {
+ return PARSON_FALSE;
+ }
+ if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') {
+ return PARSON_FALSE;
+ }
+ while (length--) {
+ if (strchr("xX", string[length])) {
+ return PARSON_FALSE;
+ }
+ }
+ return PARSON_TRUE;
+}
+
+static unsigned long hash_string(const char *string, size_t n)
+{
+#ifdef PARSON_FORCE_HASH_COLLISIONS
+ (void)string;
+ (void)n;
+ return 0;
+#else
+ unsigned long hash = 5381;
+ unsigned char c;
+ size_t i = 0;
+ for (i = 0; i < n; i++) {
+ c = string[i];
+ if (c == '\0') {
+ break;
+ }
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+ }
+ return hash;
+#endif
+}
+
+/* JSON Object */
+static JSON_Object *json_object_make(JSON_Value *wrapping_value)
+{
+ JSON_Status res = JSONFailure;
+ JSON_Object *new_obj = (JSON_Object *)parson_malloc(sizeof(JSON_Object));
+ if (new_obj == NULL) {
+ return NULL;
+ }
+ new_obj->wrapping_value = wrapping_value;
+ res = json_object_init(new_obj, 0);
+ if (res != JSONSuccess) {
+ parson_free(new_obj);
+ return NULL;
+ }
+ return new_obj;
+}
+
+static JSON_Status json_object_init(JSON_Object *object, size_t capacity)
+{
+ unsigned int i = 0;
+
+ object->cells = NULL;
+ object->names = NULL;
+ object->values = NULL;
+ object->cell_ixs = NULL;
+ object->hashes = NULL;
+
+ object->count = 0;
+ object->cell_capacity = capacity;
+ object->item_capacity = (unsigned int)(capacity * 7 / 10);
+
+ if (capacity == 0) {
+ return JSONSuccess;
+ }
+
+ object->cells =
+ (size_t *)parson_malloc(object->cell_capacity * sizeof(*object->cells));
+ object->names =
+ (char **)parson_malloc(object->item_capacity * sizeof(*object->names));
+ object->values = (JSON_Value **)parson_malloc(object->item_capacity *
+ sizeof(*object->values));
+ object->cell_ixs = (size_t *)parson_malloc(object->item_capacity *
+ sizeof(*object->cell_ixs));
+ object->hashes = (unsigned long *)parson_malloc(object->item_capacity *
+ sizeof(*object->hashes));
+ if (object->cells == NULL || object->names == NULL ||
+ object->values == NULL || object->cell_ixs == NULL ||
+ object->hashes == NULL) {
+ goto error;
+ }
+ for (i = 0; i < object->cell_capacity; i++) {
+ object->cells[i] = OBJECT_INVALID_IX;
+ }
+ return JSONSuccess;
+error:
+ parson_free(object->cells);
+ parson_free(object->names);
+ parson_free(object->values);
+ parson_free(object->cell_ixs);
+ parson_free(object->hashes);
+ return JSONFailure;
+}
+
+static void json_object_deinit(JSON_Object *object, parson_bool_t free_keys,
+ parson_bool_t free_values)
+{
+ unsigned int i = 0;
+ for (i = 0; i < object->count; i++) {
+ if (free_keys) {
+ parson_free(object->names[i]);
+ }
+ if (free_values) {
+ json_value_free(object->values[i]);
+ }
+ }
+
+ object->count = 0;
+ object->item_capacity = 0;
+ object->cell_capacity = 0;
+
+ parson_free(object->cells);
+ parson_free(object->names);
+ parson_free(object->values);
+ parson_free(object->cell_ixs);
+ parson_free(object->hashes);
+
+ object->cells = NULL;
+ object->names = NULL;
+ object->values = NULL;
+ object->cell_ixs = NULL;
+ object->hashes = NULL;
+}
+
+static JSON_Status json_object_grow_and_rehash(JSON_Object *object)
+{
+ JSON_Value *wrapping_value = NULL;
+ JSON_Object new_object;
+ char *key = NULL;
+ JSON_Value *value = NULL;
+ unsigned int i = 0;
+ size_t new_capacity = MAX(object->cell_capacity * 2, STARTING_CAPACITY);
+ JSON_Status res = json_object_init(&new_object, new_capacity);
+ if (res != JSONSuccess) {
+ return JSONFailure;
+ }
+
+ wrapping_value = json_object_get_wrapping_value(object);
+ new_object.wrapping_value = wrapping_value;
+
+ for (i = 0; i < object->count; i++) {
+ key = object->names[i];
+ value = object->values[i];
+ res = json_object_add(&new_object, key, value);
+ if (res != JSONSuccess) {
+ json_object_deinit(&new_object, PARSON_FALSE, PARSON_FALSE);
+ return JSONFailure;
+ }
+ value->parent = wrapping_value;
+ }
+ json_object_deinit(object, PARSON_FALSE, PARSON_FALSE);
+ *object = new_object;
+ return JSONSuccess;
+}
+
+static size_t json_object_get_cell_ix(const JSON_Object *object,
+ const char *key, size_t key_len,
+ unsigned long hash,
+ parson_bool_t *out_found)
+{
+ size_t cell_ix = hash & (object->cell_capacity - 1);
+ size_t cell = 0;
+ size_t ix = 0;
+ unsigned int i = 0;
+ unsigned long hash_to_check = 0;
+ const char *key_to_check = NULL;
+ size_t key_to_check_len = 0;
+
+ *out_found = PARSON_FALSE;
+
+ for (i = 0; i < object->cell_capacity; i++) {
+ ix = (cell_ix + i) & (object->cell_capacity - 1);
+ cell = object->cells[ix];
+ if (cell == OBJECT_INVALID_IX) {
+ return ix;
+ }
+ hash_to_check = object->hashes[cell];
+ if (hash != hash_to_check) {
+ continue;
+ }
+ key_to_check = object->names[cell];
+ key_to_check_len = strlen(key_to_check);
+ if (key_to_check_len == key_len &&
+ strncmp(key, key_to_check, key_len) == 0) {
+ *out_found = PARSON_TRUE;
+ return ix;
+ }
+ }
+ return OBJECT_INVALID_IX;
+}
+
+static JSON_Status json_object_add(JSON_Object *object, char *name,
+ JSON_Value *value)
+{
+ unsigned long hash = 0;
+ parson_bool_t found = PARSON_FALSE;
+ size_t cell_ix = 0;
+ JSON_Status res = JSONFailure;
+
+ if (!object || !name || !value) {
+ return JSONFailure;
+ }
+
+ hash = hash_string(name, strlen(name));
+ found = PARSON_FALSE;
+ cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
+ if (found) {
+ return JSONFailure;
+ }
+
+ if (object->count >= object->item_capacity) {
+ res = json_object_grow_and_rehash(object);
+ if (res != JSONSuccess) {
+ return JSONFailure;
+ }
+ cell_ix =
+ json_object_get_cell_ix(object, name, strlen(name), hash, &found);
+ }
+
+ object->names[object->count] = name;
+ object->cells[cell_ix] = object->count;
+ object->values[object->count] = value;
+ object->cell_ixs[object->count] = cell_ix;
+ object->hashes[object->count] = hash;
+ object->count++;
+ value->parent = json_object_get_wrapping_value(object);
+
+ return JSONSuccess;
+}
+
+static JSON_Value *json_object_getn_value(const JSON_Object *object,
+ const char *name, size_t name_len)
+{
+ unsigned long hash = 0;
+ parson_bool_t found = PARSON_FALSE;
+ size_t cell_ix = 0;
+ size_t item_ix = 0;
+ if (!object || !name) {
+ return NULL;
+ }
+ hash = hash_string(name, name_len);
+ found = PARSON_FALSE;
+ cell_ix = json_object_get_cell_ix(object, name, name_len, hash, &found);
+ if (!found) {
+ return NULL;
+ }
+ item_ix = object->cells[cell_ix];
+ return object->values[item_ix];
+}
+
+static JSON_Status json_object_remove_internal(JSON_Object *object,
+ const char *name,
+ parson_bool_t free_value)
+{
+ unsigned long hash = 0;
+ parson_bool_t found = PARSON_FALSE;
+ size_t cell = 0;
+ size_t item_ix = 0;
+ size_t last_item_ix = 0;
+ size_t i = 0;
+ size_t j = 0;
+ size_t x = 0;
+ size_t k = 0;
+ JSON_Value *val = NULL;
+
+ if (object == NULL) {
+ return JSONFailure;
+ }
+
+ hash = hash_string(name, strlen(name));
+ found = PARSON_FALSE;
+ cell = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
+ if (!found) {
+ return JSONFailure;
+ }
+
+ item_ix = object->cells[cell];
+ if (free_value) {
+ val = object->values[item_ix];
+ json_value_free(val);
+ val = NULL;
+ }
+
+ parson_free(object->names[item_ix]);
+ last_item_ix = object->count - 1;
+ if (item_ix < last_item_ix) {
+ object->names[item_ix] = object->names[last_item_ix];
+ object->values[item_ix] = object->values[last_item_ix];
+ object->cell_ixs[item_ix] = object->cell_ixs[last_item_ix];
+ object->hashes[item_ix] = object->hashes[last_item_ix];
+ object->cells[object->cell_ixs[item_ix]] = item_ix;
+ }
+ object->count--;
+
+ i = cell;
+ j = i;
+ for (x = 0; x < (object->cell_capacity - 1); x++) {
+ j = (j + 1) & (object->cell_capacity - 1);
+ if (object->cells[j] == OBJECT_INVALID_IX) {
+ break;
+ }
+ k = object->hashes[object->cells[j]] & (object->cell_capacity - 1);
+ if ((j > i && (k <= i || k > j)) || (j < i && (k <= i && k > j))) {
+ object->cell_ixs[object->cells[j]] = i;
+ object->cells[i] = object->cells[j];
+ i = j;
+ }
+ }
+ object->cells[i] = OBJECT_INVALID_IX;
+ return JSONSuccess;
+}
+
+static JSON_Status json_object_dotremove_internal(JSON_Object *object,
+ const char *name,
+ parson_bool_t free_value)
+{
+ JSON_Value *temp_value = NULL;
+ JSON_Object *temp_object = NULL;
+ const char *dot_pos = strchr(name, '.');
+ if (!dot_pos) {
+ return json_object_remove_internal(object, name, free_value);
+ }
+ temp_value = json_object_getn_value(object, name, dot_pos - name);
+ if (json_value_get_type(temp_value) != JSONObject) {
+ return JSONFailure;
+ }
+ temp_object = json_value_get_object(temp_value);
+ return json_object_dotremove_internal(temp_object, dot_pos + 1, free_value);
+}
+
+static void json_object_free(JSON_Object *object)
+{
+ json_object_deinit(object, PARSON_TRUE, PARSON_TRUE);
+ parson_free(object);
+}
+
+/* JSON Array */
+static JSON_Array *json_array_make(JSON_Value *wrapping_value)
+{
+ JSON_Array *new_array = (JSON_Array *)parson_malloc(sizeof(JSON_Array));
+ if (new_array == NULL) {
+ return NULL;
+ }
+ new_array->wrapping_value = wrapping_value;
+ new_array->items = (JSON_Value **)NULL;
+ new_array->capacity = 0;
+ new_array->count = 0;
+ return new_array;
+}
+
+static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value)
+{
+ if (array->count >= array->capacity) {
+ size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY);
+ if (json_array_resize(array, new_capacity) != JSONSuccess) {
+ return JSONFailure;
+ }
+ }
+ value->parent = json_array_get_wrapping_value(array);
+ array->items[array->count] = value;
+ array->count++;
+ return JSONSuccess;
+}
+
+static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity)
+{
+ JSON_Value **new_items = NULL;
+ if (new_capacity == 0) {
+ return JSONFailure;
+ }
+ new_items =
+ (JSON_Value **)parson_malloc(new_capacity * sizeof(JSON_Value *));
+ if (new_items == NULL) {
+ return JSONFailure;
+ }
+ if (array->items != NULL && array->count > 0) {
+ memcpy(new_items, array->items, array->count * sizeof(JSON_Value *));
+ }
+ parson_free(array->items);
+ array->items = new_items;
+ array->capacity = new_capacity;
+ return JSONSuccess;
+}
+
+static void json_array_free(JSON_Array *array)
+{
+ size_t i;
+ for (i = 0; i < array->count; i++) {
+ json_value_free(array->items[i]);
+ }
+ parson_free(array->items);
+ parson_free(array);
+}
+
+/* JSON Value */
+static JSON_Value *json_value_init_string_no_copy(char *string, size_t length)
+{
+ JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSONString;
+ new_value->value.string.chars = string;
+ new_value->value.string.length = length;
+ return new_value;
+}
+
+/* Parser */
+static JSON_Status skip_quotes(const char **string)
+{
+ if (**string != '\"') {
+ return JSONFailure;
+ }
+ SKIP_CHAR(string);
+ while (**string != '\"') {
+ if (**string == '\0') {
+ return JSONFailure;
+ }
+ else if (**string == '\\') {
+ SKIP_CHAR(string);
+ if (**string == '\0') {
+ return JSONFailure;
+ }
+ }
+ SKIP_CHAR(string);
+ }
+ SKIP_CHAR(string);
+ return JSONSuccess;
+}
+
+static JSON_Status parse_utf16(const char **unprocessed, char **processed)
+{
+ unsigned int cp, lead, trail;
+ char *processed_ptr = *processed;
+ const char *unprocessed_ptr = *unprocessed;
+ JSON_Status status = JSONFailure;
+ unprocessed_ptr++; /* skips u */
+ status = parse_utf16_hex(unprocessed_ptr, &cp);
+ if (status != JSONSuccess) {
+ return JSONFailure;
+ }
+ if (cp < 0x80) {
+ processed_ptr[0] = (char)cp; /* 0xxxxxxx */
+ }
+ else if (cp < 0x800) {
+ processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */
+ processed_ptr[1] = ((cp)&0x3F) | 0x80; /* 10xxxxxx */
+ processed_ptr += 1;
+ }
+ else if (cp < 0xD800 || cp > 0xDFFF) {
+ processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */
+ processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */
+ processed_ptr[2] = ((cp)&0x3F) | 0x80; /* 10xxxxxx */
+ processed_ptr += 2;
+ }
+ else if (cp >= 0xD800 &&
+ cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */
+ lead = cp;
+ unprocessed_ptr += 4; /* should always be within the buffer, otherwise
+ previous sscanf would fail */
+ if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u') {
+ return JSONFailure;
+ }
+ status = parse_utf16_hex(unprocessed_ptr, &trail);
+ if (status != JSONSuccess || trail < 0xDC00 ||
+ trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */
+ return JSONFailure;
+ }
+ cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) +
+ 0x010000;
+ processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */
+ processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */
+ processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */
+ processed_ptr[3] = (((cp)&0x3F) | 0x80); /* 10xxxxxx */
+ processed_ptr += 3;
+ }
+ else { /* trail surrogate before lead surrogate */
+ return JSONFailure;
+ }
+ unprocessed_ptr += 3;
+ *processed = processed_ptr;
+ *unprocessed = unprocessed_ptr;
+ return JSONSuccess;
+}
+
+/* Copies and processes passed string up to supplied length.
+Example: "\u006Corem ipsum" -> lorem ipsum */
+static char *process_string(const char *input, size_t input_len,
+ size_t *output_len)
+{
+ const char *input_ptr = input;
+ size_t initial_size = (input_len + 1) * sizeof(char);
+ size_t final_size = 0;
+ char *output = NULL, *output_ptr = NULL, *resized_output = NULL;
+ output = (char *)parson_malloc(initial_size);
+ if (output == NULL) {
+ goto error;
+ }
+ output_ptr = output;
+ while ((*input_ptr != '\0') && (size_t)(input_ptr - input) < input_len) {
+ if (*input_ptr == '\\') {
+ input_ptr++;
+ switch (*input_ptr) {
+ case '\"':
+ *output_ptr = '\"';
+ break;
+ case '\\':
+ *output_ptr = '\\';
+ break;
+ case '/':
+ *output_ptr = '/';
+ break;
+ case 'b':
+ *output_ptr = '\b';
+ break;
+ case 'f':
+ *output_ptr = '\f';
+ break;
+ case 'n':
+ *output_ptr = '\n';
+ break;
+ case 'r':
+ *output_ptr = '\r';
+ break;
+ case 't':
+ *output_ptr = '\t';
+ break;
+ case 'u':
+ if (parse_utf16(&input_ptr, &output_ptr) != JSONSuccess) {
+ goto error;
+ }
+ break;
+ default:
+ goto error;
+ }
+ }
+ else if ((unsigned char)*input_ptr < 0x20) {
+ goto error; /* 0x00-0x19 are invalid characters for json string
+ (http://www.ietf.org/rfc/rfc4627.txt) */
+ }
+ else {
+ *output_ptr = *input_ptr;
+ }
+ output_ptr++;
+ input_ptr++;
+ }
+ *output_ptr = '\0';
+ /* resize to new length */
+ final_size = (size_t)(output_ptr - output) + 1;
+ /* todo: don't resize if final_size == initial_size */
+ resized_output = (char *)parson_malloc(final_size);
+ if (resized_output == NULL) {
+ goto error;
+ }
+ memcpy(resized_output, output, final_size);
+ *output_len = final_size - 1;
+ parson_free(output);
+ return resized_output;
+error:
+ parson_free(output);
+ return NULL;
+}
+
+/* Return processed contents of a string between quotes and
+ skips passed argument to a matching quote. */
+static char *get_quoted_string(const char **string, size_t *output_string_len)
+{
+ const char *string_start = *string;
+ size_t input_string_len = 0;
+ JSON_Status status = skip_quotes(string);
+ if (status != JSONSuccess) {
+ return NULL;
+ }
+ input_string_len = *string - string_start - 2; /* length without quotes */
+ return process_string(string_start + 1, input_string_len,
+ output_string_len);
+}
+
+static JSON_Value *parse_value(const char **string, size_t nesting)
+{
+ if (nesting > MAX_NESTING) {
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ switch (**string) {
+ case '{':
+ return parse_object_value(string, nesting + 1);
+ case '[':
+ return parse_array_value(string, nesting + 1);
+ case '\"':
+ return parse_string_value(string);
+ case 'f':
+ case 't':
+ return parse_boolean_value(string);
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return parse_number_value(string);
+ case 'n':
+ return parse_null_value(string);
+ default:
+ return NULL;
+ }
+}
+
+static JSON_Value *parse_object_value(const char **string, size_t nesting)
+{
+ JSON_Status status = JSONFailure;
+ JSON_Value *output_value = NULL, *new_value = NULL;
+ JSON_Object *output_object = NULL;
+ char *new_key = NULL;
+
+ output_value = json_value_init_object();
+ if (output_value == NULL) {
+ return NULL;
+ }
+ if (**string != '{') {
+ json_value_free(output_value);
+ return NULL;
+ }
+ output_object = json_value_get_object(output_value);
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ if (**string == '}') { /* empty object */
+ SKIP_CHAR(string);
+ return output_value;
+ }
+ while (**string != '\0') {
+ size_t key_len = 0;
+ new_key = get_quoted_string(string, &key_len);
+ /* We do not support key names with embedded \0 chars */
+ if (!new_key) {
+ json_value_free(output_value);
+ return NULL;
+ }
+ if (key_len != strlen(new_key)) {
+ parson_free(new_key);
+ json_value_free(output_value);
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ':') {
+ parson_free(new_key);
+ json_value_free(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ new_value = parse_value(string, nesting);
+ if (new_value == NULL) {
+ parson_free(new_key);
+ json_value_free(output_value);
+ return NULL;
+ }
+ status = json_object_add(output_object, new_key, new_value);
+ if (status != JSONSuccess) {
+ parson_free(new_key);
+ json_value_free(new_value);
+ json_value_free(output_value);
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ',') {
+ break;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ if (**string == '}') {
+ break;
+ }
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != '}') {
+ json_value_free(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ return output_value;
+}
+
+static JSON_Value *parse_array_value(const char **string, size_t nesting)
+{
+ JSON_Value *output_value = NULL, *new_array_value = NULL;
+ JSON_Array *output_array = NULL;
+ output_value = json_value_init_array();
+ if (output_value == NULL) {
+ return NULL;
+ }
+ if (**string != '[') {
+ json_value_free(output_value);
+ return NULL;
+ }
+ output_array = json_value_get_array(output_value);
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ if (**string == ']') { /* empty array */
+ SKIP_CHAR(string);
+ return output_value;
+ }
+ while (**string != '\0') {
+ new_array_value = parse_value(string, nesting);
+ if (new_array_value == NULL) {
+ json_value_free(output_value);
+ return NULL;
+ }
+ if (json_array_add(output_array, new_array_value) != JSONSuccess) {
+ json_value_free(new_array_value);
+ json_value_free(output_value);
+ return NULL;
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ',') {
+ break;
+ }
+ SKIP_CHAR(string);
+ SKIP_WHITESPACES(string);
+ if (**string == ']') {
+ break;
+ }
+ }
+ SKIP_WHITESPACES(string);
+ if (**string != ']' || /* Trim array after parsing is over */
+ json_array_resize(output_array, json_array_get_count(output_array)) !=
+ JSONSuccess) {
+ json_value_free(output_value);
+ return NULL;
+ }
+ SKIP_CHAR(string);
+ return output_value;
+}
+
+static JSON_Value *parse_string_value(const char **string)
+{
+ JSON_Value *value = NULL;
+ size_t new_string_len = 0;
+ char *new_string = get_quoted_string(string, &new_string_len);
+ if (new_string == NULL) {
+ return NULL;
+ }
+ value = json_value_init_string_no_copy(new_string, new_string_len);
+ if (value == NULL) {
+ parson_free(new_string);
+ return NULL;
+ }
+ return value;
+}
+
+static JSON_Value *parse_boolean_value(const char **string)
+{
+ size_t true_token_size = SIZEOF_TOKEN("true");
+ size_t false_token_size = SIZEOF_TOKEN("false");
+ if (strncmp("true", *string, true_token_size) == 0) {
+ *string += true_token_size;
+ return json_value_init_boolean(1);
+ }
+ else if (strncmp("false", *string, false_token_size) == 0) {
+ *string += false_token_size;
+ return json_value_init_boolean(0);
+ }
+ return NULL;
+}
+
+static JSON_Value *parse_number_value(const char **string)
+{
+ char *end;
+ double number = 0;
+ errno = 0;
+ number = strtod(*string, &end);
+ if (errno == ERANGE && (number <= -HUGE_VAL || number >= HUGE_VAL)) {
+ return NULL;
+ }
+ if ((errno && errno != ERANGE) || !is_decimal(*string, end - *string)) {
+ return NULL;
+ }
+ *string = end;
+ return json_value_init_number(number);
+}
+
+static JSON_Value *parse_null_value(const char **string)
+{
+ size_t token_size = SIZEOF_TOKEN("null");
+ if (strncmp("null", *string, token_size) == 0) {
+ *string += token_size;
+ return json_value_init_null();
+ }
+ return NULL;
+}
+
+/* Serialization */
+
+/* APPEND_STRING() is only called on string literals.
+ It's a bit hacky because it makes plenty of assumptions about the external
+ state and should eventually be tidied up into a function (same goes for
+ APPEND_INDENT)
+ */
+#define APPEND_STRING(str) \
+ do { \
+ written = SIZEOF_TOKEN((str)); \
+ if (buf != NULL) { \
+ memcpy(buf, (str), written); \
+ buf[written] = '\0'; \
+ buf += written; \
+ } \
+ written_total += written; \
+ } while (0)
+
+#define APPEND_INDENT(level) \
+ do { \
+ int level_i = 0; \
+ for (level_i = 0; level_i < (level); level_i++) { \
+ APPEND_STRING(PARSON_INDENT_STR); \
+ } \
+ } while (0)
+
+static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf,
+ int level, parson_bool_t is_pretty,
+ char *num_buf)
+{
+ const char *key = NULL, *string = NULL;
+ JSON_Value *temp_value = NULL;
+ JSON_Array *array = NULL;
+ JSON_Object *object = NULL;
+ size_t i = 0, count = 0;
+ double num = 0.0;
+ int written = -1, written_total = 0;
+ size_t len = 0;
+
+ switch (json_value_get_type(value)) {
+ case JSONArray:
+ array = json_value_get_array(value);
+ count = json_array_get_count(array);
+ APPEND_STRING("[");
+ if (count > 0 && is_pretty) {
+ APPEND_STRING("\n");
+ }
+ for (i = 0; i < count; i++) {
+ if (is_pretty) {
+ APPEND_INDENT(level + 1);
+ }
+ temp_value = json_array_get_value(array, i);
+ written = json_serialize_to_buffer_r(temp_value, buf, level + 1,
+ is_pretty, num_buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ if (i < (count - 1)) {
+ APPEND_STRING(",");
+ }
+ if (is_pretty) {
+ APPEND_STRING("\n");
+ }
+ }
+ if (count > 0 && is_pretty) {
+ APPEND_INDENT(level);
+ }
+ APPEND_STRING("]");
+ return written_total;
+ case JSONObject:
+ object = json_value_get_object(value);
+ count = json_object_get_count(object);
+ APPEND_STRING("{");
+ if (count > 0 && is_pretty) {
+ APPEND_STRING("\n");
+ }
+ for (i = 0; i < count; i++) {
+ key = json_object_get_name(object, i);
+ if (key == NULL) {
+ return -1;
+ }
+ if (is_pretty) {
+ APPEND_INDENT(level + 1);
+ }
+ /* We do not support key names with embedded \0 chars */
+ written = json_serialize_string(key, strlen(key), buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ APPEND_STRING(":");
+ if (is_pretty) {
+ APPEND_STRING(" ");
+ }
+ temp_value = json_object_get_value_at(object, i);
+ written = json_serialize_to_buffer_r(temp_value, buf, level + 1,
+ is_pretty, num_buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ if (i < (count - 1)) {
+ APPEND_STRING(",");
+ }
+ if (is_pretty) {
+ APPEND_STRING("\n");
+ }
+ }
+ if (count > 0 && is_pretty) {
+ APPEND_INDENT(level);
+ }
+ APPEND_STRING("}");
+ return written_total;
+ case JSONString:
+ string = json_value_get_string(value);
+ if (string == NULL) {
+ return -1;
+ }
+ len = json_value_get_string_len(value);
+ written = json_serialize_string(string, len, buf);
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ return written_total;
+ case JSONBoolean:
+ if (json_value_get_boolean(value)) {
+ APPEND_STRING("true");
+ }
+ else {
+ APPEND_STRING("false");
+ }
+ return written_total;
+ case JSONNumber:
+ num = json_value_get_number(value);
+ if (buf != NULL) {
+ num_buf = buf;
+ }
+ if (parson_number_serialization_function) {
+ written = parson_number_serialization_function(num, num_buf);
+ }
+ else {
+ const char *float_format = parson_float_format
+ ? parson_float_format
+ : PARSON_DEFAULT_FLOAT_FORMAT;
+ written = parson_sprintf(num_buf, float_format, num);
+ }
+ if (written < 0) {
+ return -1;
+ }
+ if (buf != NULL) {
+ buf += written;
+ }
+ written_total += written;
+ return written_total;
+ case JSONNull:
+ APPEND_STRING("null");
+ return written_total;
+ case JSONError:
+ return -1;
+ default:
+ return -1;
+ }
+}
+
+static int json_serialize_string(const char *string, size_t len, char *buf)
+{
+ size_t i = 0;
+ char c = '\0';
+ int written = -1, written_total = 0;
+ APPEND_STRING("\"");
+ for (i = 0; i < len; i++) {
+ c = string[i];
+ switch (c) {
+ case '\"':
+ APPEND_STRING("\\\"");
+ break;
+ case '\\':
+ APPEND_STRING("\\\\");
+ break;
+ case '\b':
+ APPEND_STRING("\\b");
+ break;
+ case '\f':
+ APPEND_STRING("\\f");
+ break;
+ case '\n':
+ APPEND_STRING("\\n");
+ break;
+ case '\r':
+ APPEND_STRING("\\r");
+ break;
+ case '\t':
+ APPEND_STRING("\\t");
+ break;
+ case '\x00':
+ APPEND_STRING("\\u0000");
+ break;
+ case '\x01':
+ APPEND_STRING("\\u0001");
+ break;
+ case '\x02':
+ APPEND_STRING("\\u0002");
+ break;
+ case '\x03':
+ APPEND_STRING("\\u0003");
+ break;
+ case '\x04':
+ APPEND_STRING("\\u0004");
+ break;
+ case '\x05':
+ APPEND_STRING("\\u0005");
+ break;
+ case '\x06':
+ APPEND_STRING("\\u0006");
+ break;
+ case '\x07':
+ APPEND_STRING("\\u0007");
+ break;
+ /* '\x08' duplicate: '\b' */
+ /* '\x09' duplicate: '\t' */
+ /* '\x0a' duplicate: '\n' */
+ case '\x0b':
+ APPEND_STRING("\\u000b");
+ break;
+ /* '\x0c' duplicate: '\f' */
+ /* '\x0d' duplicate: '\r' */
+ case '\x0e':
+ APPEND_STRING("\\u000e");
+ break;
+ case '\x0f':
+ APPEND_STRING("\\u000f");
+ break;
+ case '\x10':
+ APPEND_STRING("\\u0010");
+ break;
+ case '\x11':
+ APPEND_STRING("\\u0011");
+ break;
+ case '\x12':
+ APPEND_STRING("\\u0012");
+ break;
+ case '\x13':
+ APPEND_STRING("\\u0013");
+ break;
+ case '\x14':
+ APPEND_STRING("\\u0014");
+ break;
+ case '\x15':
+ APPEND_STRING("\\u0015");
+ break;
+ case '\x16':
+ APPEND_STRING("\\u0016");
+ break;
+ case '\x17':
+ APPEND_STRING("\\u0017");
+ break;
+ case '\x18':
+ APPEND_STRING("\\u0018");
+ break;
+ case '\x19':
+ APPEND_STRING("\\u0019");
+ break;
+ case '\x1a':
+ APPEND_STRING("\\u001a");
+ break;
+ case '\x1b':
+ APPEND_STRING("\\u001b");
+ break;
+ case '\x1c':
+ APPEND_STRING("\\u001c");
+ break;
+ case '\x1d':
+ APPEND_STRING("\\u001d");
+ break;
+ case '\x1e':
+ APPEND_STRING("\\u001e");
+ break;
+ case '\x1f':
+ APPEND_STRING("\\u001f");
+ break;
+ case '/':
+ if (parson_escape_slashes) {
+ APPEND_STRING("\\/"); /* to make json embeddable in xml\/html */
+ }
+ else {
+ APPEND_STRING("/");
+ }
+ break;
+ default:
+ if (buf != NULL) {
+ buf[0] = c;
+ buf += 1;
+ }
+ written_total += 1;
+ break;
+ }
+ }
+ APPEND_STRING("\"");
+ return written_total;
+}
+
+#undef APPEND_STRING
+#undef APPEND_INDENT
+
+/* Parser API */
+JSON_Value *json_parse_file(const char *filename)
+{
+ char *file_contents = read_file(filename);
+ JSON_Value *output_value = NULL;
+ if (file_contents == NULL) {
+ return NULL;
+ }
+ output_value = json_parse_string(file_contents);
+ parson_free(file_contents);
+ return output_value;
+}
+
+JSON_Value *json_parse_file_with_comments(const char *filename)
+{
+ char *file_contents = read_file(filename);
+ JSON_Value *output_value = NULL;
+ if (file_contents == NULL) {
+ return NULL;
+ }
+ output_value = json_parse_string_with_comments(file_contents);
+ parson_free(file_contents);
+ return output_value;
+}
+
+JSON_Value *json_parse_string(const char *string)
+{
+ if (string == NULL) {
+ return NULL;
+ }
+ if (string[0] == '\xEF' && string[1] == '\xBB' && string[2] == '\xBF') {
+ string = string + 3; /* Support for UTF-8 BOM */
+ }
+ return parse_value((const char **)&string, 0);
+}
+
+JSON_Value *json_parse_string_with_comments(const char *string)
+{
+ JSON_Value *result = NULL;
+ char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL;
+ string_mutable_copy = parson_strdup(string);
+ if (string_mutable_copy == NULL) {
+ return NULL;
+ }
+ remove_comments(string_mutable_copy, "/*", "*/");
+ remove_comments(string_mutable_copy, "//", "\n");
+ string_mutable_copy_ptr = string_mutable_copy;
+ result = parse_value((const char **)&string_mutable_copy_ptr, 0);
+ parson_free(string_mutable_copy);
+ return result;
+}
+
+/* JSON Object API */
+
+JSON_Value *json_object_get_value(const JSON_Object *object, const char *name)
+{
+ if (object == NULL || name == NULL) {
+ return NULL;
+ }
+ return json_object_getn_value(object, name, strlen(name));
+}
+
+const char *json_object_get_string(const JSON_Object *object, const char *name)
+{
+ return json_value_get_string(json_object_get_value(object, name));
+}
+
+size_t json_object_get_string_len(const JSON_Object *object, const char *name)
+{
+ return json_value_get_string_len(json_object_get_value(object, name));
+}
+
+double json_object_get_number(const JSON_Object *object, const char *name)
+{
+ return json_value_get_number(json_object_get_value(object, name));
+}
+
+JSON_Object *json_object_get_object(const JSON_Object *object, const char *name)
+{
+ return json_value_get_object(json_object_get_value(object, name));
+}
+
+JSON_Array *json_object_get_array(const JSON_Object *object, const char *name)
+{
+ return json_value_get_array(json_object_get_value(object, name));
+}
+
+int json_object_get_boolean(const JSON_Object *object, const char *name)
+{
+ return json_value_get_boolean(json_object_get_value(object, name));
+}
+
+JSON_Value *json_object_dotget_value(const JSON_Object *object,
+ const char *name)
+{
+ const char *dot_position = strchr(name, '.');
+ if (!dot_position) {
+ return json_object_get_value(object, name);
+ }
+ object = json_value_get_object(
+ json_object_getn_value(object, name, dot_position - name));
+ return json_object_dotget_value(object, dot_position + 1);
+}
+
+const char *json_object_dotget_string(const JSON_Object *object,
+ const char *name)
+{
+ return json_value_get_string(json_object_dotget_value(object, name));
+}
+
+size_t json_object_dotget_string_len(const JSON_Object *object,
+ const char *name)
+{
+ return json_value_get_string_len(json_object_dotget_value(object, name));
+}
+
+double json_object_dotget_number(const JSON_Object *object, const char *name)
+{
+ return json_value_get_number(json_object_dotget_value(object, name));
+}
+
+JSON_Object *json_object_dotget_object(const JSON_Object *object,
+ const char *name)
+{
+ return json_value_get_object(json_object_dotget_value(object, name));
+}
+
+JSON_Array *json_object_dotget_array(const JSON_Object *object,
+ const char *name)
+{
+ return json_value_get_array(json_object_dotget_value(object, name));
+}
+
+int json_object_dotget_boolean(const JSON_Object *object, const char *name)
+{
+ return json_value_get_boolean(json_object_dotget_value(object, name));
+}
+
+size_t json_object_get_count(const JSON_Object *object)
+{
+ return object ? object->count : 0;
+}
+
+const char *json_object_get_name(const JSON_Object *object, size_t index)
+{
+ if (object == NULL || index >= json_object_get_count(object)) {
+ return NULL;
+ }
+ return object->names[index];
+}
+
+JSON_Value *json_object_get_value_at(const JSON_Object *object, size_t index)
+{
+ if (object == NULL || index >= json_object_get_count(object)) {
+ return NULL;
+ }
+ return object->values[index];
+}
+
+JSON_Value *json_object_get_wrapping_value(const JSON_Object *object)
+{
+ if (!object) {
+ return NULL;
+ }
+ return object->wrapping_value;
+}
+
+int json_object_has_value(const JSON_Object *object, const char *name)
+{
+ return json_object_get_value(object, name) != NULL;
+}
+
+int json_object_has_value_of_type(const JSON_Object *object, const char *name,
+ JSON_Value_Type type)
+{
+ JSON_Value *val = json_object_get_value(object, name);
+ return val != NULL && json_value_get_type(val) == type;
+}
+
+int json_object_dothas_value(const JSON_Object *object, const char *name)
+{
+ return json_object_dotget_value(object, name) != NULL;
+}
+
+int json_object_dothas_value_of_type(const JSON_Object *object,
+ const char *name, JSON_Value_Type type)
+{
+ JSON_Value *val = json_object_dotget_value(object, name);
+ return val != NULL && json_value_get_type(val) == type;
+}
+
+/* JSON Array API */
+JSON_Value *json_array_get_value(const JSON_Array *array, size_t index)
+{
+ if (array == NULL || index >= json_array_get_count(array)) {
+ return NULL;
+ }
+ return array->items[index];
+}
+
+const char *json_array_get_string(const JSON_Array *array, size_t index)
+{
+ return json_value_get_string(json_array_get_value(array, index));
+}
+
+size_t json_array_get_string_len(const JSON_Array *array, size_t index)
+{
+ return json_value_get_string_len(json_array_get_value(array, index));
+}
+
+double json_array_get_number(const JSON_Array *array, size_t index)
+{
+ return json_value_get_number(json_array_get_value(array, index));
+}
+
+JSON_Object *json_array_get_object(const JSON_Array *array, size_t index)
+{
+ return json_value_get_object(json_array_get_value(array, index));
+}
+
+JSON_Array *json_array_get_array(const JSON_Array *array, size_t index)
+{
+ return json_value_get_array(json_array_get_value(array, index));
+}
+
+int json_array_get_boolean(const JSON_Array *array, size_t index)
+{
+ return json_value_get_boolean(json_array_get_value(array, index));
+}
+
+size_t json_array_get_count(const JSON_Array *array)
+{
+ return array ? array->count : 0;
+}
+
+JSON_Value *json_array_get_wrapping_value(const JSON_Array *array)
+{
+ if (!array) {
+ return NULL;
+ }
+ return array->wrapping_value;
+}
+
+/* JSON Value API */
+JSON_Value_Type json_value_get_type(const JSON_Value *value)
+{
+ return value ? value->type : JSONError;
+}
+
+JSON_Object *json_value_get_object(const JSON_Value *value)
+{
+ return json_value_get_type(value) == JSONObject ? value->value.object
+ : NULL;
+}
+
+JSON_Array *json_value_get_array(const JSON_Value *value)
+{
+ return json_value_get_type(value) == JSONArray ? value->value.array : NULL;
+}
+
+static const JSON_String *json_value_get_string_desc(const JSON_Value *value)
+{
+ return json_value_get_type(value) == JSONString ? &value->value.string
+ : NULL;
+}
+
+const char *json_value_get_string(const JSON_Value *value)
+{
+ const JSON_String *str = json_value_get_string_desc(value);
+ return str ? str->chars : NULL;
+}
+
+size_t json_value_get_string_len(const JSON_Value *value)
+{
+ const JSON_String *str = json_value_get_string_desc(value);
+ return str ? str->length : 0;
+}
+
+double json_value_get_number(const JSON_Value *value)
+{
+ return json_value_get_type(value) == JSONNumber ? value->value.number : 0;
+}
+
+int json_value_get_boolean(const JSON_Value *value)
+{
+ return json_value_get_type(value) == JSONBoolean ? value->value.boolean
+ : -1;
+}
+
+JSON_Value *json_value_get_parent(const JSON_Value *value)
+{
+ return value ? value->parent : NULL;
+}
+
+void json_value_free(JSON_Value *value)
+{
+ switch (json_value_get_type(value)) {
+ case JSONObject:
+ json_object_free(value->value.object);
+ break;
+ case JSONString:
+ parson_free(value->value.string.chars);
+ break;
+ case JSONArray:
+ json_array_free(value->value.array);
+ break;
+ default:
+ break;
+ }
+ parson_free(value);
+}
+
+JSON_Value *json_value_init_object(void)
+{
+ JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSONObject;
+ new_value->value.object = json_object_make(new_value);
+ if (!new_value->value.object) {
+ parson_free(new_value);
+ return NULL;
+ }
+ return new_value;
+}
+
+JSON_Value *json_value_init_array(void)
+{
+ JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSONArray;
+ new_value->value.array = json_array_make(new_value);
+ if (!new_value->value.array) {
+ parson_free(new_value);
+ return NULL;
+ }
+ return new_value;
+}
+
+JSON_Value *json_value_init_string(const char *string)
+{
+ if (string == NULL) {
+ return NULL;
+ }
+ return json_value_init_string_with_len(string, strlen(string));
+}
+
+JSON_Value *json_value_init_string_with_len(const char *string, size_t length)
+{
+ char *copy = NULL;
+ JSON_Value *value;
+ if (string == NULL) {
+ return NULL;
+ }
+ if (!is_valid_utf8(string, length)) {
+ return NULL;
+ }
+ copy = parson_strndup(string, length);
+ if (copy == NULL) {
+ return NULL;
+ }
+ value = json_value_init_string_no_copy(copy, length);
+ if (value == NULL) {
+ parson_free(copy);
+ }
+ return value;
+}
+
+JSON_Value *json_value_init_number(double number)
+{
+ JSON_Value *new_value = NULL;
+ if (IS_NUMBER_INVALID(number)) {
+ return NULL;
+ }
+ new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));
+ if (new_value == NULL) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSONNumber;
+ new_value->value.number = number;
+ return new_value;
+}
+
+JSON_Value *json_value_init_boolean(int boolean)
+{
+ JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSONBoolean;
+ new_value->value.boolean = boolean ? 1 : 0;
+ return new_value;
+}
+
+JSON_Value *json_value_init_null(void)
+{
+ JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));
+ if (!new_value) {
+ return NULL;
+ }
+ new_value->parent = NULL;
+ new_value->type = JSONNull;
+ return new_value;
+}
+
+JSON_Value *json_value_deep_copy(const JSON_Value *value)
+{
+ size_t i = 0;
+ JSON_Value *return_value = NULL, *temp_value_copy = NULL,
+ *temp_value = NULL;
+ const JSON_String *temp_string = NULL;
+ const char *temp_key = NULL;
+ char *temp_string_copy = NULL;
+ JSON_Array *temp_array = NULL, *temp_array_copy = NULL;
+ JSON_Object *temp_object = NULL, *temp_object_copy = NULL;
+ JSON_Status res = JSONFailure;
+ char *key_copy = NULL;
+
+ switch (json_value_get_type(value)) {
+ case JSONArray:
+ temp_array = json_value_get_array(value);
+ return_value = json_value_init_array();
+ if (return_value == NULL) {
+ return NULL;
+ }
+ temp_array_copy = json_value_get_array(return_value);
+ for (i = 0; i < json_array_get_count(temp_array); i++) {
+ temp_value = json_array_get_value(temp_array, i);
+ temp_value_copy = json_value_deep_copy(temp_value);
+ if (temp_value_copy == NULL) {
+ json_value_free(return_value);
+ return NULL;
+ }
+ if (json_array_add(temp_array_copy, temp_value_copy) !=
+ JSONSuccess) {
+ json_value_free(return_value);
+ json_value_free(temp_value_copy);
+ return NULL;
+ }
+ }
+ return return_value;
+ case JSONObject:
+ temp_object = json_value_get_object(value);
+ return_value = json_value_init_object();
+ if (!return_value) {
+ return NULL;
+ }
+ temp_object_copy = json_value_get_object(return_value);
+ for (i = 0; i < json_object_get_count(temp_object); i++) {
+ temp_key = json_object_get_name(temp_object, i);
+ temp_value = json_object_get_value(temp_object, temp_key);
+ temp_value_copy = json_value_deep_copy(temp_value);
+ if (!temp_value_copy) {
+ json_value_free(return_value);
+ return NULL;
+ }
+ key_copy = parson_strdup(temp_key);
+ if (!key_copy) {
+ json_value_free(temp_value_copy);
+ json_value_free(return_value);
+ return NULL;
+ }
+ res = json_object_add(temp_object_copy, key_copy, temp_value_copy);
+ if (res != JSONSuccess) {
+ parson_free(key_copy);
+ json_value_free(temp_value_copy);
+ json_value_free(return_value);
+ return NULL;
+ }
+ }
+ return return_value;
+ case JSONBoolean:
+ return json_value_init_boolean(json_value_get_boolean(value));
+ case JSONNumber:
+ return json_value_init_number(json_value_get_number(value));
+ case JSONString:
+ temp_string = json_value_get_string_desc(value);
+ if (temp_string == NULL) {
+ return NULL;
+ }
+ temp_string_copy =
+ parson_strndup(temp_string->chars, temp_string->length);
+ if (temp_string_copy == NULL) {
+ return NULL;
+ }
+ return_value = json_value_init_string_no_copy(temp_string_copy,
+ temp_string->length);
+ if (return_value == NULL) {
+ parson_free(temp_string_copy);
+ }
+ return return_value;
+ case JSONNull:
+ return json_value_init_null();
+ case JSONError:
+ return NULL;
+ default:
+ return NULL;
+ }
+}
+
+size_t json_serialization_size(const JSON_Value *value)
+{
+ char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack
+ is a bad idea, so let's do it only
+ once */
+ int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_FALSE, num_buf);
+ return res < 0 ? 0 : (size_t)(res) + 1;
+}
+
+JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf,
+ size_t buf_size_in_bytes)
+{
+ int written = -1;
+ size_t needed_size_in_bytes = json_serialization_size(value);
+ if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
+ return JSONFailure;
+ }
+ written = json_serialize_to_buffer_r(value, buf, 0, PARSON_FALSE, NULL);
+ if (written < 0) {
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_serialize_to_file(const JSON_Value *value,
+ const char *filename)
+{
+ JSON_Status return_code = JSONSuccess;
+ FILE *fp = NULL;
+ char *serialized_string = json_serialize_to_string(value);
+ if (serialized_string == NULL) {
+ return JSONFailure;
+ }
+ fp = fopen(filename, "w");
+ if (fp == NULL) {
+ json_free_serialized_string(serialized_string);
+ return JSONFailure;
+ }
+ if (fputs(serialized_string, fp) == EOF) {
+ return_code = JSONFailure;
+ }
+ if (fclose(fp) == EOF) {
+ return_code = JSONFailure;
+ }
+ json_free_serialized_string(serialized_string);
+ return return_code;
+}
+
+char *json_serialize_to_string(const JSON_Value *value)
+{
+ JSON_Status serialization_result = JSONFailure;
+ size_t buf_size_bytes = json_serialization_size(value);
+ char *buf = NULL;
+ if (buf_size_bytes == 0) {
+ return NULL;
+ }
+ buf = (char *)parson_malloc(buf_size_bytes);
+ if (buf == NULL) {
+ return NULL;
+ }
+ serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes);
+ if (serialization_result != JSONSuccess) {
+ json_free_serialized_string(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+size_t json_serialization_size_pretty(const JSON_Value *value)
+{
+ char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack
+ is a bad idea, so let's do it only
+ once */
+ int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_TRUE, num_buf);
+ return res < 0 ? 0 : (size_t)(res) + 1;
+}
+
+JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf,
+ size_t buf_size_in_bytes)
+{
+ int written = -1;
+ size_t needed_size_in_bytes = json_serialization_size_pretty(value);
+ if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
+ return JSONFailure;
+ }
+ written = json_serialize_to_buffer_r(value, buf, 0, PARSON_TRUE, NULL);
+ if (written < 0) {
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_serialize_to_file_pretty(const JSON_Value *value,
+ const char *filename)
+{
+ JSON_Status return_code = JSONSuccess;
+ FILE *fp = NULL;
+ char *serialized_string = json_serialize_to_string_pretty(value);
+ if (serialized_string == NULL) {
+ return JSONFailure;
+ }
+ fp = fopen(filename, "w");
+ if (fp == NULL) {
+ json_free_serialized_string(serialized_string);
+ return JSONFailure;
+ }
+ if (fputs(serialized_string, fp) == EOF) {
+ return_code = JSONFailure;
+ }
+ if (fclose(fp) == EOF) {
+ return_code = JSONFailure;
+ }
+ json_free_serialized_string(serialized_string);
+ return return_code;
+}
+
+char *json_serialize_to_string_pretty(const JSON_Value *value)
+{
+ JSON_Status serialization_result = JSONFailure;
+ size_t buf_size_bytes = json_serialization_size_pretty(value);
+ char *buf = NULL;
+ if (buf_size_bytes == 0) {
+ return NULL;
+ }
+ buf = (char *)parson_malloc(buf_size_bytes);
+ if (buf == NULL) {
+ return NULL;
+ }
+ serialization_result =
+ json_serialize_to_buffer_pretty(value, buf, buf_size_bytes);
+ if (serialization_result != JSONSuccess) {
+ json_free_serialized_string(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+void json_free_serialized_string(char *string)
+{
+ parson_free(string);
+}
+
+JSON_Status json_array_remove(JSON_Array *array, size_t ix)
+{
+ size_t to_move_bytes = 0;
+ if (array == NULL || ix >= json_array_get_count(array)) {
+ return JSONFailure;
+ }
+ json_value_free(json_array_get_value(array, ix));
+ to_move_bytes =
+ (json_array_get_count(array) - 1 - ix) * sizeof(JSON_Value *);
+ memmove(array->items + ix, array->items + ix + 1, to_move_bytes);
+ array->count -= 1;
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_value(JSON_Array *array, size_t ix,
+ JSON_Value *value)
+{
+ if (array == NULL || value == NULL || value->parent != NULL ||
+ ix >= json_array_get_count(array)) {
+ return JSONFailure;
+ }
+ json_value_free(json_array_get_value(array, ix));
+ value->parent = json_array_get_wrapping_value(array);
+ array->items[ix] = value;
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_string(JSON_Array *array, size_t i,
+ const char *string)
+{
+ JSON_Value *value = json_value_init_string(string);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_replace_value(array, i, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_string_with_len(JSON_Array *array, size_t i,
+ const char *string, size_t len)
+{
+ JSON_Value *value = json_value_init_string_with_len(string, len);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_replace_value(array, i, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_number(JSON_Array *array, size_t i,
+ double number)
+{
+ JSON_Value *value = json_value_init_number(number);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_replace_value(array, i, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean)
+{
+ JSON_Value *value = json_value_init_boolean(boolean);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_replace_value(array, i, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_replace_null(JSON_Array *array, size_t i)
+{
+ JSON_Value *value = json_value_init_null();
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_replace_value(array, i, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_clear(JSON_Array *array)
+{
+ size_t i = 0;
+ if (array == NULL) {
+ return JSONFailure;
+ }
+ for (i = 0; i < json_array_get_count(array); i++) {
+ json_value_free(json_array_get_value(array, i));
+ }
+ array->count = 0;
+ return JSONSuccess;
+}
+
+JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value)
+{
+ if (array == NULL || value == NULL || value->parent != NULL) {
+ return JSONFailure;
+ }
+ return json_array_add(array, value);
+}
+
+JSON_Status json_array_append_string(JSON_Array *array, const char *string)
+{
+ JSON_Value *value = json_value_init_string(string);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_append_value(array, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_append_string_with_len(JSON_Array *array,
+ const char *string, size_t len)
+{
+ JSON_Value *value = json_value_init_string_with_len(string, len);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_append_value(array, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_append_number(JSON_Array *array, double number)
+{
+ JSON_Value *value = json_value_init_number(number);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_append_value(array, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_append_boolean(JSON_Array *array, int boolean)
+{
+ JSON_Value *value = json_value_init_boolean(boolean);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_append_value(array, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_array_append_null(JSON_Array *array)
+{
+ JSON_Value *value = json_value_init_null();
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_array_append_value(array, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_set_value(JSON_Object *object, const char *name,
+ JSON_Value *value)
+{
+ unsigned long hash = 0;
+ parson_bool_t found = PARSON_FALSE;
+ size_t cell_ix = 0;
+ size_t item_ix = 0;
+ JSON_Value *old_value = NULL;
+ char *key_copy = NULL;
+
+ if (!object || !name || !value || value->parent) {
+ return JSONFailure;
+ }
+ hash = hash_string(name, strlen(name));
+ found = PARSON_FALSE;
+ cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
+ if (found) {
+ item_ix = object->cells[cell_ix];
+ old_value = object->values[item_ix];
+ json_value_free(old_value);
+ object->values[item_ix] = value;
+ value->parent = json_object_get_wrapping_value(object);
+ return JSONSuccess;
+ }
+ if (object->count >= object->item_capacity) {
+ JSON_Status res = json_object_grow_and_rehash(object);
+ if (res != JSONSuccess) {
+ return JSONFailure;
+ }
+ cell_ix =
+ json_object_get_cell_ix(object, name, strlen(name), hash, &found);
+ }
+ key_copy = parson_strdup(name);
+ if (!key_copy) {
+ return JSONFailure;
+ }
+ object->names[object->count] = key_copy;
+ object->cells[cell_ix] = object->count;
+ object->values[object->count] = value;
+ object->cell_ixs[object->count] = cell_ix;
+ object->hashes[object->count] = hash;
+ object->count++;
+ value->parent = json_object_get_wrapping_value(object);
+ return JSONSuccess;
+}
+
+JSON_Status json_object_set_string(JSON_Object *object, const char *name,
+ const char *string)
+{
+ JSON_Value *value = json_value_init_string(string);
+ JSON_Status status = json_object_set_value(object, name, value);
+ if (status != JSONSuccess) {
+ json_value_free(value);
+ }
+ return status;
+}
+
+JSON_Status json_object_set_string_with_len(JSON_Object *object,
+ const char *name,
+ const char *string, size_t len)
+{
+ JSON_Value *value = json_value_init_string_with_len(string, len);
+ JSON_Status status = json_object_set_value(object, name, value);
+ if (status != JSONSuccess) {
+ json_value_free(value);
+ }
+ return status;
+}
+
+JSON_Status json_object_set_number(JSON_Object *object, const char *name,
+ double number)
+{
+ JSON_Value *value = json_value_init_number(number);
+ JSON_Status status = json_object_set_value(object, name, value);
+ if (status != JSONSuccess) {
+ json_value_free(value);
+ }
+ return status;
+}
+
+JSON_Status json_object_set_boolean(JSON_Object *object, const char *name,
+ int boolean)
+{
+ JSON_Value *value = json_value_init_boolean(boolean);
+ JSON_Status status = json_object_set_value(object, name, value);
+ if (status != JSONSuccess) {
+ json_value_free(value);
+ }
+ return status;
+}
+
+JSON_Status json_object_set_null(JSON_Object *object, const char *name)
+{
+ JSON_Value *value = json_value_init_null();
+ JSON_Status status = json_object_set_value(object, name, value);
+ if (status != JSONSuccess) {
+ json_value_free(value);
+ }
+ return status;
+}
+
+JSON_Status json_object_dotset_value(JSON_Object *object, const char *name,
+ JSON_Value *value)
+{
+ const char *dot_pos = NULL;
+ JSON_Value *temp_value = NULL, *new_value = NULL;
+ JSON_Object *temp_object = NULL, *new_object = NULL;
+ JSON_Status status = JSONFailure;
+ size_t name_len = 0;
+ char *name_copy = NULL;
+
+ if (object == NULL || name == NULL || value == NULL) {
+ return JSONFailure;
+ }
+ dot_pos = strchr(name, '.');
+ if (dot_pos == NULL) {
+ return json_object_set_value(object, name, value);
+ }
+ name_len = dot_pos - name;
+ temp_value = json_object_getn_value(object, name, name_len);
+ if (temp_value) {
+ /* Don't overwrite existing non-object (unlike json_object_set_value,
+ * but it shouldn't be changed at this point) */
+ if (json_value_get_type(temp_value) != JSONObject) {
+ return JSONFailure;
+ }
+ temp_object = json_value_get_object(temp_value);
+ return json_object_dotset_value(temp_object, dot_pos + 1, value);
+ }
+ new_value = json_value_init_object();
+ if (new_value == NULL) {
+ return JSONFailure;
+ }
+ new_object = json_value_get_object(new_value);
+ status = json_object_dotset_value(new_object, dot_pos + 1, value);
+ if (status != JSONSuccess) {
+ json_value_free(new_value);
+ return JSONFailure;
+ }
+ name_copy = parson_strndup(name, name_len);
+ if (!name_copy) {
+ json_object_dotremove_internal(new_object, dot_pos + 1, 0);
+ json_value_free(new_value);
+ return JSONFailure;
+ }
+ status = json_object_add(object, name_copy, new_value);
+ if (status != JSONSuccess) {
+ parson_free(name_copy);
+ json_object_dotremove_internal(new_object, dot_pos + 1, 0);
+ json_value_free(new_value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_dotset_string(JSON_Object *object, const char *name,
+ const char *string)
+{
+ JSON_Value *value = json_value_init_string(string);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_object_dotset_value(object, name, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_dotset_string_with_len(JSON_Object *object,
+ const char *name,
+ const char *string, size_t len)
+{
+ JSON_Value *value = json_value_init_string_with_len(string, len);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_object_dotset_value(object, name, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_dotset_number(JSON_Object *object, const char *name,
+ double number)
+{
+ JSON_Value *value = json_value_init_number(number);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_object_dotset_value(object, name, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name,
+ int boolean)
+{
+ JSON_Value *value = json_value_init_boolean(boolean);
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_object_dotset_value(object, name, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_dotset_null(JSON_Object *object, const char *name)
+{
+ JSON_Value *value = json_value_init_null();
+ if (value == NULL) {
+ return JSONFailure;
+ }
+ if (json_object_dotset_value(object, name, value) != JSONSuccess) {
+ json_value_free(value);
+ return JSONFailure;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_object_remove(JSON_Object *object, const char *name)
+{
+ return json_object_remove_internal(object, name, PARSON_TRUE);
+}
+
+JSON_Status json_object_dotremove(JSON_Object *object, const char *name)
+{
+ return json_object_dotremove_internal(object, name, PARSON_TRUE);
+}
+
+JSON_Status json_object_clear(JSON_Object *object)
+{
+ size_t i = 0;
+ if (object == NULL) {
+ return JSONFailure;
+ }
+ for (i = 0; i < json_object_get_count(object); i++) {
+ parson_free(object->names[i]);
+ object->names[i] = NULL;
+
+ json_value_free(object->values[i]);
+ object->values[i] = NULL;
+ }
+ object->count = 0;
+ for (i = 0; i < object->cell_capacity; i++) {
+ object->cells[i] = OBJECT_INVALID_IX;
+ }
+ return JSONSuccess;
+}
+
+JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value)
+{
+ JSON_Value *temp_schema_value = NULL, *temp_value = NULL;
+ JSON_Array *schema_array = NULL, *value_array = NULL;
+ JSON_Object *schema_object = NULL, *value_object = NULL;
+ JSON_Value_Type schema_type = JSONError, value_type = JSONError;
+ const char *key = NULL;
+ size_t i = 0, count = 0;
+ if (schema == NULL || value == NULL) {
+ return JSONFailure;
+ }
+ schema_type = json_value_get_type(schema);
+ value_type = json_value_get_type(value);
+ if (schema_type != value_type &&
+ schema_type != JSONNull) { /* null represents all values */
+ return JSONFailure;
+ }
+ switch (schema_type) {
+ case JSONArray:
+ schema_array = json_value_get_array(schema);
+ value_array = json_value_get_array(value);
+ count = json_array_get_count(schema_array);
+ if (count == 0) {
+ return JSONSuccess; /* Empty array allows all types */
+ }
+ /* Get first value from array, rest is ignored */
+ temp_schema_value = json_array_get_value(schema_array, 0);
+ for (i = 0; i < json_array_get_count(value_array); i++) {
+ temp_value = json_array_get_value(value_array, i);
+ if (json_validate(temp_schema_value, temp_value) != JSONSuccess) {
+ return JSONFailure;
+ }
+ }
+ return JSONSuccess;
+ case JSONObject:
+ schema_object = json_value_get_object(schema);
+ value_object = json_value_get_object(value);
+ count = json_object_get_count(schema_object);
+ if (count == 0) {
+ return JSONSuccess; /* Empty object allows all objects */
+ }
+ else if (json_object_get_count(value_object) < count) {
+ return JSONFailure; /* Tested object mustn't have less name-value
+ pairs than schema */
+ }
+ for (i = 0; i < count; i++) {
+ key = json_object_get_name(schema_object, i);
+ temp_schema_value = json_object_get_value(schema_object, key);
+ temp_value = json_object_get_value(value_object, key);
+ if (temp_value == NULL) {
+ return JSONFailure;
+ }
+ if (json_validate(temp_schema_value, temp_value) != JSONSuccess) {
+ return JSONFailure;
+ }
+ }
+ return JSONSuccess;
+ case JSONString:
+ case JSONNumber:
+ case JSONBoolean:
+ case JSONNull:
+ return JSONSuccess; /* equality already tested before switch */
+ case JSONError:
+ default:
+ return JSONFailure;
+ }
+}
+
+int json_value_equals(const JSON_Value *a, const JSON_Value *b)
+{
+ JSON_Object *a_object = NULL, *b_object = NULL;
+ JSON_Array *a_array = NULL, *b_array = NULL;
+ const JSON_String *a_string = NULL, *b_string = NULL;
+ const char *key = NULL;
+ size_t a_count = 0, b_count = 0, i = 0;
+ JSON_Value_Type a_type, b_type;
+ a_type = json_value_get_type(a);
+ b_type = json_value_get_type(b);
+ if (a_type != b_type) {
+ return PARSON_FALSE;
+ }
+ switch (a_type) {
+ case JSONArray:
+ a_array = json_value_get_array(a);
+ b_array = json_value_get_array(b);
+ a_count = json_array_get_count(a_array);
+ b_count = json_array_get_count(b_array);
+ if (a_count != b_count) {
+ return PARSON_FALSE;
+ }
+ for (i = 0; i < a_count; i++) {
+ if (!json_value_equals(json_array_get_value(a_array, i),
+ json_array_get_value(b_array, i))) {
+ return PARSON_FALSE;
+ }
+ }
+ return PARSON_TRUE;
+ case JSONObject:
+ a_object = json_value_get_object(a);
+ b_object = json_value_get_object(b);
+ a_count = json_object_get_count(a_object);
+ b_count = json_object_get_count(b_object);
+ if (a_count != b_count) {
+ return PARSON_FALSE;
+ }
+ for (i = 0; i < a_count; i++) {
+ key = json_object_get_name(a_object, i);
+ if (!json_value_equals(json_object_get_value(a_object, key),
+ json_object_get_value(b_object, key))) {
+ return PARSON_FALSE;
+ }
+ }
+ return PARSON_TRUE;
+ case JSONString:
+ a_string = json_value_get_string_desc(a);
+ b_string = json_value_get_string_desc(b);
+ if (a_string == NULL || b_string == NULL) {
+ return PARSON_FALSE; /* shouldn't happen */
+ }
+ return a_string->length == b_string->length &&
+ memcmp(a_string->chars, b_string->chars, a_string->length) == 0;
+ case JSONBoolean:
+ return json_value_get_boolean(a) == json_value_get_boolean(b);
+ case JSONNumber:
+ return fabs(json_value_get_number(a) - json_value_get_number(b)) <
+ 0.000001; /* EPSILON */
+ case JSONError:
+ return PARSON_TRUE;
+ case JSONNull:
+ return PARSON_TRUE;
+ default:
+ return PARSON_TRUE;
+ }
+}
+
+JSON_Value_Type json_type(const JSON_Value *value)
+{
+ return json_value_get_type(value);
+}
+
+JSON_Object *json_object(const JSON_Value *value)
+{
+ return json_value_get_object(value);
+}
+
+JSON_Array *json_array(const JSON_Value *value)
+{
+ return json_value_get_array(value);
+}
+
+const char *json_string(const JSON_Value *value)
+{
+ return json_value_get_string(value);
+}
+
+size_t json_string_len(const JSON_Value *value)
+{
+ return json_value_get_string_len(value);
+}
+
+double json_number(const JSON_Value *value)
+{
+ return json_value_get_number(value);
+}
+
+int json_boolean(const JSON_Value *value)
+{
+ return json_value_get_boolean(value);
+}
+
+void json_set_allocation_functions(JSON_Malloc_Function malloc_fun,
+ JSON_Free_Function free_fun)
+{
+ parson_malloc = malloc_fun;
+ parson_free = free_fun;
+}
+
+void json_set_escape_slashes(int escape_slashes)
+{
+ parson_escape_slashes = escape_slashes;
+}
+
+void json_set_float_serialization_format(const char *format)
+{
+ if (parson_float_format) {
+ parson_free(parson_float_format);
+ parson_float_format = NULL;
+ }
+ if (!format) {
+ parson_float_format = NULL;
+ return;
+ }
+ parson_float_format = parson_strdup(format);
+}
+
+void json_set_number_serialization_function(
+ JSON_Number_Serialization_Function func)
+{
+ parson_number_serialization_function = func;
+}
diff --git a/lib/external/parson/parson.h b/lib/external/parson/parson.h
new file mode 100644
index 00000000000..5799e0346ef
--- /dev/null
+++ b/lib/external/parson/parson.h
@@ -0,0 +1,333 @@
+/*
+ SPDX-License-Identifier: MIT
+
+ Parson 1.5.3 (https://github.com/kgabis/parson)
+ Copyright (c) 2012 - 2023 Krzysztof Gabis
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef parson_parson_h
+#define parson_parson_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+} /* unconfuse xcode */
+#endif
+
+#define PARSON_VERSION_MAJOR 1
+#define PARSON_VERSION_MINOR 5
+#define PARSON_VERSION_PATCH 3
+
+#define PARSON_VERSION_STRING "1.5.3"
+
+#include /* size_t */
+
+/* Types and enums */
+typedef struct json_object_t JSON_Object;
+typedef struct json_array_t JSON_Array;
+typedef struct json_value_t JSON_Value;
+
+enum json_value_type {
+ JSONError = -1,
+ JSONNull = 1,
+ JSONString = 2,
+ JSONNumber = 3,
+ JSONObject = 4,
+ JSONArray = 5,
+ JSONBoolean = 6
+};
+typedef int JSON_Value_Type;
+
+enum json_result_t { JSONSuccess = 0, JSONFailure = -1 };
+typedef int JSON_Status;
+
+typedef void *(*JSON_Malloc_Function)(size_t);
+typedef void (*JSON_Free_Function)(void *);
+
+/* A function used for serializing numbers (see
+ json_set_number_serialization_function). If 'buf' is null then it should
+ return number of bytes that would've been written (but not more than
+ PARSON_NUM_BUF_SIZE).
+*/
+typedef int (*JSON_Number_Serialization_Function)(double num, char *buf);
+
+/* Call only once, before calling any other function from parson API. If not
+ called, malloc and free from stdlib will be used for all allocations */
+void json_set_allocation_functions(JSON_Malloc_Function malloc_fun,
+ JSON_Free_Function free_fun);
+
+/* Sets if slashes should be escaped or not when serializing JSON. By default
+ slashes are escaped. This function sets a global setting and is not thread
+ safe. */
+void json_set_escape_slashes(int escape_slashes);
+
+/* Sets float format used for serialization of numbers.
+ Make sure it can't serialize to a string longer than PARSON_NUM_BUF_SIZE.
+ If format is null then the default format is used. */
+void json_set_float_serialization_format(const char *format);
+
+/* Sets a function that will be used for serialization of numbers.
+ If function is null then the default serialization function is used. */
+void json_set_number_serialization_function(
+ JSON_Number_Serialization_Function fun);
+
+/* Parses first JSON value in a file, returns NULL in case of error */
+JSON_Value *json_parse_file(const char *filename);
+
+/* Parses first JSON value in a file and ignores comments (/ * * / and //),
+ returns NULL in case of error */
+JSON_Value *json_parse_file_with_comments(const char *filename);
+
+/* Parses first JSON value in a string, returns NULL in case of error */
+JSON_Value *json_parse_string(const char *string);
+
+/* Parses first JSON value in a string and ignores comments (/ * * / and //),
+ returns NULL in case of error */
+JSON_Value *json_parse_string_with_comments(const char *string);
+
+/* Serialization */
+size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */
+JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf,
+ size_t buf_size_in_bytes);
+JSON_Status json_serialize_to_file(const JSON_Value *value,
+ const char *filename);
+char *json_serialize_to_string(const JSON_Value *value);
+
+/* Pretty serialization */
+size_t
+json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */
+JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf,
+ size_t buf_size_in_bytes);
+JSON_Status json_serialize_to_file_pretty(const JSON_Value *value,
+ const char *filename);
+char *json_serialize_to_string_pretty(const JSON_Value *value);
+
+void json_free_serialized_string(
+ char *string); /* frees string from json_serialize_to_string and
+ json_serialize_to_string_pretty */
+
+/* Comparing */
+int json_value_equals(const JSON_Value *a, const JSON_Value *b);
+
+/* Validation
+ This is *NOT* JSON Schema. It validates json by checking if object have
+ identically named fields with matching types. For example schema {"name":"",
+ "age":0} will validate
+ {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
+ but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
+ In case of arrays, only first value in schema is checked against all values
+ in tested array. Empty objects ({}) validate all objects, empty arrays ([])
+ validate all arrays, null validates values of every type.
+ */
+JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);
+
+/*
+ * JSON Object
+ */
+JSON_Value *json_object_get_value(const JSON_Object *object, const char *name);
+const char *json_object_get_string(const JSON_Object *object, const char *name);
+size_t json_object_get_string_len(
+ const JSON_Object *object,
+ const char *name); /* doesn't account for last null character */
+JSON_Object *json_object_get_object(const JSON_Object *object,
+ const char *name);
+JSON_Array *json_object_get_array(const JSON_Object *object, const char *name);
+double json_object_get_number(const JSON_Object *object,
+ const char *name); /* returns 0 on fail */
+int json_object_get_boolean(const JSON_Object *object,
+ const char *name); /* returns -1 on fail */
+
+/* dotget functions enable addressing values with dot notation in nested
+ objects, just like in structs or c++/java/c# objects (e.g.
+ objectA.objectB.value). Because valid names in JSON can contain dots, some
+ values may be inaccessible this way. */
+JSON_Value *json_object_dotget_value(const JSON_Object *object,
+ const char *name);
+const char *json_object_dotget_string(const JSON_Object *object,
+ const char *name);
+size_t json_object_dotget_string_len(
+ const JSON_Object *object,
+ const char *name); /* doesn't account for last null character */
+JSON_Object *json_object_dotget_object(const JSON_Object *object,
+ const char *name);
+JSON_Array *json_object_dotget_array(const JSON_Object *object,
+ const char *name);
+double json_object_dotget_number(const JSON_Object *object,
+ const char *name); /* returns 0 on fail */
+int json_object_dotget_boolean(const JSON_Object *object,
+ const char *name); /* returns -1 on fail */
+
+/* Functions to get available names */
+size_t json_object_get_count(const JSON_Object *object);
+const char *json_object_get_name(const JSON_Object *object, size_t index);
+JSON_Value *json_object_get_value_at(const JSON_Object *object, size_t index);
+JSON_Value *json_object_get_wrapping_value(const JSON_Object *object);
+
+/* Functions to check if object has a value with a specific name. Returned value
+ * is 1 if object has a value and 0 if it doesn't. dothas functions behave
+ * exactly like dotget functions. */
+int json_object_has_value(const JSON_Object *object, const char *name);
+int json_object_has_value_of_type(const JSON_Object *object, const char *name,
+ JSON_Value_Type type);
+
+int json_object_dothas_value(const JSON_Object *object, const char *name);
+int json_object_dothas_value_of_type(const JSON_Object *object,
+ const char *name, JSON_Value_Type type);
+
+/* Creates new name-value pair or frees and replaces old value with a new one.
+ * json_object_set_value does not copy passed value so it shouldn't be freed
+ * afterwards. */
+JSON_Status json_object_set_value(JSON_Object *object, const char *name,
+ JSON_Value *value);
+JSON_Status json_object_set_string(JSON_Object *object, const char *name,
+ const char *string);
+JSON_Status json_object_set_string_with_len(
+ JSON_Object *object, const char *name, const char *string,
+ size_t len); /* length shouldn't include last null character */
+JSON_Status json_object_set_number(JSON_Object *object, const char *name,
+ double number);
+JSON_Status json_object_set_boolean(JSON_Object *object, const char *name,
+ int boolean);
+JSON_Status json_object_set_null(JSON_Object *object, const char *name);
+
+/* Works like dotget functions, but creates whole hierarchy if necessary.
+ * json_object_dotset_value does not copy passed value so it shouldn't be freed
+ * afterwards. */
+JSON_Status json_object_dotset_value(JSON_Object *object, const char *name,
+ JSON_Value *value);
+JSON_Status json_object_dotset_string(JSON_Object *object, const char *name,
+ const char *string);
+JSON_Status json_object_dotset_string_with_len(
+ JSON_Object *object, const char *name, const char *string,
+ size_t len); /* length shouldn't include last null character */
+JSON_Status json_object_dotset_number(JSON_Object *object, const char *name,
+ double number);
+JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name,
+ int boolean);
+JSON_Status json_object_dotset_null(JSON_Object *object, const char *name);
+
+/* Frees and removes name-value pair */
+JSON_Status json_object_remove(JSON_Object *object, const char *name);
+
+/* Works like dotget function, but removes name-value pair only on exact match.
+ */
+JSON_Status json_object_dotremove(JSON_Object *object, const char *key);
+
+/* Removes all name-value pairs in object */
+JSON_Status json_object_clear(JSON_Object *object);
+
+/*
+ *JSON Array
+ */
+JSON_Value *json_array_get_value(const JSON_Array *array, size_t index);
+const char *json_array_get_string(const JSON_Array *array, size_t index);
+size_t json_array_get_string_len(
+ const JSON_Array *array,
+ size_t index); /* doesn't account for last null character */
+JSON_Object *json_array_get_object(const JSON_Array *array, size_t index);
+JSON_Array *json_array_get_array(const JSON_Array *array, size_t index);
+double json_array_get_number(const JSON_Array *array,
+ size_t index); /* returns 0 on fail */
+int json_array_get_boolean(const JSON_Array *array,
+ size_t index); /* returns -1 on fail */
+size_t json_array_get_count(const JSON_Array *array);
+JSON_Value *json_array_get_wrapping_value(const JSON_Array *array);
+
+/* Frees and removes value at given index, does nothing and returns JSONFailure
+ * if index doesn't exist. Order of values in array may change during execution.
+ */
+JSON_Status json_array_remove(JSON_Array *array, size_t i);
+
+/* Frees and removes from array value at given index and replaces it with given
+ * one. Does nothing and returns JSONFailure if index doesn't exist.
+ * json_array_replace_value does not copy passed value so it shouldn't be freed
+ * afterwards. */
+JSON_Status json_array_replace_value(JSON_Array *array, size_t i,
+ JSON_Value *value);
+JSON_Status json_array_replace_string(JSON_Array *array, size_t i,
+ const char *string);
+JSON_Status json_array_replace_string_with_len(
+ JSON_Array *array, size_t i, const char *string,
+ size_t len); /* length shouldn't include last null character */
+JSON_Status json_array_replace_number(JSON_Array *array, size_t i,
+ double number);
+JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i,
+ int boolean);
+JSON_Status json_array_replace_null(JSON_Array *array, size_t i);
+
+/* Frees and removes all values from array */
+JSON_Status json_array_clear(JSON_Array *array);
+
+/* Appends new value at the end of array.
+ * json_array_append_value does not copy passed value so it shouldn't be freed
+ * afterwards. */
+JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);
+JSON_Status json_array_append_string(JSON_Array *array, const char *string);
+JSON_Status json_array_append_string_with_len(
+ JSON_Array *array, const char *string,
+ size_t len); /* length shouldn't include last null character */
+JSON_Status json_array_append_number(JSON_Array *array, double number);
+JSON_Status json_array_append_boolean(JSON_Array *array, int boolean);
+JSON_Status json_array_append_null(JSON_Array *array);
+
+/*
+ *JSON Value
+ */
+JSON_Value *json_value_init_object(void);
+JSON_Value *json_value_init_array(void);
+JSON_Value *
+json_value_init_string(const char *string); /* copies passed string */
+JSON_Value *json_value_init_string_with_len(
+ const char *string,
+ size_t length); /* copies passed string, length shouldn't include last null
+ character */
+JSON_Value *json_value_init_number(double number);
+JSON_Value *json_value_init_boolean(int boolean);
+JSON_Value *json_value_init_null(void);
+JSON_Value *json_value_deep_copy(const JSON_Value *value);
+void json_value_free(JSON_Value *value);
+
+JSON_Value_Type json_value_get_type(const JSON_Value *value);
+JSON_Object *json_value_get_object(const JSON_Value *value);
+JSON_Array *json_value_get_array(const JSON_Value *value);
+const char *json_value_get_string(const JSON_Value *value);
+size_t json_value_get_string_len(
+ const JSON_Value *value); /* doesn't account for last null character */
+double json_value_get_number(const JSON_Value *value);
+int json_value_get_boolean(const JSON_Value *value);
+JSON_Value *json_value_get_parent(const JSON_Value *value);
+
+/* Same as above, but shorter */
+JSON_Value_Type json_type(const JSON_Value *value);
+JSON_Object *json_object(const JSON_Value *value);
+JSON_Array *json_array(const JSON_Value *value);
+const char *json_string(const JSON_Value *value);
+size_t json_string_len(
+ const JSON_Value *value); /* doesn't account for last null character */
+double json_number(const JSON_Value *value);
+int json_boolean(const JSON_Value *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif