From 3ff537e147d7288db07ea784f5372ad8849d8129 Mon Sep 17 00:00:00 2001
From: czq <860517298@qq.com>
Date: Tue, 10 Jun 2025 13:20:07 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E5=AD=98=20=E5=BC=95=E5=85=A5Lwjson?=
=?UTF-8?q?=E5=BA=93=E8=A7=A3=E6=9E=90MQTT=E6=9C=8D=E5=8A=A1=E5=99=A8?=
=?UTF-8?q?=E4=B8=8B=E5=8F=91=E7=9A=84=E6=95=B0=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.cproject | 2 +-
IoT_SCV_CH584M1.wvproj | 2 +-
bsp/inc/bsp_ml307r.h | 8 +-
bsp/src/bsp_ml307r.c | 152 +-
common/cJSON/README.md | 590 -------
common/cJSON/cJSON.c | 3164 -----------------------------------
common/cJSON/cJSON.h | 306 ----
common/lwjson/lwjson.c | 752 +++++++++
common/lwjson/lwjson.h | 388 +++++
common/lwjson/lwjson_opts.h | 153 ++
10 files changed, 1439 insertions(+), 4078 deletions(-)
delete mode 100644 common/cJSON/README.md
delete mode 100644 common/cJSON/cJSON.c
delete mode 100644 common/cJSON/cJSON.h
create mode 100644 common/lwjson/lwjson.c
create mode 100644 common/lwjson/lwjson.h
create mode 100644 common/lwjson/lwjson_opts.h
diff --git a/.cproject b/.cproject
index b2f5195..493ca6d 100644
--- a/.cproject
+++ b/.cproject
@@ -131,7 +131,7 @@
-
+
diff --git a/IoT_SCV_CH584M1.wvproj b/IoT_SCV_CH584M1.wvproj
index d3c8686..1a35188 100644
--- a/IoT_SCV_CH584M1.wvproj
+++ b/IoT_SCV_CH584M1.wvproj
@@ -170,7 +170,7 @@
"${project}/RVMSIS",
"${project}/bsp/inc",
"${project}/APP/include",
- "${project}/common/cJSON"
+ "${project}/common/lwjson"
],
"include_system_paths": [],
"include_files": []
diff --git a/bsp/inc/bsp_ml307r.h b/bsp/inc/bsp_ml307r.h
index 5ba3c37..14952e0 100644
--- a/bsp/inc/bsp_ml307r.h
+++ b/bsp/inc/bsp_ml307r.h
@@ -16,10 +16,10 @@
#define ML307_UART_RX_PIN GPIO_Pin_12 //PB12
// MQTT服务器配置
-#define MQTT_HOST "120.25.151.173" // MQTT服务器地址
-// #define MQTT_HOST "8.135.10.183" // MQTT服务器地址
-#define MQTT_PORT 1883 // MQTT服务器端口
-// #define MQTT_PORT 27440 // MQTT服务器端口
+// #define MQTT_HOST "120.25.151.173" // MQTT服务器地址
+#define MQTT_HOST "8.135.10.183" // MQTT服务器地址
+// #define MQTT_PORT 1883 // MQTT服务器端口
+#define MQTT_PORT 27440 // MQTT服务器端口
#define MQTT_USER "guest" // MQTT用户名
#define MQTT_PASS "guest" // MQTT密码
#define CLIENT_ID "ZBF" // 客户端ID前缀
diff --git a/bsp/src/bsp_ml307r.c b/bsp/src/bsp_ml307r.c
index 78bd87d..dd74dd3 100644
--- a/bsp/src/bsp_ml307r.c
+++ b/bsp/src/bsp_ml307r.c
@@ -11,8 +11,8 @@
#include "bsp_tim.h"
#include "time.h"
#include
-#include "cJSON.h"
#include "bsp_adc.h"
+#include "lwjson.h"
#undef LOG_ENABLE
#define LOG_ENABLE 1
@@ -56,15 +56,6 @@ int Ml307r_Gpio_Init(void)
return 0;
}
-// 初始化 cJSON 使用 TMOS 的内存管理函数
-void Init_cJSON_With_TMOS(void)
-{
- cJSON_Hooks hooks;
- hooks.malloc_fn = (void *(*)(size_t))tmos_msg_allocate;
- hooks.free_fn = (void (*)(void *))tmos_msg_deallocate;
- cJSON_InitHooks(&hooks);
-}
-
void At_Debug(const char *fmt, ...)
{
va_list args;
@@ -170,6 +161,139 @@ void StopTask_CallBack(MultiTimer* timer, void* userData)
IotFlag_t.Date_Sending_flag = 0;
}
+
+/**
+ * @brief 解析服务器下发的JSON数据
+ * @param json_str JSON字符串
+ * @return 0: 成功, -1: 失败
+ */
+static int parse_server_json_data(const char* json_str)
+{
+ lwjson_t lwjson;
+ lwjson_token_t tokens[64]; // 增加token数量以处理复杂的JSON结构
+ const lwjson_token_t* token;
+
+ // 初始化lwjson
+ if (lwjson_init(&lwjson, tokens, LWJSON_ARRAYSIZE(tokens)) != lwjsonOK) {
+ logDebug("lwjson init failed\r\n");
+ return -1;
+ }
+
+ // 解析JSON字符串
+ if (lwjson_parse(&lwjson, json_str) != lwjsonOK) {
+ logDebug("JSON parse failed\r\n");
+ lwjson_free(&lwjson);
+ return -1;
+ }
+
+ logDebug("JSON parse success\r\n");
+
+ // 解析设备序列号
+ token = lwjson_find(&lwjson, "sn");
+ if (token != NULL && token->type == LWJSON_TYPE_STRING) {
+ char sn_str[32] = {0};
+ size_t len = token->u.str.token_value_len;
+ if (len < sizeof(sn_str)) {
+ strncpy(sn_str, token->u.str.token_value, len);
+ logDebug("Device SN: %s\r\n", sn_str);
+ }
+ }
+
+ // 解析时间戳
+ token = lwjson_find(&lwjson, "t");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ uint32_t timestamp = (uint32_t)token->u.num_int;
+ logDebug("Timestamp: %u\r\n", timestamp);
+ }
+
+ // 解析类型
+ token = lwjson_find(&lwjson, "type");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int msg_type = (int)token->u.num_int;
+ logDebug("Message type: %d\r\n", msg_type);
+ }
+
+ // 解析data对象中的各个字段
+
+ // 开关状态
+ token = lwjson_find(&lwjson, "data.switch_status");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int switch_status = (int)token->u.num_int;
+ logDebug("Switch status: %d\r\n", switch_status);
+
+ // 根据开关状态执行相应操作
+ if (switch_status == 1) {
+ logDebug("Command: Open valve\r\n");
+ // BSP_VALVE_Open(); // 取消注释以执行实际操作
+ IotFlag_t.Valve_Open_flag = 1;
+ } else if (switch_status == 0) {
+ logDebug("Command: Close valve\r\n");
+ // BSP_VALVE_Close(); // 取消注释以执行实际操作
+ IotFlag_t.Valve_Close_flag = 1;
+ }
+ }
+
+ // 温度
+ token = lwjson_find(&lwjson, "data.temp");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int temperature = (int)token->u.num_int;
+ logDebug("Temperature: %d°C\r\n", temperature);
+ }
+
+ // 进口压力
+ token = lwjson_find(&lwjson, "data.in_pressure");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int in_pressure = (int)token->u.num_int;
+ logDebug("In pressure: %d Pa\r\n", in_pressure);
+ }
+
+ // 出口压力
+ token = lwjson_find(&lwjson, "data.out_pressure");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int out_pressure = (int)token->u.num_int;
+ logDebug("Out pressure: %d Pa\r\n", out_pressure);
+ }
+
+ // 大气压力
+ token = lwjson_find(&lwjson, "data.atm_pressure");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int atm_pressure = (int)token->u.num_int;
+ logDebug("Atm pressure: %d Pa\r\n", atm_pressure);
+ }
+
+ // 电池电量
+ token = lwjson_find(&lwjson, "data.bat");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int battery = (int)token->u.num_int;
+ logDebug("Battery: %d%%\r\n", battery);
+ }
+
+ // 湿度
+ token = lwjson_find(&lwjson, "data.humi");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int humidity = (int)token->u.num_int;
+ logDebug("Humidity: %d%%\r\n", humidity);
+ }
+
+ // RSSI
+ token = lwjson_find(&lwjson, "data.rssi");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int rssi = (int)token->u.num_int;
+ logDebug("RSSI: %d\r\n", rssi);
+ }
+
+ // 信号强度
+ token = lwjson_find(&lwjson, "data.signal");
+ if (token != NULL && token->type == LWJSON_TYPE_NUM_INT) {
+ int signal = (int)token->u.num_int;
+ logDebug("Signal: %d\r\n", signal);
+ }
+
+ // 释放资源
+ lwjson_free(&lwjson);
+ return 0;
+}
+
//模块准备就绪
static int URC_Module_Ready_Cb(at_urc_info_t *info)
{
@@ -195,6 +319,11 @@ static int URC_MQTT_Publish_Cb(at_urc_info_t *info)
logDebug("payload: %s\r\n", json_start);
// 解析JSON负载
+ if (parse_server_json_data(json_start) == 0) {
+ logDebug("Server JSON data parsed successfully\r\n");
+ } else {
+ logDebug("Failed to parse server JSON data\r\n");
+ }
}
}
@@ -603,7 +732,7 @@ static void BSP_Get_Module_Info(void)
Module_Get_Imei();
Module_Get_Imsi();
Module_Get_Iccid();
- DelayMs(1000);
+ DelayMs(2000);
Module_Timer();
// Module_NetWork_Info(); // 获取网络连接信息
}
@@ -1367,7 +1496,6 @@ void BSP_Ml307r_Init(void)
ml307r_obj = at_obj_create(&at_adapter);//Create AT communication object
if(ml307r_obj != NULL)
{
- Init_cJSON_With_TMOS();
at_obj_set_urc(ml307r_obj, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
IotFlag_t.module_init_flag = 1;
IotFlag_t.Date_Sending_flag = 1;
diff --git a/common/cJSON/README.md b/common/cJSON/README.md
deleted file mode 100644
index 99147af..0000000
--- a/common/cJSON/README.md
+++ /dev/null
@@ -1,590 +0,0 @@
-# cJSON
-
-Ultralightweight JSON parser in ANSI C.
-
-## Table of contents
-* [License](#license)
-* [Usage](#usage)
- * [Welcome to cJSON](#welcome-to-cjson)
- * [Building](#building)
- * [Copying the source](#copying-the-source)
- * [CMake](#cmake)
- * [Makefile](#makefile)
- * [Meson](#meson)
- * [Vcpkg](#Vcpkg)
- * [Including cJSON](#including-cjson)
- * [Data Structure](#data-structure)
- * [Working with the data structure](#working-with-the-data-structure)
- * [Basic types](#basic-types)
- * [Arrays](#arrays)
- * [Objects](#objects)
- * [Parsing JSON](#parsing-json)
- * [Printing JSON](#printing-json)
- * [Example](#example)
- * [Printing](#printing)
- * [Parsing](#parsing)
- * [Caveats](#caveats)
- * [Zero Character](#zero-character)
- * [Character Encoding](#character-encoding)
- * [C Standard](#c-standard)
- * [Floating Point Numbers](#floating-point-numbers)
- * [Deep Nesting Of Arrays And Objects](#deep-nesting-of-arrays-and-objects)
- * [Thread Safety](#thread-safety)
- * [Case Sensitivity](#case-sensitivity)
- * [Duplicate Object Members](#duplicate-object-members)
- * [Enjoy cJSON!](#enjoy-cjson)
-
-## License
-
-MIT License
-
-> Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
->
-> 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.
-
-## Usage
-
-### Welcome to cJSON.
-
-cJSON aims to be the dumbest possible parser that you can get your job done with.
-It's a single file of C, and a single header file.
-
-JSON is described best here: http://www.json.org/
-It's like XML, but fat-free. You use it to move data around, store things, or just
-generally represent your program's state.
-
-As a library, cJSON exists to take away as much legwork as it can, but not get in your way.
-As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it
-in one of two modes: Auto and Manual. Let's have a quick run-through.
-
-I lifted some JSON from this page: http://www.json.org/fatfree.html
-That page inspired me to write cJSON, which is a parser that tries to share the same
-philosophy as JSON itself. Simple, dumb, out of the way.
-
-### Building
-
-There are several ways to incorporate cJSON into your project.
-
-#### copying the source
-
-Because the entire library is only one C file and one header file, you can just copy `cJSON.h` and `cJSON.c` to your projects source and start using it.
-
-cJSON is written in ANSI C (C89) in order to support as many platforms and compilers as possible.
-
-#### CMake
-
-With CMake, cJSON supports a full blown build system. This way you get the most features. CMake with an equal or higher version than 2.8.5 is supported. With CMake it is recommended to do an out of tree build, meaning the compiled files are put in a directory separate from the source files. So in order to build cJSON with CMake on a Unix platform, make a `build` directory and run CMake inside it.
-
-```
-mkdir build
-cd build
-cmake ..
-```
-
-This will create a Makefile and a bunch of other files. You can then compile it:
-
-```
-make
-```
-
-And install it with `make install` if you want. By default it installs the headers `/usr/local/include/cjson` and the libraries to `/usr/local/lib`. It also installs files for pkg-config to make it easier to detect and use an existing installation of CMake. And it installs CMake config files, that can be used by other CMake based projects to discover the library.
-
-You can change the build process with a list of different options that you can pass to CMake. Turn them on with `On` and off with `Off`:
-
-* `-DENABLE_CJSON_TEST=On`: Enable building the tests. (on by default)
-* `-DENABLE_CJSON_UTILS=On`: Enable building cJSON_Utils. (off by default)
-* `-DENABLE_TARGET_EXPORT=On`: Enable the export of CMake targets. Turn off if it makes problems. (on by default)
-* `-DENABLE_CUSTOM_COMPILER_FLAGS=On`: Enable custom compiler flags (currently for Clang, GCC and MSVC). Turn off if it makes problems. (on by default)
-* `-DENABLE_VALGRIND=On`: Run tests with [valgrind](http://valgrind.org). (off by default)
-* `-DENABLE_SANITIZERS=On`: Compile cJSON with [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) enabled (if possible). (off by default)
-* `-DENABLE_SAFE_STACK`: Enable the [SafeStack](https://clang.llvm.org/docs/SafeStack.html) instrumentation pass. Currently only works with the Clang compiler. (off by default)
-* `-DBUILD_SHARED_LIBS=On`: Build the shared libraries. (on by default)
-* `-DBUILD_SHARED_AND_STATIC_LIBS=On`: Build both shared and static libraries. (off by default)
-* `-DCMAKE_INSTALL_PREFIX=/usr`: Set a prefix for the installation.
-* `-DENABLE_LOCALES=On`: Enable the usage of localeconv method. ( on by default )
-* `-DCJSON_OVERRIDE_BUILD_SHARED_LIBS=On`: Enable overriding the value of `BUILD_SHARED_LIBS` with `-DCJSON_BUILD_SHARED_LIBS`.
-* `-DENABLE_CJSON_VERSION_SO`: Enable cJSON so version. ( on by default )
-
-If you are packaging cJSON for a distribution of Linux, you would probably take these steps for example:
-```
-mkdir build
-cd build
-cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usr
-make
-make DESTDIR=$pkgdir install
-```
-
-On Windows CMake is usually used to create a Visual Studio solution file by running it inside the Developer Command Prompt for Visual Studio, for exact steps follow the official documentation from CMake and Microsoft and use the online search engine of your choice. The descriptions of the the options above still generally apply, although not all of them work on Windows.
-
-#### Makefile
-
-**NOTE:** This Method is deprecated. Use CMake if at all possible. Makefile support is limited to fixing bugs.
-
-If you don't have CMake available, but still have GNU make. You can use the makefile to build cJSON:
-
-Run this command in the directory with the source code and it will automatically compile static and shared libraries and a little test program (not the full test suite).
-
-```
-make all
-```
-
-If you want, you can install the compiled library to your system using `make install`. By default it will install the headers in `/usr/local/include/cjson` and the libraries in `/usr/local/lib`. But you can change this behavior by setting the `PREFIX` and `DESTDIR` variables: `make PREFIX=/usr DESTDIR=temp install`. And uninstall them with: `make PREFIX=/usr DESTDIR=temp uninstall`.
-
-#### Meson
-
-To make cjson work in a project using meson, the libcjson dependency has to be included:
-
-```meson
-project('c-json-example', 'c')
-
-cjson = dependency('libcjson')
-
-example = executable(
- 'example',
- 'example.c',
- dependencies: [cjson],
-)
-```
-
-
-#### Vcpkg
-
-You can download and install cJSON using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
-```
-git clone https://github.com/Microsoft/vcpkg.git
-cd vcpkg
-./bootstrap-vcpkg.sh
-./vcpkg integrate install
-vcpkg install cjson
-```
-
-The cJSON port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
-
-### Including cJSON
-
-If you installed it via CMake or the Makefile, you can include cJSON like this:
-
-```c
-#include
-```
-
-### Data Structure
-
-cJSON represents JSON data using the `cJSON` struct data type:
-
-```c
-/* The cJSON structure: */
-typedef struct cJSON
-{
- struct cJSON *next;
- struct cJSON *prev;
- struct cJSON *child;
- int type;
- char *valuestring;
- /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
- int valueint;
- double valuedouble;
- char *string;
-} cJSON;
-```
-
-An item of this type represents a JSON value. The type is stored in `type` as a bit-flag (**this means that you cannot find out the type by just comparing the value of `type`**).
-
-To check the type of an item, use the corresponding `cJSON_Is...` function. It does a `NULL` check followed by a type check and returns a boolean value if the item is of this type.
-
-The type can be one of the following:
-
-* `cJSON_Invalid` (check with `cJSON_IsInvalid`): Represents an invalid item that doesn't contain any value. You automatically have this type if you set the item to all zero bytes.
-* `cJSON_False` (check with `cJSON_IsFalse`): Represents a `false` boolean value. You can also check for boolean values in general with `cJSON_IsBool`.
-* `cJSON_True` (check with `cJSON_IsTrue`): Represents a `true` boolean value. You can also check for boolean values in general with `cJSON_IsBool`.
-* `cJSON_NULL` (check with `cJSON_IsNull`): Represents a `null` value.
-* `cJSON_Number` (check with `cJSON_IsNumber`): Represents a number value. The value is stored as a double in `valuedouble` and also in `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint`.
-* `cJSON_String` (check with `cJSON_IsString`): Represents a string value. It is stored in the form of a zero terminated string in `valuestring`.
-* `cJSON_Array` (check with `cJSON_IsArray`): Represent an array value. This is implemented by pointing `child` to a linked list of `cJSON` items that represent the values in the array. The elements are linked together using `next` and `prev`, where the first element has `prev.next == NULL` and the last element `next == NULL`.
-* `cJSON_Object` (check with `cJSON_IsObject`): Represents an object value. Objects are stored same way as an array, the only difference is that the items in the object store their keys in `string`.
-* `cJSON_Raw` (check with `cJSON_IsRaw`): Represents any kind of JSON that is stored as a zero terminated array of characters in `valuestring`. This can be used, for example, to avoid printing the same static JSON over and over again to save performance. cJSON will never create this type when parsing. Also note that cJSON doesn't check if it is valid JSON.
-
-Additionally there are the following two flags:
-
-* `cJSON_IsReference`: Specifies that the item that `child` points to and/or `valuestring` is not owned by this item, it is only a reference. So `cJSON_Delete` and other functions will only deallocate this item, not its `child`/`valuestring`.
-* `cJSON_StringIsConst`: This means that `string` points to a constant string. This means that `cJSON_Delete` and other functions will not try to deallocate `string`.
-
-### Working with the data structure
-
-For every value type there is a `cJSON_Create...` function that can be used to create an item of that type.
-All of these will allocate a `cJSON` struct that can later be deleted with `cJSON_Delete`.
-Note that you have to delete them at some point, otherwise you will get a memory leak.
-**Important**: If you have added an item to an array or an object already, you **mustn't** delete it with `cJSON_Delete`. Adding it to an array or object transfers its ownership so that when that array or object is deleted,
-it gets deleted as well. You also could use `cJSON_SetValuestring` to change a `cJSON_String`'s `valuestring`, and you needn't to free the previous `valuestring` manually.
-
-#### Basic types
-
-* **null** is created with `cJSON_CreateNull`
-* **booleans** are created with `cJSON_CreateTrue`, `cJSON_CreateFalse` or `cJSON_CreateBool`
-* **numbers** are created with `cJSON_CreateNumber`. This will set both `valuedouble` and `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint`
-* **strings** are created with `cJSON_CreateString` (copies the string) or with `cJSON_CreateStringReference` (directly points to the string. This means that `valuestring` won't be deleted by `cJSON_Delete` and you are responsible for its lifetime, useful for constants)
-
-#### Arrays
-
-You can create an empty array with `cJSON_CreateArray`. `cJSON_CreateArrayReference` can be used to create an array that doesn't "own" its content, so its content doesn't get deleted by `cJSON_Delete`.
-
-To add items to an array, use `cJSON_AddItemToArray` to append items to the end.
-Using `cJSON_AddItemReferenceToArray` an element can be added as a reference to another item, array or string. This means that `cJSON_Delete` will not delete that items `child` or `valuestring` properties, so no double frees are occurring if they are already used elsewhere.
-To insert items in the middle, use `cJSON_InsertItemInArray`. It will insert an item at the given 0 based index and shift all the existing items to the right.
-
-If you want to take an item out of an array at a given index and continue using it, use `cJSON_DetachItemFromArray`, it will return the detached item, so be sure to assign it to a pointer, otherwise you will have a memory leak.
-
-Deleting items is done with `cJSON_DeleteItemFromArray`. It works like `cJSON_DetachItemFromArray`, but deletes the detached item via `cJSON_Delete`.
-
-You can also replace an item in an array in place. Either with `cJSON_ReplaceItemInArray` using an index or with `cJSON_ReplaceItemViaPointer` given a pointer to an element. `cJSON_ReplaceItemViaPointer` will return `0` if it fails. What this does internally is to detach the old item, delete it and insert the new item in its place.
-
-To get the size of an array, use `cJSON_GetArraySize`. Use `cJSON_GetArrayItem` to get an element at a given index.
-
-Because an array is stored as a linked list, iterating it via index is inefficient (`O(n虏)`), so you can iterate over an array using the `cJSON_ArrayForEach` macro in `O(n)` time complexity.
-
-#### Objects
-
-You can create an empty object with `cJSON_CreateObject`. `cJSON_CreateObjectReference` can be used to create an object that doesn't "own" its content, so its content doesn't get deleted by `cJSON_Delete`.
-
-To add items to an object, use `cJSON_AddItemToObject`. Use `cJSON_AddItemToObjectCS` to add an item to an object with a name that is a constant or reference (key of the item, `string` in the `cJSON` struct), so that it doesn't get freed by `cJSON_Delete`.
-Using `cJSON_AddItemReferenceToArray` an element can be added as a reference to another object, array or string. This means that `cJSON_Delete` will not delete that items `child` or `valuestring` properties, so no double frees are occurring if they are already used elsewhere.
-
-If you want to take an item out of an object, use `cJSON_DetachItemFromObjectCaseSensitive`, it will return the detached item, so be sure to assign it to a pointer, otherwise you will have a memory leak.
-
-Deleting items is done with `cJSON_DeleteItemFromObjectCaseSensitive`. It works like `cJSON_DetachItemFromObjectCaseSensitive` followed by `cJSON_Delete`.
-
-You can also replace an item in an object in place. Either with `cJSON_ReplaceItemInObjectCaseSensitive` using a key or with `cJSON_ReplaceItemViaPointer` given a pointer to an element. `cJSON_ReplaceItemViaPointer` will return `0` if it fails. What this does internally is to detach the old item, delete it and insert the new item in its place.
-
-To get the size of an object, you can use `cJSON_GetArraySize`, this works because internally objects are stored as arrays.
-
-If you want to access an item in an object, use `cJSON_GetObjectItemCaseSensitive`.
-
-To iterate over an object, you can use the `cJSON_ArrayForEach` macro the same way as for arrays.
-
-cJSON also provides convenient helper functions for quickly creating a new item and adding it to an object, like `cJSON_AddNullToObject`. They return a pointer to the new item or `NULL` if they failed.
-
-### Parsing JSON
-
-Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse`.
-
-```c
-cJSON *json = cJSON_Parse(string);
-```
-
-Given some JSON in a string (whether zero terminated or not), you can parse it with `cJSON_ParseWithLength`.
-
-```c
-cJSON *json = cJSON_ParseWithLength(string, buffer_length);
-```
-
-It will parse the JSON and allocate a tree of `cJSON` items that represents it. Once it returns, you are fully responsible for deallocating it after use with `cJSON_Delete`.
-
-The allocator used by `cJSON_Parse` is `malloc` and `free` by default but can be changed (globally) with `cJSON_InitHooks`.
-
-If an error occurs a pointer to the position of the error in the input string can be accessed using `cJSON_GetErrorPtr`. Note though that this can produce race conditions in multithreading scenarios, in that case it is better to use `cJSON_ParseWithOpts` with `return_parse_end`.
-By default, characters in the input string that follow the parsed JSON will not be considered as an error.
-
-If you want more options, use `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`.
-`return_parse_end` returns a pointer to the end of the JSON in the input string or the position that an error occurs at (thereby replacing `cJSON_GetErrorPtr` in a thread safe way). `require_null_terminated`, if set to `1` will make it an error if the input string contains data after the JSON.
-
-If you want more options giving buffer length, use `cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)`.
-
-### Printing JSON
-
-Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`.
-
-```c
-char *string = cJSON_Print(json);
-```
-
-It will allocate a string and print a JSON representation of the tree into it. Once it returns, you are fully responsible for deallocating it after use with your allocator. (usually `free`, depends on what has been set with `cJSON_InitHooks`).
-
-`cJSON_Print` will print with whitespace for formatting. If you want to print without formatting, use `cJSON_PrintUnformatted`.
-
-If you have a rough idea of how big your resulting string will be, you can use `cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)`. `fmt` is a boolean to turn formatting with whitespace on and off. `prebuffer` specifies the first buffer size to use for printing. `cJSON_Print` currently uses 256 bytes for its first buffer size. Once printing runs out of space, a new buffer is allocated and the old gets copied over before printing is continued.
-
-These dynamic buffer allocations can be completely avoided by using `cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)`. It takes a buffer to a pointer to print to and its length. If the length is reached, printing will fail and it returns `0`. In case of success, `1` is returned. Note that you should provide 5 bytes more than is actually needed, because cJSON is not 100% accurate in estimating if the provided memory is enough.
-
-### Example
-
-In this example we want to build and parse the following JSON:
-
-```json
-{
- "name": "Awesome 4K",
- "resolutions": [
- {
- "width": 1280,
- "height": 720
- },
- {
- "width": 1920,
- "height": 1080
- },
- {
- "width": 3840,
- "height": 2160
- }
- ]
-}
-```
-
-#### Printing
-
-Let's build the above JSON and print it to a string:
-
-```c
-//create a monitor with a list of supported resolutions
-//NOTE: Returns a heap allocated string, you are required to free it after use.
-char *create_monitor(void)
-{
- const unsigned int resolution_numbers[3][2] = {
- {1280, 720},
- {1920, 1080},
- {3840, 2160}
- };
- char *string = NULL;
- cJSON *name = NULL;
- cJSON *resolutions = NULL;
- cJSON *resolution = NULL;
- cJSON *width = NULL;
- cJSON *height = NULL;
- size_t index = 0;
-
- cJSON *monitor = cJSON_CreateObject();
- if (monitor == NULL)
- {
- goto end;
- }
-
- name = cJSON_CreateString("Awesome 4K");
- if (name == NULL)
- {
- goto end;
- }
- /* after creation was successful, immediately add it to the monitor,
- * thereby transferring ownership of the pointer to it */
- cJSON_AddItemToObject(monitor, "name", name);
-
- resolutions = cJSON_CreateArray();
- if (resolutions == NULL)
- {
- goto end;
- }
- cJSON_AddItemToObject(monitor, "resolutions", resolutions);
-
- for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
- {
- resolution = cJSON_CreateObject();
- if (resolution == NULL)
- {
- goto end;
- }
- cJSON_AddItemToArray(resolutions, resolution);
-
- width = cJSON_CreateNumber(resolution_numbers[index][0]);
- if (width == NULL)
- {
- goto end;
- }
- cJSON_AddItemToObject(resolution, "width", width);
-
- height = cJSON_CreateNumber(resolution_numbers[index][1]);
- if (height == NULL)
- {
- goto end;
- }
- cJSON_AddItemToObject(resolution, "height", height);
- }
-
- string = cJSON_Print(monitor);
- if (string == NULL)
- {
- fprintf(stderr, "Failed to print monitor.\n");
- }
-
-end:
- cJSON_Delete(monitor);
- return string;
-}
-```
-
-Alternatively we can use the `cJSON_Add...ToObject` helper functions to make our lives a little easier:
-
-```c
-//NOTE: Returns a heap allocated string, you are required to free it after use.
-char *create_monitor_with_helpers(void)
-{
- const unsigned int resolution_numbers[3][2] = {
- {1280, 720},
- {1920, 1080},
- {3840, 2160}
- };
- char *string = NULL;
- cJSON *resolutions = NULL;
- size_t index = 0;
-
- cJSON *monitor = cJSON_CreateObject();
-
- if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL)
- {
- goto end;
- }
-
- resolutions = cJSON_AddArrayToObject(monitor, "resolutions");
- if (resolutions == NULL)
- {
- goto end;
- }
-
- for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
- {
- cJSON *resolution = cJSON_CreateObject();
-
- if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL)
- {
- goto end;
- }
-
- if (cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL)
- {
- goto end;
- }
-
- cJSON_AddItemToArray(resolutions, resolution);
- }
-
- string = cJSON_Print(monitor);
- if (string == NULL)
- {
- fprintf(stderr, "Failed to print monitor.\n");
- }
-
-end:
- cJSON_Delete(monitor);
- return string;
-}
-```
-
-#### Parsing
-
-In this example we will parse a JSON in the above format and check if the monitor supports a Full HD resolution while printing some diagnostic output:
-
-```c
-/* return 1 if the monitor supports full hd, 0 otherwise */
-int supports_full_hd(const char * const monitor)
-{
- const cJSON *resolution = NULL;
- const cJSON *resolutions = NULL;
- const cJSON *name = NULL;
- int status = 0;
- cJSON *monitor_json = cJSON_Parse(monitor);
- if (monitor_json == NULL)
- {
- const char *error_ptr = cJSON_GetErrorPtr();
- if (error_ptr != NULL)
- {
- fprintf(stderr, "Error before: %s\n", error_ptr);
- }
- status = 0;
- goto end;
- }
-
- name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name");
- if (cJSON_IsString(name) && (name->valuestring != NULL))
- {
- printf("Checking monitor \"%s\"\n", name->valuestring);
- }
-
- resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions");
- cJSON_ArrayForEach(resolution, resolutions)
- {
- cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width");
- cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height");
-
- if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height))
- {
- status = 0;
- goto end;
- }
-
- if ((width->valuedouble == 1920) && (height->valuedouble == 1080))
- {
- status = 1;
- goto end;
- }
- }
-
-end:
- cJSON_Delete(monitor_json);
- return status;
-}
-```
-
-Note that there are no NULL checks except for the result of `cJSON_Parse` because `cJSON_GetObjectItemCaseSensitive` checks for `NULL` inputs already, so a `NULL` value is just propagated and `cJSON_IsNumber` and `cJSON_IsString` return `0` if the input is `NULL`.
-
-### Caveats
-
-#### Zero Character
-
-cJSON doesn't support strings that contain the zero character `'\0'` or `\u0000`. This is impossible with the current API because strings are zero terminated.
-
-#### Character Encoding
-
-cJSON only supports UTF-8 encoded input. In most cases it doesn't reject invalid UTF-8 as input though, it just propagates it through as is. As long as the input doesn't contain invalid UTF-8, the output will always be valid UTF-8.
-
-#### C Standard
-
-cJSON is written in ANSI C (or C89, C90). If your compiler or C library doesn't follow this standard, correct behavior is not guaranteed.
-
-NOTE: ANSI C is not C++ therefore it shouldn't be compiled with a C++ compiler. You can compile it with a C compiler and link it with your C++ code however. Although compiling with a C++ compiler might work, correct behavior is not guaranteed.
-
-#### Floating Point Numbers
-
-cJSON does not officially support any `double` implementations other than IEEE754 double precision floating point numbers. It might still work with other implementations but bugs with these will be considered invalid.
-
-The maximum length of a floating point literal that cJSON supports is currently 63 characters.
-
-#### Deep Nesting Of Arrays And Objects
-
-cJSON doesn't support arrays and objects that are nested too deeply because this would result in a stack overflow. To prevent this cJSON limits the depth to `CJSON_NESTING_LIMIT` which is 1000 by default but can be changed at compile time.
-
-#### Thread Safety
-
-In general cJSON is **not thread safe**.
-
-However it is thread safe under the following conditions:
-
-* `cJSON_GetErrorPtr` is never used (the `return_parse_end` parameter of `cJSON_ParseWithOpts` can be used instead)
-* `cJSON_InitHooks` is only ever called before using cJSON in any threads.
-* `setlocale` is never called before all calls to cJSON functions have returned.
-
-#### Case Sensitivity
-
-When cJSON was originally created, it didn't follow the JSON standard and didn't make a distinction between uppercase and lowercase letters. If you want the correct, standard compliant, behavior, you need to use the `CaseSensitive` functions where available.
-
-#### Duplicate Object Members
-
-cJSON supports parsing and printing JSON that contains objects that have multiple members with the same name. `cJSON_GetObjectItemCaseSensitive` however will always only return the first one.
-
-# Enjoy cJSON!
-
-- Dave Gamble (original author)
-- Max Bruckner and Alan Wang (current maintainer)
-- and the other [cJSON contributors](CONTRIBUTORS.md)
diff --git a/common/cJSON/cJSON.c b/common/cJSON/cJSON.c
deleted file mode 100644
index d7c7236..0000000
--- a/common/cJSON/cJSON.c
+++ /dev/null
@@ -1,3164 +0,0 @@
-/*
- Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
- 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.
-*/
-
-/* cJSON */
-/* JSON parser in C. */
-
-/* disable warnings about old C89 functions in MSVC */
-#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
-#define _CRT_SECURE_NO_DEPRECATE
-#endif
-
-#ifdef __GNUC__
-#pragma GCC visibility push(default)
-#endif
-#if defined(_MSC_VER)
-#pragma warning (push)
-/* disable warning about single line comments in system headers */
-#pragma warning (disable : 4001)
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef ENABLE_LOCALES
-#include
-#endif
-
-#if defined(_MSC_VER)
-#pragma warning (pop)
-#endif
-#ifdef __GNUC__
-#pragma GCC visibility pop
-#endif
-
-#include "cJSON.h"
-
-/* define our own boolean type */
-#ifdef true
-#undef true
-#endif
-#define true ((cJSON_bool)1)
-
-#ifdef false
-#undef false
-#endif
-#define false ((cJSON_bool)0)
-
-/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
-#ifndef isinf
-#define isinf(d) (isnan((d - d)) && !isnan(d))
-#endif
-#ifndef isnan
-#define isnan(d) (d != d)
-#endif
-
-#ifndef NAN
-#ifdef _WIN32
-#define NAN sqrt(-1.0)
-#else
-#define NAN 0.0/0.0
-#endif
-#endif
-
-typedef struct {
- const unsigned char *json;
- size_t position;
-} error;
-static error global_error = { NULL, 0 };
-
-CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
-{
- return (const char*) (global_error.json + global_error.position);
-}
-
-CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
-{
- if (!cJSON_IsString(item))
- {
- return NULL;
- }
-
- return item->valuestring;
-}
-
-CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
-{
- if (!cJSON_IsNumber(item))
- {
- return (double) NAN;
- }
-
- return item->valuedouble;
-}
-
-/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
-#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18)
- #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
-#endif
-
-CJSON_PUBLIC(const char*) cJSON_Version(void)
-{
- static char version[15];
- sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
-
- return version;
-}
-
-/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
-static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
-{
- if ((string1 == NULL) || (string2 == NULL))
- {
- return 1;
- }
-
- if (string1 == string2)
- {
- return 0;
- }
-
- for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
- {
- if (*string1 == '\0')
- {
- return 0;
- }
- }
-
- return tolower(*string1) - tolower(*string2);
-}
-
-typedef struct internal_hooks
-{
- void *(CJSON_CDECL *allocate)(size_t size);
- void (CJSON_CDECL *deallocate)(void *pointer);
- void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
-} internal_hooks;
-
-#if defined(_MSC_VER)
-/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
-static void * CJSON_CDECL internal_malloc(size_t size)
-{
- return malloc(size);
-}
-static void CJSON_CDECL internal_free(void *pointer)
-{
- free(pointer);
-}
-static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
-{
- return realloc(pointer, size);
-}
-#else
-#define internal_malloc malloc
-#define internal_free free
-#define internal_realloc realloc
-#endif
-
-/* strlen of character literals resolved at compile time */
-#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
-
-static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
-
-static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
-{
- size_t length = 0;
- unsigned char *copy = NULL;
-
- if (string == NULL)
- {
- return NULL;
- }
-
- length = strlen((const char*)string) + sizeof("");
- copy = (unsigned char*)hooks->allocate(length);
- if (copy == NULL)
- {
- return NULL;
- }
- memcpy(copy, string, length);
-
- return copy;
-}
-
-CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
-{
- if (hooks == NULL)
- {
- /* Reset hooks */
- global_hooks.allocate = malloc;
- global_hooks.deallocate = free;
- global_hooks.reallocate = realloc;
- return;
- }
-
- global_hooks.allocate = malloc;
- if (hooks->malloc_fn != NULL)
- {
- global_hooks.allocate = hooks->malloc_fn;
- }
-
- global_hooks.deallocate = free;
- if (hooks->free_fn != NULL)
- {
- global_hooks.deallocate = hooks->free_fn;
- }
-
- /* use realloc only if both free and malloc are used */
- global_hooks.reallocate = NULL;
- if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
- {
- global_hooks.reallocate = realloc;
- }
-}
-
-/* Internal constructor. */
-static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
-{
- cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
- if (node)
- {
- memset(node, '\0', sizeof(cJSON));
- }
-
- return node;
-}
-
-/* Delete a cJSON structure. */
-CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
-{
- cJSON *next = NULL;
- while (item != NULL)
- {
- next = item->next;
- if (!(item->type & cJSON_IsReference) && (item->child != NULL))
- {
- cJSON_Delete(item->child);
- }
- if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
- {
- global_hooks.deallocate(item->valuestring);
- item->valuestring = NULL;
- }
- if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
- {
- global_hooks.deallocate(item->string);
- item->string = NULL;
- }
- global_hooks.deallocate(item);
- item = next;
- }
-}
-
-/* get the decimal point character of the current locale */
-static unsigned char get_decimal_point(void)
-{
-#ifdef ENABLE_LOCALES
- struct lconv *lconv = localeconv();
- return (unsigned char) lconv->decimal_point[0];
-#else
- return '.';
-#endif
-}
-
-typedef struct
-{
- const unsigned char *content;
- size_t length;
- size_t offset;
- size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
- internal_hooks hooks;
-} parse_buffer;
-
-/* check if the given size is left to read in a given parse buffer (starting with 1) */
-#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
-/* check if the buffer can be accessed at the given index (starting with 0) */
-#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
-#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
-/* get a pointer to the buffer at the position */
-#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
-
-/* Parse the input text to generate a number, and populate the result into item. */
-static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
-{
- double number = 0;
- unsigned char *after_end = NULL;
- unsigned char number_c_string[64];
- unsigned char decimal_point = get_decimal_point();
- size_t i = 0;
-
- if ((input_buffer == NULL) || (input_buffer->content == NULL))
- {
- return false;
- }
-
- /* copy the number into a temporary buffer and replace '.' with the decimal point
- * of the current locale (for strtod)
- * This also takes care of '\0' not necessarily being available for marking the end of the input */
- for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
- {
- switch (buffer_at_offset(input_buffer)[i])
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '+':
- case '-':
- case 'e':
- case 'E':
- number_c_string[i] = buffer_at_offset(input_buffer)[i];
- break;
-
- case '.':
- number_c_string[i] = decimal_point;
- break;
-
- default:
- goto loop_end;
- }
- }
-loop_end:
- number_c_string[i] = '\0';
-
- number = strtod((const char*)number_c_string, (char**)&after_end);
- if (number_c_string == after_end)
- {
- return false; /* parse_error */
- }
-
- item->valuedouble = number;
-
- /* use saturation in case of overflow */
- if (number >= INT_MAX)
- {
- item->valueint = INT_MAX;
- }
- else if (number <= (double)INT_MIN)
- {
- item->valueint = INT_MIN;
- }
- else
- {
- item->valueint = (int)number;
- }
-
- item->type = cJSON_Number;
-
- input_buffer->offset += (size_t)(after_end - number_c_string);
- return true;
-}
-
-/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
-CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
-{
- if (number >= INT_MAX)
- {
- object->valueint = INT_MAX;
- }
- else if (number <= (double)INT_MIN)
- {
- object->valueint = INT_MIN;
- }
- else
- {
- object->valueint = (int)number;
- }
-
- return object->valuedouble = number;
-}
-
-/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */
-CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
-{
- char *copy = NULL;
- size_t v1_len;
- size_t v2_len;
- /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
- if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
- {
- return NULL;
- }
- /* return NULL if the object is corrupted or valuestring is NULL */
- if (object->valuestring == NULL || valuestring == NULL)
- {
- return NULL;
- }
-
- v1_len = strlen(valuestring);
- v2_len = strlen(object->valuestring);
-
- if (v1_len <= v2_len)
- {
- /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */
- if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring ))
- {
- return NULL;
- }
- strcpy(object->valuestring, valuestring);
- return object->valuestring;
- }
- copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
- if (copy == NULL)
- {
- return NULL;
- }
- if (object->valuestring != NULL)
- {
- cJSON_free(object->valuestring);
- }
- object->valuestring = copy;
-
- return copy;
-}
-
-typedef struct
-{
- unsigned char *buffer;
- size_t length;
- size_t offset;
- size_t depth; /* current nesting depth (for formatted printing) */
- cJSON_bool noalloc;
- cJSON_bool format; /* is this print a formatted print */
- internal_hooks hooks;
-} printbuffer;
-
-/* realloc printbuffer if necessary to have at least "needed" bytes more */
-static unsigned char* ensure(printbuffer * const p, size_t needed)
-{
- unsigned char *newbuffer = NULL;
- size_t newsize = 0;
-
- if ((p == NULL) || (p->buffer == NULL))
- {
- return NULL;
- }
-
- if ((p->length > 0) && (p->offset >= p->length))
- {
- /* make sure that offset is valid */
- return NULL;
- }
-
- if (needed > INT_MAX)
- {
- /* sizes bigger than INT_MAX are currently not supported */
- return NULL;
- }
-
- needed += p->offset + 1;
- if (needed <= p->length)
- {
- return p->buffer + p->offset;
- }
-
- if (p->noalloc) {
- return NULL;
- }
-
- /* calculate new buffer size */
- if (needed > (INT_MAX / 2))
- {
- /* overflow of int, use INT_MAX if possible */
- if (needed <= INT_MAX)
- {
- newsize = INT_MAX;
- }
- else
- {
- return NULL;
- }
- }
- else
- {
- newsize = needed * 2;
- }
-
- if (p->hooks.reallocate != NULL)
- {
- /* reallocate with realloc if available */
- newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
- if (newbuffer == NULL)
- {
- p->hooks.deallocate(p->buffer);
- p->length = 0;
- p->buffer = NULL;
-
- return NULL;
- }
- }
- else
- {
- /* otherwise reallocate manually */
- newbuffer = (unsigned char*)p->hooks.allocate(newsize);
- if (!newbuffer)
- {
- p->hooks.deallocate(p->buffer);
- p->length = 0;
- p->buffer = NULL;
-
- return NULL;
- }
-
- memcpy(newbuffer, p->buffer, p->offset + 1);
- p->hooks.deallocate(p->buffer);
- }
- p->length = newsize;
- p->buffer = newbuffer;
-
- return newbuffer + p->offset;
-}
-
-/* calculate the new length of the string in a printbuffer and update the offset */
-static void update_offset(printbuffer * const buffer)
-{
- const unsigned char *buffer_pointer = NULL;
- if ((buffer == NULL) || (buffer->buffer == NULL))
- {
- return;
- }
- buffer_pointer = buffer->buffer + buffer->offset;
-
- buffer->offset += strlen((const char*)buffer_pointer);
-}
-
-/* securely comparison of floating-point variables */
-static cJSON_bool compare_double(double a, double b)
-{
- double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
- return (fabs(a - b) <= maxVal * DBL_EPSILON);
-}
-
-/* Render the number nicely from the given item into a string. */
-static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output_pointer = NULL;
- double d = item->valuedouble;
- int length = 0;
- size_t i = 0;
- unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
- unsigned char decimal_point = get_decimal_point();
- double test = 0.0;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* This checks for NaN and Infinity */
- if (isnan(d) || isinf(d))
- {
- length = sprintf((char*)number_buffer, "null");
- }
- else if(d == (double)item->valueint)
- {
- length = sprintf((char*)number_buffer, "%d", item->valueint);
- }
- else
- {
- /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
- length = sprintf((char*)number_buffer, "%1.15g", d);
-
- /* Check whether the original double can be recovered */
- if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
- {
- /* If not, print with 17 decimal places of precision */
- length = sprintf((char*)number_buffer, "%1.17g", d);
- }
- }
-
- /* sprintf failed or buffer overrun occurred */
- if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
- {
- return false;
- }
-
- /* reserve appropriate space in the output */
- output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
- if (output_pointer == NULL)
- {
- return false;
- }
-
- /* copy the printed number to the output and replace locale
- * dependent decimal point with '.' */
- for (i = 0; i < ((size_t)length); i++)
- {
- if (number_buffer[i] == decimal_point)
- {
- output_pointer[i] = '.';
- continue;
- }
-
- output_pointer[i] = number_buffer[i];
- }
- output_pointer[i] = '\0';
-
- output_buffer->offset += (size_t)length;
-
- return true;
-}
-
-/* parse 4 digit hexadecimal number */
-static unsigned parse_hex4(const unsigned char * const input)
-{
- unsigned int h = 0;
- size_t i = 0;
-
- for (i = 0; i < 4; i++)
- {
- /* parse digit */
- if ((input[i] >= '0') && (input[i] <= '9'))
- {
- h += (unsigned int) input[i] - '0';
- }
- else if ((input[i] >= 'A') && (input[i] <= 'F'))
- {
- h += (unsigned int) 10 + input[i] - 'A';
- }
- else if ((input[i] >= 'a') && (input[i] <= 'f'))
- {
- h += (unsigned int) 10 + input[i] - 'a';
- }
- else /* invalid */
- {
- return 0;
- }
-
- if (i < 3)
- {
- /* shift left to make place for the next nibble */
- h = h << 4;
- }
- }
-
- return h;
-}
-
-/* converts a UTF-16 literal to UTF-8
- * A literal can be one or two sequences of the form \uXXXX */
-static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
-{
- long unsigned int codepoint = 0;
- unsigned int first_code = 0;
- const unsigned char *first_sequence = input_pointer;
- unsigned char utf8_length = 0;
- unsigned char utf8_position = 0;
- unsigned char sequence_length = 0;
- unsigned char first_byte_mark = 0;
-
- if ((input_end - first_sequence) < 6)
- {
- /* input ends unexpectedly */
- goto fail;
- }
-
- /* get the first utf16 sequence */
- first_code = parse_hex4(first_sequence + 2);
-
- /* check that the code is valid */
- if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
- {
- goto fail;
- }
-
- /* UTF16 surrogate pair */
- if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
- {
- const unsigned char *second_sequence = first_sequence + 6;
- unsigned int second_code = 0;
- sequence_length = 12; /* \uXXXX\uXXXX */
-
- if ((input_end - second_sequence) < 6)
- {
- /* input ends unexpectedly */
- goto fail;
- }
-
- if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
- {
- /* missing second half of the surrogate pair */
- goto fail;
- }
-
- /* get the second utf16 sequence */
- second_code = parse_hex4(second_sequence + 2);
- /* check that the code is valid */
- if ((second_code < 0xDC00) || (second_code > 0xDFFF))
- {
- /* invalid second half of the surrogate pair */
- goto fail;
- }
-
-
- /* calculate the unicode codepoint from the surrogate pair */
- codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
- }
- else
- {
- sequence_length = 6; /* \uXXXX */
- codepoint = first_code;
- }
-
- /* encode as UTF-8
- * takes at maximum 4 bytes to encode:
- * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
- if (codepoint < 0x80)
- {
- /* normal ascii, encoding 0xxxxxxx */
- utf8_length = 1;
- }
- else if (codepoint < 0x800)
- {
- /* two bytes, encoding 110xxxxx 10xxxxxx */
- utf8_length = 2;
- first_byte_mark = 0xC0; /* 11000000 */
- }
- else if (codepoint < 0x10000)
- {
- /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
- utf8_length = 3;
- first_byte_mark = 0xE0; /* 11100000 */
- }
- else if (codepoint <= 0x10FFFF)
- {
- /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
- utf8_length = 4;
- first_byte_mark = 0xF0; /* 11110000 */
- }
- else
- {
- /* invalid unicode codepoint */
- goto fail;
- }
-
- /* encode as utf8 */
- for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
- {
- /* 10xxxxxx */
- (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
- codepoint >>= 6;
- }
- /* encode first byte */
- if (utf8_length > 1)
- {
- (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
- }
- else
- {
- (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
- }
-
- *output_pointer += utf8_length;
-
- return sequence_length;
-
-fail:
- return 0;
-}
-
-/* Parse the input text into an unescaped cinput, and populate item. */
-static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
-{
- const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
- const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
- unsigned char *output_pointer = NULL;
- unsigned char *output = NULL;
-
- /* not a string */
- if (buffer_at_offset(input_buffer)[0] != '\"')
- {
- goto fail;
- }
-
- {
- /* calculate approximate size of the output (overestimate) */
- size_t allocation_length = 0;
- size_t skipped_bytes = 0;
- while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
- {
- /* is escape sequence */
- if (input_end[0] == '\\')
- {
- if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
- {
- /* prevent buffer overflow when last input character is a backslash */
- goto fail;
- }
- skipped_bytes++;
- input_end++;
- }
- input_end++;
- }
- if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
- {
- goto fail; /* string ended unexpectedly */
- }
-
- /* This is at most how much we need for the output */
- allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
- output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
- if (output == NULL)
- {
- goto fail; /* allocation failure */
- }
- }
-
- output_pointer = output;
- /* loop through the string literal */
- while (input_pointer < input_end)
- {
- if (*input_pointer != '\\')
- {
- *output_pointer++ = *input_pointer++;
- }
- /* escape sequence */
- else
- {
- unsigned char sequence_length = 2;
- if ((input_end - input_pointer) < 1)
- {
- goto fail;
- }
-
- switch (input_pointer[1])
- {
- case 'b':
- *output_pointer++ = '\b';
- break;
- case 'f':
- *output_pointer++ = '\f';
- break;
- case 'n':
- *output_pointer++ = '\n';
- break;
- case 'r':
- *output_pointer++ = '\r';
- break;
- case 't':
- *output_pointer++ = '\t';
- break;
- case '\"':
- case '\\':
- case '/':
- *output_pointer++ = input_pointer[1];
- break;
-
- /* UTF-16 literal */
- case 'u':
- sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
- if (sequence_length == 0)
- {
- /* failed to convert UTF16-literal to UTF-8 */
- goto fail;
- }
- break;
-
- default:
- goto fail;
- }
- input_pointer += sequence_length;
- }
- }
-
- /* zero terminate the output */
- *output_pointer = '\0';
-
- item->type = cJSON_String;
- item->valuestring = (char*)output;
-
- input_buffer->offset = (size_t) (input_end - input_buffer->content);
- input_buffer->offset++;
-
- return true;
-
-fail:
- if (output != NULL)
- {
- input_buffer->hooks.deallocate(output);
- output = NULL;
- }
-
- if (input_pointer != NULL)
- {
- input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
- }
-
- return false;
-}
-
-/* Render the cstring provided to an escaped version that can be printed. */
-static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
-{
- const unsigned char *input_pointer = NULL;
- unsigned char *output = NULL;
- unsigned char *output_pointer = NULL;
- size_t output_length = 0;
- /* numbers of additional characters needed for escaping */
- size_t escape_characters = 0;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* empty string */
- if (input == NULL)
- {
- output = ensure(output_buffer, sizeof("\"\""));
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "\"\"");
-
- return true;
- }
-
- /* set "flag" to 1 if something needs to be escaped */
- for (input_pointer = input; *input_pointer; input_pointer++)
- {
- switch (*input_pointer)
- {
- case '\"':
- case '\\':
- case '\b':
- case '\f':
- case '\n':
- case '\r':
- case '\t':
- /* one character escape sequence */
- escape_characters++;
- break;
- default:
- if (*input_pointer < 32)
- {
- /* UTF-16 escape sequence uXXXX */
- escape_characters += 5;
- }
- break;
- }
- }
- output_length = (size_t)(input_pointer - input) + escape_characters;
-
- output = ensure(output_buffer, output_length + sizeof("\"\""));
- if (output == NULL)
- {
- return false;
- }
-
- /* no characters have to be escaped */
- if (escape_characters == 0)
- {
- output[0] = '\"';
- memcpy(output + 1, input, output_length);
- output[output_length + 1] = '\"';
- output[output_length + 2] = '\0';
-
- return true;
- }
-
- output[0] = '\"';
- output_pointer = output + 1;
- /* copy the string */
- for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
- {
- if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
- {
- /* normal character, copy */
- *output_pointer = *input_pointer;
- }
- else
- {
- /* character needs to be escaped */
- *output_pointer++ = '\\';
- switch (*input_pointer)
- {
- case '\\':
- *output_pointer = '\\';
- break;
- case '\"':
- *output_pointer = '\"';
- break;
- case '\b':
- *output_pointer = 'b';
- break;
- case '\f':
- *output_pointer = 'f';
- break;
- case '\n':
- *output_pointer = 'n';
- break;
- case '\r':
- *output_pointer = 'r';
- break;
- case '\t':
- *output_pointer = 't';
- break;
- default:
- /* escape and print as unicode codepoint */
- sprintf((char*)output_pointer, "u%04x", *input_pointer);
- output_pointer += 4;
- break;
- }
- }
- }
- output[output_length + 1] = '\"';
- output[output_length + 2] = '\0';
-
- return true;
-}
-
-/* Invoke print_string_ptr (which is useful) on an item. */
-static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
-{
- return print_string_ptr((unsigned char*)item->valuestring, p);
-}
-
-/* Predeclare these prototypes. */
-static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
-static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
-static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
-static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
-
-/* Utility to jump whitespace and cr/lf */
-static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
-{
- if ((buffer == NULL) || (buffer->content == NULL))
- {
- return NULL;
- }
-
- if (cannot_access_at_index(buffer, 0))
- {
- return buffer;
- }
-
- while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
- {
- buffer->offset++;
- }
-
- if (buffer->offset == buffer->length)
- {
- buffer->offset--;
- }
-
- return buffer;
-}
-
-/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
-static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
-{
- if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
- {
- return NULL;
- }
-
- if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
- {
- buffer->offset += 3;
- }
-
- return buffer;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
-{
- size_t buffer_length;
-
- if (NULL == value)
- {
- return NULL;
- }
-
- /* Adding null character size due to require_null_terminated. */
- buffer_length = strlen(value) + sizeof("");
-
- return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
-}
-
-/* Parse an object - create a new root, and populate. */
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
-{
- parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
- cJSON *item = NULL;
-
- /* reset error position */
- global_error.json = NULL;
- global_error.position = 0;
-
- if (value == NULL || 0 == buffer_length)
- {
- goto fail;
- }
-
- buffer.content = (const unsigned char*)value;
- buffer.length = buffer_length;
- buffer.offset = 0;
- buffer.hooks = global_hooks;
-
- item = cJSON_New_Item(&global_hooks);
- if (item == NULL) /* memory fail */
- {
- goto fail;
- }
-
- if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
- {
- /* parse failure. ep is set. */
- goto fail;
- }
-
- /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
- if (require_null_terminated)
- {
- buffer_skip_whitespace(&buffer);
- if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
- {
- goto fail;
- }
- }
- if (return_parse_end)
- {
- *return_parse_end = (const char*)buffer_at_offset(&buffer);
- }
-
- return item;
-
-fail:
- if (item != NULL)
- {
- cJSON_Delete(item);
- }
-
- if (value != NULL)
- {
- error local_error;
- local_error.json = (const unsigned char*)value;
- local_error.position = 0;
-
- if (buffer.offset < buffer.length)
- {
- local_error.position = buffer.offset;
- }
- else if (buffer.length > 0)
- {
- local_error.position = buffer.length - 1;
- }
-
- if (return_parse_end != NULL)
- {
- *return_parse_end = (const char*)local_error.json + local_error.position;
- }
-
- global_error = local_error;
- }
-
- return NULL;
-}
-
-/* Default options for cJSON_Parse */
-CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
-{
- return cJSON_ParseWithOpts(value, 0, 0);
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
-{
- return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
-}
-
-#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
-
-static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
-{
- static const size_t default_buffer_size = 256;
- printbuffer buffer[1];
- unsigned char *printed = NULL;
-
- memset(buffer, 0, sizeof(buffer));
-
- /* create buffer */
- buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
- buffer->length = default_buffer_size;
- buffer->format = format;
- buffer->hooks = *hooks;
- if (buffer->buffer == NULL)
- {
- goto fail;
- }
-
- /* print the value */
- if (!print_value(item, buffer))
- {
- goto fail;
- }
- update_offset(buffer);
-
- /* check if reallocate is available */
- if (hooks->reallocate != NULL)
- {
- printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
- if (printed == NULL) {
- goto fail;
- }
- buffer->buffer = NULL;
- }
- else /* otherwise copy the JSON over to a new buffer */
- {
- printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
- if (printed == NULL)
- {
- goto fail;
- }
- memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
- printed[buffer->offset] = '\0'; /* just to be sure */
-
- /* free the buffer */
- hooks->deallocate(buffer->buffer);
- buffer->buffer = NULL;
- }
-
- return printed;
-
-fail:
- if (buffer->buffer != NULL)
- {
- hooks->deallocate(buffer->buffer);
- buffer->buffer = NULL;
- }
-
- if (printed != NULL)
- {
- hooks->deallocate(printed);
- printed = NULL;
- }
-
- return NULL;
-}
-
-/* Render a cJSON item/entity/structure to text. */
-CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
-{
- return (char*)print(item, true, &global_hooks);
-}
-
-CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
-{
- return (char*)print(item, false, &global_hooks);
-}
-
-CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
-{
- printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
-
- if (prebuffer < 0)
- {
- return NULL;
- }
-
- p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
- if (!p.buffer)
- {
- return NULL;
- }
-
- p.length = (size_t)prebuffer;
- p.offset = 0;
- p.noalloc = false;
- p.format = fmt;
- p.hooks = global_hooks;
-
- if (!print_value(item, &p))
- {
- global_hooks.deallocate(p.buffer);
- p.buffer = NULL;
- return NULL;
- }
-
- return (char*)p.buffer;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
-{
- printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
-
- if ((length < 0) || (buffer == NULL))
- {
- return false;
- }
-
- p.buffer = (unsigned char*)buffer;
- p.length = (size_t)length;
- p.offset = 0;
- p.noalloc = true;
- p.format = format;
- p.hooks = global_hooks;
-
- return print_value(item, &p);
-}
-
-/* Parser core - when encountering text, process appropriately. */
-static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
-{
- if ((input_buffer == NULL) || (input_buffer->content == NULL))
- {
- return false; /* no input */
- }
-
- /* parse the different types of values */
- /* null */
- if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
- {
- item->type = cJSON_NULL;
- input_buffer->offset += 4;
- return true;
- }
- /* false */
- if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
- {
- item->type = cJSON_False;
- input_buffer->offset += 5;
- return true;
- }
- /* true */
- if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
- {
- item->type = cJSON_True;
- item->valueint = 1;
- input_buffer->offset += 4;
- return true;
- }
- /* string */
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
- {
- return parse_string(item, input_buffer);
- }
- /* number */
- if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
- {
- return parse_number(item, input_buffer);
- }
- /* array */
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
- {
- return parse_array(item, input_buffer);
- }
- /* object */
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
- {
- return parse_object(item, input_buffer);
- }
-
- return false;
-}
-
-/* Render a value to text. */
-static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output = NULL;
-
- if ((item == NULL) || (output_buffer == NULL))
- {
- return false;
- }
-
- switch ((item->type) & 0xFF)
- {
- case cJSON_NULL:
- output = ensure(output_buffer, 5);
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "null");
- return true;
-
- case cJSON_False:
- output = ensure(output_buffer, 6);
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "false");
- return true;
-
- case cJSON_True:
- output = ensure(output_buffer, 5);
- if (output == NULL)
- {
- return false;
- }
- strcpy((char*)output, "true");
- return true;
-
- case cJSON_Number:
- return print_number(item, output_buffer);
-
- case cJSON_Raw:
- {
- size_t raw_length = 0;
- if (item->valuestring == NULL)
- {
- return false;
- }
-
- raw_length = strlen(item->valuestring) + sizeof("");
- output = ensure(output_buffer, raw_length);
- if (output == NULL)
- {
- return false;
- }
- memcpy(output, item->valuestring, raw_length);
- return true;
- }
-
- case cJSON_String:
- return print_string(item, output_buffer);
-
- case cJSON_Array:
- return print_array(item, output_buffer);
-
- case cJSON_Object:
- return print_object(item, output_buffer);
-
- default:
- return false;
- }
-}
-
-/* Build an array from input text. */
-static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
-{
- cJSON *head = NULL; /* head of the linked list */
- cJSON *current_item = NULL;
-
- if (input_buffer->depth >= CJSON_NESTING_LIMIT)
- {
- return false; /* to deeply nested */
- }
- input_buffer->depth++;
-
- if (buffer_at_offset(input_buffer)[0] != '[')
- {
- /* not an array */
- goto fail;
- }
-
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
- {
- /* empty array */
- goto success;
- }
-
- /* check if we skipped to the end of the buffer */
- if (cannot_access_at_index(input_buffer, 0))
- {
- input_buffer->offset--;
- goto fail;
- }
-
- /* step back to character in front of the first element */
- input_buffer->offset--;
- /* loop through the comma separated array elements */
- do
- {
- /* allocate next item */
- cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
- if (new_item == NULL)
- {
- goto fail; /* allocation failure */
- }
-
- /* attach next item to list */
- if (head == NULL)
- {
- /* start the linked list */
- current_item = head = new_item;
- }
- else
- {
- /* add to the end and advance */
- current_item->next = new_item;
- new_item->prev = current_item;
- current_item = new_item;
- }
-
- /* parse next value */
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (!parse_value(current_item, input_buffer))
- {
- goto fail; /* failed to parse value */
- }
- buffer_skip_whitespace(input_buffer);
- }
- while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
-
- if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
- {
- goto fail; /* expected end of array */
- }
-
-success:
- input_buffer->depth--;
-
- if (head != NULL) {
- head->prev = current_item;
- }
-
- item->type = cJSON_Array;
- item->child = head;
-
- input_buffer->offset++;
-
- return true;
-
-fail:
- if (head != NULL)
- {
- cJSON_Delete(head);
- }
-
- return false;
-}
-
-/* Render an array to text */
-static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output_pointer = NULL;
- size_t length = 0;
- cJSON *current_element = item->child;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* Compose the output array. */
- /* opening square bracket */
- output_pointer = ensure(output_buffer, 1);
- if (output_pointer == NULL)
- {
- return false;
- }
-
- *output_pointer = '[';
- output_buffer->offset++;
- output_buffer->depth++;
-
- while (current_element != NULL)
- {
- if (!print_value(current_element, output_buffer))
- {
- return false;
- }
- update_offset(output_buffer);
- if (current_element->next)
- {
- length = (size_t) (output_buffer->format ? 2 : 1);
- output_pointer = ensure(output_buffer, length + 1);
- if (output_pointer == NULL)
- {
- return false;
- }
- *output_pointer++ = ',';
- if(output_buffer->format)
- {
- *output_pointer++ = ' ';
- }
- *output_pointer = '\0';
- output_buffer->offset += length;
- }
- current_element = current_element->next;
- }
-
- output_pointer = ensure(output_buffer, 2);
- if (output_pointer == NULL)
- {
- return false;
- }
- *output_pointer++ = ']';
- *output_pointer = '\0';
- output_buffer->depth--;
-
- return true;
-}
-
-/* Build an object from the text. */
-static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
-{
- cJSON *head = NULL; /* linked list head */
- cJSON *current_item = NULL;
-
- if (input_buffer->depth >= CJSON_NESTING_LIMIT)
- {
- return false; /* to deeply nested */
- }
- input_buffer->depth++;
-
- if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
- {
- goto fail; /* not an object */
- }
-
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
- {
- goto success; /* empty object */
- }
-
- /* check if we skipped to the end of the buffer */
- if (cannot_access_at_index(input_buffer, 0))
- {
- input_buffer->offset--;
- goto fail;
- }
-
- /* step back to character in front of the first element */
- input_buffer->offset--;
- /* loop through the comma separated array elements */
- do
- {
- /* allocate next item */
- cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
- if (new_item == NULL)
- {
- goto fail; /* allocation failure */
- }
-
- /* attach next item to list */
- if (head == NULL)
- {
- /* start the linked list */
- current_item = head = new_item;
- }
- else
- {
- /* add to the end and advance */
- current_item->next = new_item;
- new_item->prev = current_item;
- current_item = new_item;
- }
-
- if (cannot_access_at_index(input_buffer, 1))
- {
- goto fail; /* nothing comes after the comma */
- }
-
- /* parse the name of the child */
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (!parse_string(current_item, input_buffer))
- {
- goto fail; /* failed to parse name */
- }
- buffer_skip_whitespace(input_buffer);
-
- /* swap valuestring and string, because we parsed the name */
- current_item->string = current_item->valuestring;
- current_item->valuestring = NULL;
-
- if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
- {
- goto fail; /* invalid object */
- }
-
- /* parse the value */
- input_buffer->offset++;
- buffer_skip_whitespace(input_buffer);
- if (!parse_value(current_item, input_buffer))
- {
- goto fail; /* failed to parse value */
- }
- buffer_skip_whitespace(input_buffer);
- }
- while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
-
- if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
- {
- goto fail; /* expected end of object */
- }
-
-success:
- input_buffer->depth--;
-
- if (head != NULL) {
- head->prev = current_item;
- }
-
- item->type = cJSON_Object;
- item->child = head;
-
- input_buffer->offset++;
- return true;
-
-fail:
- if (head != NULL)
- {
- cJSON_Delete(head);
- }
-
- return false;
-}
-
-/* Render an object to text. */
-static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
-{
- unsigned char *output_pointer = NULL;
- size_t length = 0;
- cJSON *current_item = item->child;
-
- if (output_buffer == NULL)
- {
- return false;
- }
-
- /* Compose the output: */
- length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
- output_pointer = ensure(output_buffer, length + 1);
- if (output_pointer == NULL)
- {
- return false;
- }
-
- *output_pointer++ = '{';
- output_buffer->depth++;
- if (output_buffer->format)
- {
- *output_pointer++ = '\n';
- }
- output_buffer->offset += length;
-
- while (current_item)
- {
- if (output_buffer->format)
- {
- size_t i;
- output_pointer = ensure(output_buffer, output_buffer->depth);
- if (output_pointer == NULL)
- {
- return false;
- }
- for (i = 0; i < output_buffer->depth; i++)
- {
- *output_pointer++ = '\t';
- }
- output_buffer->offset += output_buffer->depth;
- }
-
- /* print key */
- if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
- {
- return false;
- }
- update_offset(output_buffer);
-
- length = (size_t) (output_buffer->format ? 2 : 1);
- output_pointer = ensure(output_buffer, length);
- if (output_pointer == NULL)
- {
- return false;
- }
- *output_pointer++ = ':';
- if (output_buffer->format)
- {
- *output_pointer++ = '\t';
- }
- output_buffer->offset += length;
-
- /* print value */
- if (!print_value(current_item, output_buffer))
- {
- return false;
- }
- update_offset(output_buffer);
-
- /* print comma if not last */
- length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
- output_pointer = ensure(output_buffer, length + 1);
- if (output_pointer == NULL)
- {
- return false;
- }
- if (current_item->next)
- {
- *output_pointer++ = ',';
- }
-
- if (output_buffer->format)
- {
- *output_pointer++ = '\n';
- }
- *output_pointer = '\0';
- output_buffer->offset += length;
-
- current_item = current_item->next;
- }
-
- output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
- if (output_pointer == NULL)
- {
- return false;
- }
- if (output_buffer->format)
- {
- size_t i;
- for (i = 0; i < (output_buffer->depth - 1); i++)
- {
- *output_pointer++ = '\t';
- }
- }
- *output_pointer++ = '}';
- *output_pointer = '\0';
- output_buffer->depth--;
-
- return true;
-}
-
-/* Get Array size/item / object item. */
-CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
-{
- cJSON *child = NULL;
- size_t size = 0;
-
- if (array == NULL)
- {
- return 0;
- }
-
- child = array->child;
-
- while(child != NULL)
- {
- size++;
- child = child->next;
- }
-
- /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
-
- return (int)size;
-}
-
-static cJSON* get_array_item(const cJSON *array, size_t index)
-{
- cJSON *current_child = NULL;
-
- if (array == NULL)
- {
- return NULL;
- }
-
- current_child = array->child;
- while ((current_child != NULL) && (index > 0))
- {
- index--;
- current_child = current_child->next;
- }
-
- return current_child;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
-{
- if (index < 0)
- {
- return NULL;
- }
-
- return get_array_item(array, (size_t)index);
-}
-
-static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
-{
- cJSON *current_element = NULL;
-
- if ((object == NULL) || (name == NULL))
- {
- return NULL;
- }
-
- current_element = object->child;
- if (case_sensitive)
- {
- while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
- {
- current_element = current_element->next;
- }
- }
- else
- {
- while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
- {
- current_element = current_element->next;
- }
- }
-
- if ((current_element == NULL) || (current_element->string == NULL)) {
- return NULL;
- }
-
- return current_element;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
-{
- return get_object_item(object, string, false);
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
-{
- return get_object_item(object, string, true);
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
-{
- return cJSON_GetObjectItem(object, string) ? 1 : 0;
-}
-
-/* Utility for array list handling. */
-static void suffix_object(cJSON *prev, cJSON *item)
-{
- prev->next = item;
- item->prev = prev;
-}
-
-/* Utility for handling references. */
-static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
-{
- cJSON *reference = NULL;
- if (item == NULL)
- {
- return NULL;
- }
-
- reference = cJSON_New_Item(hooks);
- if (reference == NULL)
- {
- return NULL;
- }
-
- memcpy(reference, item, sizeof(cJSON));
- reference->string = NULL;
- reference->type |= cJSON_IsReference;
- reference->next = reference->prev = NULL;
- return reference;
-}
-
-static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
-{
- cJSON *child = NULL;
-
- if ((item == NULL) || (array == NULL) || (array == item))
- {
- return false;
- }
-
- child = array->child;
- /*
- * To find the last item in array quickly, we use prev in array
- */
- if (child == NULL)
- {
- /* list is empty, start new one */
- array->child = item;
- item->prev = item;
- item->next = NULL;
- }
- else
- {
- /* append to the end */
- if (child->prev)
- {
- suffix_object(child->prev, item);
- array->child->prev = item;
- }
- }
-
- return true;
-}
-
-/* Add item to array/object. */
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
-{
- return add_item_to_array(array, item);
-}
-
-#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
- #pragma GCC diagnostic push
-#endif
-#ifdef __GNUC__
-#pragma GCC diagnostic ignored "-Wcast-qual"
-#endif
-/* helper function to cast away const */
-static void* cast_away_const(const void* string)
-{
- return (void*)string;
-}
-#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
- #pragma GCC diagnostic pop
-#endif
-
-
-static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
-{
- char *new_key = NULL;
- int new_type = cJSON_Invalid;
-
- if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
- {
- return false;
- }
-
- if (constant_key)
- {
- new_key = (char*)cast_away_const(string);
- new_type = item->type | cJSON_StringIsConst;
- }
- else
- {
- new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
- if (new_key == NULL)
- {
- return false;
- }
-
- new_type = item->type & ~cJSON_StringIsConst;
- }
-
- if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
- {
- hooks->deallocate(item->string);
- }
-
- item->string = new_key;
- item->type = new_type;
-
- return add_item_to_array(object, item);
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
-{
- return add_item_to_object(object, string, item, &global_hooks, false);
-}
-
-/* Add an item to an object with constant string as key */
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
-{
- return add_item_to_object(object, string, item, &global_hooks, true);
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
-{
- if (array == NULL)
- {
- return false;
- }
-
- return add_item_to_array(array, create_reference(item, &global_hooks));
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
-{
- if ((object == NULL) || (string == NULL))
- {
- return false;
- }
-
- return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
-{
- cJSON *null = cJSON_CreateNull();
- if (add_item_to_object(object, name, null, &global_hooks, false))
- {
- return null;
- }
-
- cJSON_Delete(null);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
-{
- cJSON *true_item = cJSON_CreateTrue();
- if (add_item_to_object(object, name, true_item, &global_hooks, false))
- {
- return true_item;
- }
-
- cJSON_Delete(true_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
-{
- cJSON *false_item = cJSON_CreateFalse();
- if (add_item_to_object(object, name, false_item, &global_hooks, false))
- {
- return false_item;
- }
-
- cJSON_Delete(false_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
-{
- cJSON *bool_item = cJSON_CreateBool(boolean);
- if (add_item_to_object(object, name, bool_item, &global_hooks, false))
- {
- return bool_item;
- }
-
- cJSON_Delete(bool_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
-{
- cJSON *number_item = cJSON_CreateNumber(number);
- if (add_item_to_object(object, name, number_item, &global_hooks, false))
- {
- return number_item;
- }
-
- cJSON_Delete(number_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
-{
- cJSON *string_item = cJSON_CreateString(string);
- if (add_item_to_object(object, name, string_item, &global_hooks, false))
- {
- return string_item;
- }
-
- cJSON_Delete(string_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
-{
- cJSON *raw_item = cJSON_CreateRaw(raw);
- if (add_item_to_object(object, name, raw_item, &global_hooks, false))
- {
- return raw_item;
- }
-
- cJSON_Delete(raw_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
-{
- cJSON *object_item = cJSON_CreateObject();
- if (add_item_to_object(object, name, object_item, &global_hooks, false))
- {
- return object_item;
- }
-
- cJSON_Delete(object_item);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
-{
- cJSON *array = cJSON_CreateArray();
- if (add_item_to_object(object, name, array, &global_hooks, false))
- {
- return array;
- }
-
- cJSON_Delete(array);
- return NULL;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
-{
- if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL))
- {
- return NULL;
- }
-
- if (item != parent->child)
- {
- /* not the first element */
- item->prev->next = item->next;
- }
- if (item->next != NULL)
- {
- /* not the last element */
- item->next->prev = item->prev;
- }
-
- if (item == parent->child)
- {
- /* first element */
- parent->child = item->next;
- }
- else if (item->next == NULL)
- {
- /* last element */
- parent->child->prev = item->prev;
- }
-
- /* make sure the detached item doesn't point anywhere anymore */
- item->prev = NULL;
- item->next = NULL;
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
-{
- if (which < 0)
- {
- return NULL;
- }
-
- return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
-}
-
-CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
-{
- cJSON_Delete(cJSON_DetachItemFromArray(array, which));
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
-{
- cJSON *to_detach = cJSON_GetObjectItem(object, string);
-
- return cJSON_DetachItemViaPointer(object, to_detach);
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
-{
- cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
-
- return cJSON_DetachItemViaPointer(object, to_detach);
-}
-
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
-{
- cJSON_Delete(cJSON_DetachItemFromObject(object, string));
-}
-
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
-{
- cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
-}
-
-/* Replace array/object items with new ones. */
-CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
-{
- cJSON *after_inserted = NULL;
-
- if (which < 0 || newitem == NULL)
- {
- return false;
- }
-
- after_inserted = get_array_item(array, (size_t)which);
- if (after_inserted == NULL)
- {
- return add_item_to_array(array, newitem);
- }
-
- if (after_inserted != array->child && after_inserted->prev == NULL) {
- /* return false if after_inserted is a corrupted array item */
- return false;
- }
-
- newitem->next = after_inserted;
- newitem->prev = after_inserted->prev;
- after_inserted->prev = newitem;
- if (after_inserted == array->child)
- {
- array->child = newitem;
- }
- else
- {
- newitem->prev->next = newitem;
- }
- return true;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
-{
- if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL))
- {
- return false;
- }
-
- if (replacement == item)
- {
- return true;
- }
-
- replacement->next = item->next;
- replacement->prev = item->prev;
-
- if (replacement->next != NULL)
- {
- replacement->next->prev = replacement;
- }
- if (parent->child == item)
- {
- if (parent->child->prev == parent->child)
- {
- replacement->prev = replacement;
- }
- parent->child = replacement;
- }
- else
- { /*
- * To find the last item in array quickly, we use prev in array.
- * We can't modify the last item's next pointer where this item was the parent's child
- */
- if (replacement->prev != NULL)
- {
- replacement->prev->next = replacement;
- }
- if (replacement->next == NULL)
- {
- parent->child->prev = replacement;
- }
- }
-
- item->next = NULL;
- item->prev = NULL;
- cJSON_Delete(item);
-
- return true;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
-{
- if (which < 0)
- {
- return false;
- }
-
- return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
-}
-
-static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
-{
- if ((replacement == NULL) || (string == NULL))
- {
- return false;
- }
-
- /* replace the name in the replacement */
- if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
- {
- cJSON_free(replacement->string);
- }
- replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
- if (replacement->string == NULL)
- {
- return false;
- }
-
- replacement->type &= ~cJSON_StringIsConst;
-
- return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
-{
- return replace_item_in_object(object, string, newitem, false);
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
-{
- return replace_item_in_object(object, string, newitem, true);
-}
-
-/* Create basic types: */
-CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_NULL;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_True;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_False;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = boolean ? cJSON_True : cJSON_False;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_Number;
- item->valuedouble = num;
-
- /* use saturation in case of overflow */
- if (num >= INT_MAX)
- {
- item->valueint = INT_MAX;
- }
- else if (num <= (double)INT_MIN)
- {
- item->valueint = INT_MIN;
- }
- else
- {
- item->valueint = (int)num;
- }
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_String;
- item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
- if(!item->valuestring)
- {
- cJSON_Delete(item);
- return NULL;
- }
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if (item != NULL)
- {
- item->type = cJSON_String | cJSON_IsReference;
- item->valuestring = (char*)cast_away_const(string);
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if (item != NULL) {
- item->type = cJSON_Object | cJSON_IsReference;
- item->child = (cJSON*)cast_away_const(child);
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
- cJSON *item = cJSON_New_Item(&global_hooks);
- if (item != NULL) {
- item->type = cJSON_Array | cJSON_IsReference;
- item->child = (cJSON*)cast_away_const(child);
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type = cJSON_Raw;
- item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
- if(!item->valuestring)
- {
- cJSON_Delete(item);
- return NULL;
- }
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if(item)
- {
- item->type=cJSON_Array;
- }
-
- return item;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
-{
- cJSON *item = cJSON_New_Item(&global_hooks);
- if (item)
- {
- item->type = cJSON_Object;
- }
-
- return item;
-}
-
-/* Create Arrays: */
-CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (numbers == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for(i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateNumber(numbers[i]);
- if (!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p, n);
- }
- p = n;
- }
-
- if (a && a->child) {
- a->child->prev = n;
- }
-
- return a;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (numbers == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for(i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateNumber((double)numbers[i]);
- if(!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p, n);
- }
- p = n;
- }
-
- if (a && a->child) {
- a->child->prev = n;
- }
-
- return a;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (numbers == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for(i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateNumber(numbers[i]);
- if(!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p, n);
- }
- p = n;
- }
-
- if (a && a->child) {
- a->child->prev = n;
- }
-
- return a;
-}
-
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
-{
- size_t i = 0;
- cJSON *n = NULL;
- cJSON *p = NULL;
- cJSON *a = NULL;
-
- if ((count < 0) || (strings == NULL))
- {
- return NULL;
- }
-
- a = cJSON_CreateArray();
-
- for (i = 0; a && (i < (size_t)count); i++)
- {
- n = cJSON_CreateString(strings[i]);
- if(!n)
- {
- cJSON_Delete(a);
- return NULL;
- }
- if(!i)
- {
- a->child = n;
- }
- else
- {
- suffix_object(p,n);
- }
- p = n;
- }
-
- if (a && a->child) {
- a->child->prev = n;
- }
-
- return a;
-}
-
-/* Duplication */
-cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse);
-
-CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
-{
- return cJSON_Duplicate_rec(item, 0, recurse );
-}
-
-cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse)
-{
- cJSON *newitem = NULL;
- cJSON *child = NULL;
- cJSON *next = NULL;
- cJSON *newchild = NULL;
-
- /* Bail on bad ptr */
- if (!item)
- {
- goto fail;
- }
- /* Create new item */
- newitem = cJSON_New_Item(&global_hooks);
- if (!newitem)
- {
- goto fail;
- }
- /* Copy over all vars */
- newitem->type = item->type & (~cJSON_IsReference);
- newitem->valueint = item->valueint;
- newitem->valuedouble = item->valuedouble;
- if (item->valuestring)
- {
- newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
- if (!newitem->valuestring)
- {
- goto fail;
- }
- }
- if (item->string)
- {
- newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
- if (!newitem->string)
- {
- goto fail;
- }
- }
- /* If non-recursive, then we're done! */
- if (!recurse)
- {
- return newitem;
- }
- /* Walk the ->next chain for the child. */
- child = item->child;
- while (child != NULL)
- {
- if(depth >= CJSON_CIRCULAR_LIMIT) {
- goto fail;
- }
- newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */
- if (!newchild)
- {
- goto fail;
- }
- if (next != NULL)
- {
- /* If newitem->child already set, then crosswire ->prev and ->next and move on */
- next->next = newchild;
- newchild->prev = next;
- next = newchild;
- }
- else
- {
- /* Set newitem->child and move to it */
- newitem->child = newchild;
- next = newchild;
- }
- child = child->next;
- }
- if (newitem && newitem->child)
- {
- newitem->child->prev = newchild;
- }
-
- return newitem;
-
-fail:
- if (newitem != NULL)
- {
- cJSON_Delete(newitem);
- }
-
- return NULL;
-}
-
-static void skip_oneline_comment(char **input)
-{
- *input += static_strlen("//");
-
- for (; (*input)[0] != '\0'; ++(*input))
- {
- if ((*input)[0] == '\n') {
- *input += static_strlen("\n");
- return;
- }
- }
-}
-
-static void skip_multiline_comment(char **input)
-{
- *input += static_strlen("/*");
-
- for (; (*input)[0] != '\0'; ++(*input))
- {
- if (((*input)[0] == '*') && ((*input)[1] == '/'))
- {
- *input += static_strlen("*/");
- return;
- }
- }
-}
-
-static void minify_string(char **input, char **output) {
- (*output)[0] = (*input)[0];
- *input += static_strlen("\"");
- *output += static_strlen("\"");
-
-
- for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
- (*output)[0] = (*input)[0];
-
- if ((*input)[0] == '\"') {
- (*output)[0] = '\"';
- *input += static_strlen("\"");
- *output += static_strlen("\"");
- return;
- } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
- (*output)[1] = (*input)[1];
- *input += static_strlen("\"");
- *output += static_strlen("\"");
- }
- }
-}
-
-CJSON_PUBLIC(void) cJSON_Minify(char *json)
-{
- char *into = json;
-
- if (json == NULL)
- {
- return;
- }
-
- while (json[0] != '\0')
- {
- switch (json[0])
- {
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- json++;
- break;
-
- case '/':
- if (json[1] == '/')
- {
- skip_oneline_comment(&json);
- }
- else if (json[1] == '*')
- {
- skip_multiline_comment(&json);
- } else {
- json++;
- }
- break;
-
- case '\"':
- minify_string(&json, (char**)&into);
- break;
-
- default:
- into[0] = json[0];
- json++;
- into++;
- }
- }
-
- /* and null-terminate. */
- *into = '\0';
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Invalid;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_False;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xff) == cJSON_True;
-}
-
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & (cJSON_True | cJSON_False)) != 0;
-}
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_NULL;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Number;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_String;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Array;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Object;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
-{
- if (item == NULL)
- {
- return false;
- }
-
- return (item->type & 0xFF) == cJSON_Raw;
-}
-
-CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
-{
- if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
- {
- return false;
- }
-
- /* check if type is valid */
- switch (a->type & 0xFF)
- {
- case cJSON_False:
- case cJSON_True:
- case cJSON_NULL:
- case cJSON_Number:
- case cJSON_String:
- case cJSON_Raw:
- case cJSON_Array:
- case cJSON_Object:
- break;
-
- default:
- return false;
- }
-
- /* identical objects are equal */
- if (a == b)
- {
- return true;
- }
-
- switch (a->type & 0xFF)
- {
- /* in these cases and equal type is enough */
- case cJSON_False:
- case cJSON_True:
- case cJSON_NULL:
- return true;
-
- case cJSON_Number:
- if (compare_double(a->valuedouble, b->valuedouble))
- {
- return true;
- }
- return false;
-
- case cJSON_String:
- case cJSON_Raw:
- if ((a->valuestring == NULL) || (b->valuestring == NULL))
- {
- return false;
- }
- if (strcmp(a->valuestring, b->valuestring) == 0)
- {
- return true;
- }
-
- return false;
-
- case cJSON_Array:
- {
- cJSON *a_element = a->child;
- cJSON *b_element = b->child;
-
- for (; (a_element != NULL) && (b_element != NULL);)
- {
- if (!cJSON_Compare(a_element, b_element, case_sensitive))
- {
- return false;
- }
-
- a_element = a_element->next;
- b_element = b_element->next;
- }
-
- /* one of the arrays is longer than the other */
- if (a_element != b_element) {
- return false;
- }
-
- return true;
- }
-
- case cJSON_Object:
- {
- cJSON *a_element = NULL;
- cJSON *b_element = NULL;
- cJSON_ArrayForEach(a_element, a)
- {
- /* TODO This has O(n^2) runtime, which is horrible! */
- b_element = get_object_item(b, a_element->string, case_sensitive);
- if (b_element == NULL)
- {
- return false;
- }
-
- if (!cJSON_Compare(a_element, b_element, case_sensitive))
- {
- return false;
- }
- }
-
- /* doing this twice, once on a and b to prevent true comparison if a subset of b
- * TODO: Do this the proper way, this is just a fix for now */
- cJSON_ArrayForEach(b_element, b)
- {
- a_element = get_object_item(a, b_element->string, case_sensitive);
- if (a_element == NULL)
- {
- return false;
- }
-
- if (!cJSON_Compare(b_element, a_element, case_sensitive))
- {
- return false;
- }
- }
-
- return true;
- }
-
- default:
- return false;
- }
-}
-
-CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
-{
- return global_hooks.allocate(size);
-}
-
-CJSON_PUBLIC(void) cJSON_free(void *object)
-{
- global_hooks.deallocate(object);
- object = NULL;
-}
diff --git a/common/cJSON/cJSON.h b/common/cJSON/cJSON.h
deleted file mode 100644
index 37520bb..0000000
--- a/common/cJSON/cJSON.h
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
-
- 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 cJSON__h
-#define cJSON__h
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
-#define __WINDOWS__
-#endif
-
-#ifdef __WINDOWS__
-
-/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
-
-CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
-CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
-CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
-
-For *nix builds that support visibility attribute, you can define similar behavior by
-
-setting default visibility to hidden by adding
--fvisibility=hidden (for gcc)
-or
--xldscope=hidden (for sun cc)
-to CFLAGS
-
-then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
-
-*/
-
-#define CJSON_CDECL __cdecl
-#define CJSON_STDCALL __stdcall
-
-/* export symbols by default, this is necessary for copy pasting the C and header file */
-#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
-#define CJSON_EXPORT_SYMBOLS
-#endif
-
-#if defined(CJSON_HIDE_SYMBOLS)
-#define CJSON_PUBLIC(type) type CJSON_STDCALL
-#elif defined(CJSON_EXPORT_SYMBOLS)
-#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
-#elif defined(CJSON_IMPORT_SYMBOLS)
-#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
-#endif
-#else /* !__WINDOWS__ */
-#define CJSON_CDECL
-#define CJSON_STDCALL
-
-#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
-#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
-#else
-#define CJSON_PUBLIC(type) type
-#endif
-#endif
-
-/* project version */
-#define CJSON_VERSION_MAJOR 1
-#define CJSON_VERSION_MINOR 7
-#define CJSON_VERSION_PATCH 18
-
-#include
-
-/* cJSON Types: */
-#define cJSON_Invalid (0)
-#define cJSON_False (1 << 0)
-#define cJSON_True (1 << 1)
-#define cJSON_NULL (1 << 2)
-#define cJSON_Number (1 << 3)
-#define cJSON_String (1 << 4)
-#define cJSON_Array (1 << 5)
-#define cJSON_Object (1 << 6)
-#define cJSON_Raw (1 << 7) /* raw json */
-
-#define cJSON_IsReference 256
-#define cJSON_StringIsConst 512
-
-/* The cJSON structure: */
-typedef struct cJSON
-{
- /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
- struct cJSON *next;
- struct cJSON *prev;
- /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
- struct cJSON *child;
-
- /* The type of the item, as above. */
- int type;
-
- /* The item's string, if type==cJSON_String and type == cJSON_Raw */
- char *valuestring;
- /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
- int valueint;
- /* The item's number, if type==cJSON_Number */
- double valuedouble;
-
- /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
- char *string;
-} cJSON;
-
-typedef struct cJSON_Hooks
-{
- /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
- void *(CJSON_CDECL *malloc_fn)(size_t sz);
- void (CJSON_CDECL *free_fn)(void *ptr);
-} cJSON_Hooks;
-
-typedef int cJSON_bool;
-
-/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
- * This is to prevent stack overflows. */
-#ifndef CJSON_NESTING_LIMIT
-#define CJSON_NESTING_LIMIT 1000
-#endif
-
-/* Limits the length of circular references can be before cJSON rejects to parse them.
- * This is to prevent stack overflows. */
-#ifndef CJSON_CIRCULAR_LIMIT
-#define CJSON_CIRCULAR_LIMIT 10000
-#endif
-
-/* returns the version of cJSON as a string */
-CJSON_PUBLIC(const char*) cJSON_Version(void);
-
-/* Supply malloc, realloc and free functions to cJSON */
-CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
-
-/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
-/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
-CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
-/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
-/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
-CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
-
-/* Render a cJSON entity to text for transfer/storage. */
-CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
-/* Render a cJSON entity to text for transfer/storage without any formatting. */
-CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
-/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
-CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
-/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
-/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
-CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
-/* Delete a cJSON entity and all subentities. */
-CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
-
-/* Returns the number of items in an array (or object). */
-CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
-/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
-CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
-/* Get item "string" from object. Case insensitive. */
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
-CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
-CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
-/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
-CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
-
-/* Check item type and return its value */
-CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
-CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
-
-/* These functions check the type of an item */
-CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
-CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
-
-/* These calls create a cJSON item of the appropriate type. */
-CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
-CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
-CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
-/* raw json */
-CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
-CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
-CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
-
-/* Create a string where valuestring references a string so
- * it will not be freed by cJSON_Delete */
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
-/* Create an object/array that only references it's elements so
- * they will not be freed by cJSON_Delete */
-CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
-CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
-
-/* These utilities create an Array of count items.
- * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
-CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
-CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
-CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
-CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
-
-/* Append item to the specified array/object. */
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
-/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
- * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
- * writing to `item->string` */
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
-/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
-CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
-
-/* Remove/Detach items from Arrays/Objects. */
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
-CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
-CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
-
-/* Update array items. */
-CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
-CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
-
-/* Duplicate a cJSON item */
-CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
-/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
- * need to be released. With recurse!=0, it will duplicate any children connected to the item.
- * The item->next and ->prev pointers are always zero on return from Duplicate. */
-/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
- * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
-CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
-
-/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
- * The input pointer json cannot point to a read-only address area, such as a string constant,
- * but should point to a readable and writable address area. */
-CJSON_PUBLIC(void) cJSON_Minify(char *json);
-
-/* Helper functions for creating and adding items to an object at the same time.
- * They return the added item or NULL on failure. */
-CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
-CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
-CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
-CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
-CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
-CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
-CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
-CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
-CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
-
-/* When assigning an integer value, it needs to be propagated to valuedouble too. */
-#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
-/* helper for the cJSON_SetNumberValue macro */
-CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
-#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
-/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
-CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
-
-/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
-#define cJSON_SetBoolValue(object, boolValue) ( \
- (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
- (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
- cJSON_Invalid\
-)
-
-/* Macro for iterating over an array or object */
-#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
-
-/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
-CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
-CJSON_PUBLIC(void) cJSON_free(void *object);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/common/lwjson/lwjson.c b/common/lwjson/lwjson.c
new file mode 100644
index 0000000..94a7ea7
--- /dev/null
+++ b/common/lwjson/lwjson.c
@@ -0,0 +1,752 @@
+/**
+ * \file lwjson.c
+ * \brief Lightweight JSON format parser
+ */
+
+/*
+ * Copyright (c) 2024 Tilen MAJERLE
+ *
+ * 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.
+ *
+ * This file is part of LwJSON - Lightweight JSON format parser.
+ *
+ * Author: Tilen MAJERLE
+ * Version: v1.7.0
+ */
+#include
+#include "lwjson.h"
+
+/**
+ * \brief Internal string object
+ */
+typedef struct {
+ const char* start; /*!< Original pointer to beginning of JSON object */
+ size_t len; /*!< Total length of input json string */
+ const char* p; /*!< Current char pointer */
+} lwjson_int_str_t;
+
+/**
+ * \brief Allocate new token for JSON block
+ * \param[in] lwobj: LwJSON instance
+ * \return Pointer to new token
+ */
+static lwjson_token_t*
+prv_alloc_token(lwjson_t* lwobj) {
+ if (lwobj->next_free_token_pos < lwobj->tokens_len) {
+ LWJSON_MEMSET(&lwobj->tokens[lwobj->next_free_token_pos], 0x00, sizeof(*lwobj->tokens));
+ return &lwobj->tokens[lwobj->next_free_token_pos++];
+ }
+ return NULL;
+}
+
+/**
+ * \brief Skip all characters that are considered *blank* as per RFC4627
+ * \param[in,out] pobj: Pointer to text that is modified on success
+ * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
+ */
+static lwjsonr_t
+prv_skip_blank(lwjson_int_str_t* pobj) {
+ while (pobj->p != NULL && *pobj->p != '\0' && (size_t)(pobj->p - pobj->start) < pobj->len) {
+ if (*pobj->p == ' ' || *pobj->p == '\t' || *pobj->p == '\r' || *pobj->p == '\n' || *pobj->p == '\f') {
+ ++pobj->p;
+#if LWJSON_CFG_COMMENTS
+ /* Check for comments and remove them */
+ } else if (*pobj->p == '/') {
+ ++pobj->p;
+ if (pobj->p != NULL && *pobj->p == '*') {
+ ++pobj->p;
+ while (pobj->p != NULL && *pobj->p != '\0' && (size_t)(pobj->p - pobj->start) < pobj->len) {
+ if (*pobj->p == '*') {
+ ++pobj->p;
+ if (*pobj->p == '/') {
+ ++pobj->p;
+ break;
+ }
+ }
+ ++pobj->p;
+ }
+ }
+#endif /* LWJSON_CFG_COMMENTS */
+ } else {
+ break;
+ }
+ }
+ if (pobj->p != NULL && *pobj->p != '\0' && (size_t)(pobj->p - pobj->start) < pobj->len) {
+ return lwjsonOK;
+ }
+ return lwjsonERRJSON;
+}
+
+/**
+ * \brief Parse JSON string that must start end end with double quotes `"` character
+ * It just parses length of characters and does not perform any decode operation
+ * \param[in,out] pobj: Pointer to text that is modified on success
+ * \param[out] pout: Pointer to pointer to string that is set where string starts
+ * \param[out] poutlen: Length of string in units of characters is stored here
+ * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
+ */
+static lwjsonr_t
+prv_parse_string(lwjson_int_str_t* pobj, const char** pout, size_t* poutlen) {
+ lwjsonr_t res;
+ size_t len = 0;
+
+ res = prv_skip_blank(pobj);
+ if (res != lwjsonOK) {
+ return res;
+ }
+ if (*pobj->p++ != '"') {
+ return lwjsonERRJSON;
+ }
+ *pout = pobj->p;
+ /* Parse string but take care of escape characters */
+ for (;; ++pobj->p, ++len) {
+ if (pobj->p == NULL || *pobj->p == '\0' || (size_t)(pobj->p - pobj->start) >= pobj->len) {
+ return lwjsonERRJSON;
+ }
+ /* Check special characters */
+ if (*pobj->p == '\\') {
+ ++pobj->p;
+ ++len;
+ switch (*pobj->p) {
+ case '"': /* fallthrough */
+ case '\\': /* fallthrough */
+ case '/': /* fallthrough */
+ case 'b': /* fallthrough */
+ case 'f': /* fallthrough */
+ case 'n': /* fallthrough */
+ case 'r': /* fallthrough */
+ case 't': break;
+ case 'u':
+ ++pobj->p;
+ for (size_t i = 0; i < 4; ++i, ++len) {
+ if (!((*pobj->p >= '0' && *pobj->p <= '9') || (*pobj->p >= 'a' && *pobj->p <= 'f')
+ || (*pobj->p >= 'A' && *pobj->p <= 'F'))) {
+ return lwjsonERRJSON;
+ }
+ if (i < 3) {
+ ++pobj->p;
+ }
+ }
+ break;
+ default: return lwjsonERRJSON;
+ }
+ } else if (*pobj->p == '"') {
+ ++pobj->p;
+ break;
+ }
+ }
+ *poutlen = len;
+ return res;
+}
+
+/**
+ * \brief Parse property name that must comply with JSON string format as in RFC4627
+ * Property string must be followed by colon character ":"
+ * \param[in,out] pobj: Pointer to text that is modified on success
+ * \param[out] t: Token instance to write property name to
+ * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
+ */
+static lwjsonr_t
+prv_parse_property_name(lwjson_int_str_t* pobj, lwjson_token_t* t) {
+ lwjsonr_t res;
+
+ /* Parse property string first */
+ res = prv_parse_string(pobj, &t->token_name, &t->token_name_len);
+ if (res != lwjsonOK) {
+ return res;
+ }
+ /* Skip any spaces */
+ res = prv_skip_blank(pobj);
+ if (res != lwjsonOK) {
+ return res;
+ }
+ /* Must continue with colon */
+ if (*pobj->p++ != ':') {
+ return lwjsonERRJSON;
+ }
+ /* Skip any spaces */
+ res = prv_skip_blank(pobj);
+ if (res != lwjsonOK) {
+ return res;
+ }
+ return lwjsonOK;
+}
+
+/**
+ * \brief Calculate new value for exponent 10^exponent.
+ * \param[in] exp: Exponent.
+ * \param[in] is_minus: Indicates whether the exponent is negative.
+ * \return Value of 10^exponent.
+ */
+static lwjson_real_t prv_power10(lwjson_int_t exp, uint8_t is_minus)
+{
+ lwjson_real_t result = (lwjson_real_t)1;
+ lwjson_real_t base = (lwjson_real_t)10;
+
+ for (;exp > 0; base *= base, exp >>= 1) {
+ if ((exp & 1) == 1) {
+ result *= base;
+ }
+ }
+
+ return is_minus ? ((lwjson_real_t)1 / result) : result;
+}
+
+/**
+ * \brief Parse number as described in RFC4627
+ * \param[in,out] pobj: Pointer to text that is modified on success
+ * \param[out] tout: Pointer to output number format
+ * \param[out] fout: Pointer to output real-type variable. Used if type is REAL.
+ * \param[out] iout: Pointer to output int-type variable. Used if type is INT.
+ * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
+ */
+static lwjsonr_t
+prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fout, lwjson_int_t* iout) {
+ lwjsonr_t res;
+ uint8_t is_minus;
+ lwjson_real_t real_num = 0;
+ lwjson_int_t int_num = 0;
+ lwjson_type_t type = LWJSON_TYPE_NUM_INT;
+
+ res = prv_skip_blank(pobj);
+ if (res != lwjsonOK) {
+ return res;
+ }
+ if (*pobj->p == '\0' || (size_t)(pobj->p - pobj->start) >= pobj->len) {
+ return lwjsonERRJSON;
+ }
+ is_minus = *pobj->p == '-' ? (++pobj->p, 1) : 0;
+ if (*pobj->p == '\0' /* Invalid string */
+ || *pobj->p < '0' || *pobj->p > '9' /* Character outside number range */
+ || (*pobj->p == '0'
+ && (pobj->p[1] < '0' && pobj->p[1] > '9'))) { /* Number starts with 0 but not followed by dot */
+ return lwjsonERRJSON;
+ }
+
+ /* Parse number */
+ for (int_num = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p) {
+ int_num = int_num * (lwjson_int_t)10 + (*pobj->p - '0');
+ }
+
+ real_num = (lwjson_real_t)int_num;
+
+ if (pobj->p != NULL && *pobj->p == '.') { /* Number has exponent */
+ lwjson_real_t exp;
+ lwjson_int_t dec_num;
+
+ real_num = (lwjson_real_t)int_num;
+
+ type = LWJSON_TYPE_NUM_REAL; /* Format is real */
+ ++pobj->p; /* Ignore comma character */
+ if (*pobj->p < '0' || *pobj->p > '9') { /* Must be followed by number characters */
+ return lwjsonERRJSON;
+ }
+
+ /* Get number after decimal point */
+ for (exp = (lwjson_real_t)1, dec_num = 0; *pobj->p >= '0' && *pobj->p <= '9';
+ ++pobj->p, exp *= (lwjson_real_t)10) {
+ dec_num = dec_num * (lwjson_int_t)10 + (lwjson_int_t)(*pobj->p - '0');
+ }
+
+ /* Add decimal part to number */
+ real_num += (lwjson_real_t)dec_num / exp;
+ }
+ if (pobj->p != NULL && (*pobj->p == 'e' || *pobj->p == 'E')) { /* Engineering mode */
+ uint8_t is_minus_exp;
+ lwjson_int_t exp_cnt;
+
+ type = LWJSON_TYPE_NUM_REAL; /* Format is real */
+ ++pobj->p; /* Ignore enginnering sing part */
+ is_minus_exp = *pobj->p == '-' ? (++pobj->p, 1) : 0; /* Check if negative */
+ if (*pobj->p == '+') { /* Optional '+' is possible too */
+ ++pobj->p;
+ }
+ if (*pobj->p < '0' || *pobj->p > '9') { /* Must be followed by number characters */
+ return lwjsonERRJSON;
+ }
+
+ /* Parse exponent number */
+ for (exp_cnt = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p) {
+ exp_cnt = exp_cnt * (lwjson_int_t)10 + (lwjson_int_t)(*pobj->p - '0');
+ }
+
+ /* Calculate new value for exponent 10^exponent */
+ real_num *= prv_power10(exp_cnt, is_minus_exp);
+ }
+
+ /* Write output values */
+ if (tout != NULL) {
+ *tout = type;
+ }
+ if (type == LWJSON_TYPE_NUM_INT) {
+ *iout = is_minus ? -int_num : int_num;
+ } else {
+ *fout = is_minus ? -real_num : real_num;
+ }
+ return lwjsonOK;
+}
+
+/**
+ * \brief Create path segment from input path for search operation
+ * \param[in,out] ppath: Pointer to pointer to input path. Pointer is modified
+ * \param[out] opath: Pointer to pointer to write path segment
+ * \param[out] olen: Pointer to variable to write length of segment
+ * \param[out] is_last: Pointer to write if this is last segment
+ * \return `1` on success, `0` otherwise
+ */
+static uint8_t
+prv_create_path_segment(const char** ppath, const char** opath, size_t* olen, uint8_t* is_last) {
+ const char* segment = *ppath;
+
+ *is_last = 0;
+ *opath = NULL;
+ *olen = 0;
+
+ /* Check input path */
+ if (segment == NULL || *segment == '\0') {
+ *is_last = 1;
+ return 0;
+ }
+
+ /*
+ * Path must be one of:
+ * - literal text
+ * - "#" followed by dot "."
+ */
+ if (*segment == '#') {
+ *opath = segment;
+ for (*olen = 0;; ++segment, ++(*olen)) {
+ if (*segment == '.') {
+ ++segment;
+ break;
+ } else if (*segment == '\0') {
+ if (*olen == 1) {
+ return 0;
+ } else {
+ break;
+ }
+ }
+ }
+ *ppath = segment;
+ } else {
+ *opath = segment;
+ for (*olen = 0; *segment != '\0' && *segment != '.'; ++(*olen), ++segment) {}
+ *ppath = segment + 1;
+ }
+ if (*segment == '\0') {
+ *is_last = 1;
+ }
+ return 1;
+}
+
+/**
+ * \brief Input recursive function for find operation
+ * \param[in] parent: Parent token of type \ref LWJSON_TYPE_ARRAY or LWJSON_TYPE_OBJECT
+ * \param[in] path: Path to search for starting this token further
+ * \return Found token on success, `NULL` otherwise
+ */
+static const lwjson_token_t*
+prv_find(const lwjson_token_t* parent, const char* path) {
+ const char* segment;
+ size_t segment_len;
+ uint8_t is_last, res;
+
+ /* Get path segments */
+ res = prv_create_path_segment(&path, &segment, &segment_len, &is_last);
+ if (res != 0) {
+ /* Check if detected an array request */
+ if (*segment == '#') {
+ /* Parent must be array */
+ if (parent->type != LWJSON_TYPE_ARRAY) {
+ return NULL;
+ }
+
+ /* Check if index requested */
+ if (segment_len > 1) {
+ const lwjson_token_t* tkn;
+ size_t index = 0;
+
+ /* Parse number */
+ for (size_t i = 1; i < segment_len; ++i) {
+ if (segment[i] < '0' || segment[i] > '9') {
+ return NULL;
+ } else {
+ index = index * 10 + (segment[i] - '0');
+ }
+ }
+
+ /* Start from beginning */
+ for (tkn = parent->u.first_child; tkn != NULL && index > 0; tkn = tkn->next, --index) {}
+ if (tkn != NULL) {
+ if (is_last) {
+ return tkn;
+ } else {
+ return prv_find(tkn, path);
+ }
+ }
+ return NULL;
+ }
+
+ /* Scan all indexes and get first match */
+ for (const lwjson_token_t* tkn = parent->u.first_child; tkn != NULL; tkn = tkn->next) {
+ const lwjson_token_t* tmp = prv_find(tkn, path);
+ if (tmp != NULL) {
+ return tmp;
+ }
+ }
+ } else {
+ if (parent->type != LWJSON_TYPE_OBJECT) {
+ return NULL;
+ }
+ for (const lwjson_token_t* tkn = parent->u.first_child; tkn != NULL; tkn = tkn->next) {
+ if (tkn->token_name_len == segment_len && !strncmp(tkn->token_name, segment, segment_len)) {
+ const lwjson_token_t* tmp;
+ if (is_last) {
+ return tkn;
+ }
+ tmp = prv_find(tkn, path);
+ if (tmp != NULL) {
+ return tmp;
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+ * \brief Check for character after opening bracket of array or object
+ * \param[in,out] pobj: JSON string
+ * \param[in] t: Token to check for type
+ * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
+ */
+static inline lwjsonr_t
+prv_check_valid_char_after_open_bracket(lwjson_int_str_t* pobj, lwjson_token_t* t) {
+ lwjsonr_t res;
+
+ /* Check next character after object open */
+ res = prv_skip_blank(pobj);
+ if (res != lwjsonOK) {
+ return res;
+ }
+ if (*pobj->p == '\0' || (t->type == LWJSON_TYPE_OBJECT && (*pobj->p != '"' && *pobj->p != '}'))
+ || (t->type == LWJSON_TYPE_ARRAY
+ && (*pobj->p != '"' && *pobj->p != ']' && *pobj->p != '[' && *pobj->p != '{' && *pobj->p != '-'
+ && (*pobj->p < '0' || *pobj->p > '9') && *pobj->p != 't' && *pobj->p != 'n' && *pobj->p != 'f'))) {
+ res = lwjsonERRJSON;
+ }
+ return res;
+}
+
+/**
+ * \brief Setup LwJSON instance for parsing JSON strings
+ * \param[in,out] lwobj: LwJSON instance
+ * \param[in] tokens: Pointer to array of tokens used for parsing
+ * \param[in] tokens_len: Number of tokens
+ * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
+ */
+lwjsonr_t
+lwjson_init(lwjson_t* lwobj, lwjson_token_t* tokens, size_t tokens_len) {
+ LWJSON_MEMSET(lwobj, 0x00, sizeof(*lwobj));
+ LWJSON_MEMSET(tokens, 0x00, sizeof(*tokens) * tokens_len);
+ lwobj->tokens = tokens;
+ lwobj->tokens_len = tokens_len;
+ lwobj->first_token.type = LWJSON_TYPE_OBJECT;
+ return lwjsonOK;
+}
+
+/**
+ * \brief Parse JSON data with length parameter
+ * JSON format must be complete and must comply with RFC4627
+ * \param[in,out] lwobj: LwJSON instance
+ * \param[in] json_data: JSON string to parse
+ * \param[in] json膶len: JSON data length
+ * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
+ */
+lwjsonr_t
+lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t json_len) {
+ lwjsonr_t res = lwjsonOK;
+ lwjson_token_t *t, *to;
+ lwjson_int_str_t pobj = {.start = json_data, .len = json_len, .p = json_data};
+
+ /* Check input parameters */
+ if (lwobj == NULL || json_data == NULL || json_len == 0) {
+ res = lwjsonERRPAR;
+ goto ret;
+ }
+
+ /* set first token */
+ to = &lwobj->first_token;
+
+ /* values from very beginning */
+ lwobj->flags.parsed = 0;
+ lwobj->next_free_token_pos = 0;
+ LWJSON_MEMSET(to, 0x00, sizeof(*to));
+
+ /* First parse */
+ res = prv_skip_blank(&pobj);
+ if (res != lwjsonOK) {
+ goto ret;
+ }
+ if (*pobj.p == '{') {
+ to->type = LWJSON_TYPE_OBJECT;
+ } else if (*pobj.p == '[') {
+ to->type = LWJSON_TYPE_ARRAY;
+ } else {
+ res = lwjsonERRJSON;
+ goto ret;
+ }
+ ++pobj.p;
+ res = prv_check_valid_char_after_open_bracket(&pobj, to);
+ if (res != lwjsonOK) {
+ goto ret;
+ }
+
+ /* Process all characters as indicated by input user */
+ while (pobj.p != NULL && *pobj.p != '\0' && (size_t)(pobj.p - pobj.start) < pobj.len) {
+ /* Filter out blanks */
+ res = prv_skip_blank(&pobj);
+ if (res != lwjsonOK) {
+ goto ret;
+ }
+ if (*pobj.p == ',') {
+ ++pobj.p;
+ continue;
+ }
+
+ /* Check if end of object or array*/
+ if (*pobj.p == (to->type == LWJSON_TYPE_OBJECT ? '}' : ']')) {
+ lwjson_token_t* parent = to->next;
+ to->next = NULL;
+ ++pobj.p;
+
+ /* End of string if to == NULL (no parent), check if properly terminated */
+ to = parent;
+ if (to == NULL) {
+ prv_skip_blank(&pobj);
+ res = (pobj.p == NULL || *pobj.p == '\0' || (size_t)(pobj.p - pobj.start) == pobj.len) ? lwjsonOK
+ : lwjsonERR;
+ goto ret;
+ }
+ continue;
+ }
+
+ /* Allocate new token */
+ t = prv_alloc_token(lwobj);
+ if (t == NULL) {
+ res = lwjsonERRMEM;
+ goto ret;
+ }
+
+ /* If object type is not array, first thing is property that starts with quotes */
+ if (to->type != LWJSON_TYPE_ARRAY) {
+ if (*pobj.p != '"') {
+ res = lwjsonERRJSON;
+ goto ret;
+ }
+ res = prv_parse_property_name(&pobj, t);
+ if (res != lwjsonOK) {
+ goto ret;
+ }
+ }
+
+ /* Add element to linked list */
+ if (to->u.first_child == NULL) {
+ to->u.first_child = t;
+ } else {
+ lwjson_token_t* c;
+ for (c = to->u.first_child; c->next != NULL; c = c->next) {}
+ c->next = t;
+ }
+
+ /* Check next character to process */
+ switch (*pobj.p) {
+ case '{':
+ case '[':
+ t->type = *pobj.p == '{' ? LWJSON_TYPE_OBJECT : LWJSON_TYPE_ARRAY;
+ ++pobj.p;
+
+ res = prv_check_valid_char_after_open_bracket(&pobj, t);
+ if (res != lwjsonOK) {
+ goto ret;
+ }
+ t->next = to; /* Temporary saved as parent object */
+ to = t;
+ break;
+ case '"':
+ res = prv_parse_string(&pobj, &t->u.str.token_value, &t->u.str.token_value_len);
+ if (res == lwjsonOK) {
+ t->type = LWJSON_TYPE_STRING;
+ } else {
+ goto ret;
+ }
+ break;
+ case 't':
+ /* RFC4627 is lower-case only */
+ if (strncmp(pobj.p, "true", 4) == 0) {
+ t->type = LWJSON_TYPE_TRUE;
+ pobj.p += 4;
+ } else {
+ res = lwjsonERRJSON;
+ goto ret;
+ }
+ break;
+ case 'f':
+ /* RFC4627 is lower-case only */
+ if (strncmp(pobj.p, "false", 5) == 0) {
+ t->type = LWJSON_TYPE_FALSE;
+ pobj.p += 5;
+ } else {
+ res = lwjsonERRJSON;
+ goto ret;
+ }
+ break;
+ case 'n':
+ /* RFC4627 is lower-case only */
+ if (strncmp(pobj.p, "null", 4) == 0) {
+ t->type = LWJSON_TYPE_NULL;
+ pobj.p += 4;
+ } else {
+ res = lwjsonERRJSON;
+ goto ret;
+ }
+ break;
+ default:
+ if (*pobj.p == '-' || (*pobj.p >= '0' && *pobj.p <= '9')) {
+ if (prv_parse_number(&pobj, &t->type, &t->u.num_real, &t->u.num_int) != lwjsonOK) {
+ res = lwjsonERRJSON;
+ goto ret;
+ }
+ } else {
+ res = lwjsonERRJSON;
+ goto ret;
+ }
+ break;
+ }
+
+ /* Below code is used to check characters after valid tokens */
+ if (t->type == LWJSON_TYPE_ARRAY || t->type == LWJSON_TYPE_OBJECT) {
+ continue;
+ }
+
+ /*
+ * Check what are values after the token value
+ *
+ * As per RFC4627, every token value may have one or more
+ * blank characters, followed by one of below options:
+ * - Comma separator for next token
+ * - End of array indication
+ * - End of object indication
+ */
+ res = prv_skip_blank(&pobj);
+ if (res != lwjsonOK) {
+ goto ret;
+ }
+ /* Check if valid string is availabe after */
+ if (pobj.p == NULL || *pobj.p == '\0' || (*pobj.p != ',' && *pobj.p != ']' && *pobj.p != '}')) {
+ res = lwjsonERRJSON;
+ goto ret;
+ } else if (*pobj.p == ',') { /* Check to advance to next token immediatey */
+ ++pobj.p;
+ }
+ }
+ if (to != &lwobj->first_token || (to != NULL && to->next != NULL)) {
+ res = lwjsonERRJSON;
+ to = NULL;
+ }
+ if (to != NULL) {
+ if (to->type != LWJSON_TYPE_ARRAY && to->type != LWJSON_TYPE_OBJECT) {
+ res = lwjsonERRJSON;
+ }
+ to->token_name = NULL;
+ to->token_name_len = 0;
+ }
+ret:
+ if (res == lwjsonOK) {
+ lwobj->flags.parsed = 1;
+ }
+ return res;
+}
+
+/**
+ * \brief Parse input JSON format
+ * JSON format must be complete and must comply with RFC4627
+ * \param[in,out] lwobj: LwJSON instance
+ * \param[in] json_str: JSON string to parse
+ * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
+ */
+lwjsonr_t
+lwjson_parse(lwjson_t* lwobj, const char* json_str) {
+ return lwjson_parse_ex(lwobj, json_str, strlen(json_str));
+}
+
+/**
+ * \brief Free token instances (specially used in case of dynamic memory allocation)
+ * \param[in,out] lwobj: LwJSON instance
+ * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
+ */
+lwjsonr_t
+lwjson_free(lwjson_t* lwobj) {
+ LWJSON_MEMSET(lwobj->tokens, 0x00, sizeof(*lwobj->tokens) * lwobj->tokens_len);
+ lwobj->flags.parsed = 0;
+ return lwjsonOK;
+}
+
+/**
+ * \brief Find first match in the given path for JSON entry
+ * JSON must be valid and parsed with \ref lwjson_parse function
+ * \param[in] lwobj: JSON instance with parsed JSON string
+ * \param[in] path: Path with dot-separated entries to search for the JSON key to return
+ * \return Pointer to found token on success, `NULL` if token cannot be found
+ */
+const lwjson_token_t*
+lwjson_find(lwjson_t* lwobj, const char* path) {
+ if (lwobj == NULL || !lwobj->flags.parsed || path == NULL) {
+ return NULL;
+ }
+ return prv_find(lwjson_get_first_token(lwobj), path);
+}
+
+/**
+ * \brief Find first match in the given path for JSON path
+ * JSON must be valid and parsed with \ref lwjson_parse function
+ *
+ * \param[in] lwobj: JSON instance with parsed JSON string
+ * \param[in] token: Root token to start search at.
+ * Token must be type \ref LWJSON_TYPE_OBJECT or \ref LWJSON_TYPE_ARRAY.
+ * Set to `NULL` to use root token of LwJSON object
+ * \param[in] path: path with dot-separated entries to search for JSON key
+ * \return Pointer to found token on success, `NULL` if token cannot be found
+ */
+const lwjson_token_t*
+lwjson_find_ex(lwjson_t* lwobj, const lwjson_token_t* token, const char* path) {
+ if (lwobj == NULL || !lwobj->flags.parsed || path == NULL) {
+ return NULL;
+ }
+ if (token == NULL) {
+ token = lwjson_get_first_token(lwobj);
+ }
+ if (token == NULL || (token->type != LWJSON_TYPE_ARRAY && token->type != LWJSON_TYPE_OBJECT)) {
+ return NULL;
+ }
+ return prv_find(token, path);
+}
diff --git a/common/lwjson/lwjson.h b/common/lwjson/lwjson.h
new file mode 100644
index 0000000..ad077d6
--- /dev/null
+++ b/common/lwjson/lwjson.h
@@ -0,0 +1,388 @@
+/**
+ * \file lwjson.h
+ * \brief LwJSON - Lightweight JSON format parser
+ */
+
+/*
+ * Copyright (c) 2024 Tilen MAJERLE
+ *
+ * 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.
+ *
+ * This file is part of LwJSON - Lightweight JSON format parser.
+ *
+ * Author: Tilen MAJERLE
+ * Version: v1.7.0
+ */
+#ifndef LWJSON_HDR_H
+#define LWJSON_HDR_H
+
+#include
+#include
+#include "lwjson_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * \defgroup LWJSON Lightweight JSON format parser
+ * \brief LwJSON - Lightweight JSON format parser
+ * \{
+ */
+
+/**
+ * \brief Get size of statically allocated array
+ * \param[in] x: Object to get array size of
+ * \return Number of elements in array
+ */
+#define LWJSON_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/**
+ * \brief List of supported JSON types
+ */
+typedef enum {
+ LWJSON_TYPE_STRING, /*!< String/Text format. Everything that has beginning and ending quote character */
+ LWJSON_TYPE_NUM_INT, /*!< Number type for integer */
+ LWJSON_TYPE_NUM_REAL, /*!< Number type for real number */
+ LWJSON_TYPE_OBJECT, /*!< Object data type */
+ LWJSON_TYPE_ARRAY, /*!< Array data type */
+ LWJSON_TYPE_TRUE, /*!< True boolean value */
+ LWJSON_TYPE_FALSE, /*!< False boolean value */
+ LWJSON_TYPE_NULL, /*!< Null value */
+} lwjson_type_t;
+
+#if defined(LWJSON_DEV)
+extern const char* const lwjson_type_strings[];
+#endif
+
+/**
+ * \brief Real data type
+ */
+typedef LWJSON_CFG_REAL_TYPE lwjson_real_t;
+
+/**
+ * \brief Integer data type
+ */
+typedef LWJSON_CFG_INT_TYPE lwjson_int_t;
+
+/**
+ * \brief JSON token
+ */
+typedef struct lwjson_token {
+ struct lwjson_token* next; /*!< Next token on a list */
+ lwjson_type_t type; /*!< Token type */
+ const char* token_name; /*!< Token name (if exists) */
+ size_t token_name_len; /*!< Length of token name (this is needed to support const input strings to parse) */
+
+ union {
+ struct {
+ const char* token_value; /*!< Pointer to the beginning of the string */
+ size_t
+ token_value_len; /*!< Length of token value (this is needed to support const input strings to parse) */
+ } str; /*!< String data */
+
+ lwjson_real_t num_real; /*!< Real number format */
+ lwjson_int_t num_int; /*!< Int number format */
+ struct lwjson_token* first_child; /*!< First children object for object or array type */
+ } u; /*!< Union with different data types */
+} lwjson_token_t;
+
+/**
+ * \brief JSON result enumeration
+ */
+typedef enum {
+ lwjsonOK = 0x00, /*!< Function returns successfully */
+ lwjsonERR, /*!< Generic error message */
+ lwjsonERRJSON, /*!< Error JSON format */
+ lwjsonERRMEM, /*!< Memory error */
+ lwjsonERRPAR, /*!< Parameter error */
+
+ lwjsonSTREAMWAITFIRSTCHAR, /*!< Streaming parser did not yet receive first valid character
+ indicating start of JSON sequence */
+ lwjsonSTREAMDONE, /*!< Streaming parser is done,
+ closing character matched the stream opening one */
+ lwjsonSTREAMINPROG, /*!< Stream parsing is still in progress */
+} lwjsonr_t;
+
+/**
+ * \brief LwJSON instance
+ */
+typedef struct {
+ lwjson_token_t* tokens; /*!< Pointer to array of tokens */
+ size_t tokens_len; /*!< Size of all tokens */
+ size_t next_free_token_pos; /*!< Position of next free token instance */
+ lwjson_token_t first_token; /*!< First token on a list */
+
+ struct {
+ uint8_t parsed : 1; /*!< Flag indicating JSON parsing has finished successfully */
+ } flags; /*!< List of flags */
+} lwjson_t;
+
+lwjsonr_t lwjson_init(lwjson_t* lwobj, lwjson_token_t* tokens, size_t tokens_len);
+lwjsonr_t lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t len);
+lwjsonr_t lwjson_parse(lwjson_t* lwobj, const char* json_str);
+const lwjson_token_t* lwjson_find(lwjson_t* lwobj, const char* path);
+const lwjson_token_t* lwjson_find_ex(lwjson_t* lwobj, const lwjson_token_t* token, const char* path);
+lwjsonr_t lwjson_free(lwjson_t* lwobj);
+
+void lwjson_print_token(const lwjson_token_t* token);
+void lwjson_print_json(const lwjson_t* lwobj);
+
+/**
+ * \brief Object type for streaming parser
+ */
+typedef enum {
+ LWJSON_STREAM_TYPE_NONE, /*!< No entry - not used */
+ LWJSON_STREAM_TYPE_OBJECT, /*!< Object indication */
+ LWJSON_STREAM_TYPE_OBJECT_END, /*!< Object end indication */
+ LWJSON_STREAM_TYPE_ARRAY, /*!< Array indication */
+ LWJSON_STREAM_TYPE_ARRAY_END, /*!< Array end indication */
+ LWJSON_STREAM_TYPE_KEY, /*!< Key string */
+ LWJSON_STREAM_TYPE_STRING, /*!< Strin type */
+ LWJSON_STREAM_TYPE_TRUE, /*!< True primitive */
+ LWJSON_STREAM_TYPE_FALSE, /*!< False primitive */
+ LWJSON_STREAM_TYPE_NULL, /*!< Null primitive */
+ LWJSON_STREAM_TYPE_NUMBER, /*!< Generic number */
+} lwjson_stream_type_t;
+
+/**
+ * \brief Stream parsing stack object
+ */
+typedef struct {
+ lwjson_stream_type_t type; /*!< Streaming type - current value */
+
+ union {
+ char name[LWJSON_CFG_STREAM_KEY_MAX_LEN
+ + 1]; /*!< Last known key name, used only for \ref LWJSON_STREAM_TYPE_KEY type */
+ uint16_t index; /*!< Current index when type is an array */
+ } meta; /*!< Meta information */
+} lwjson_stream_stack_t;
+
+typedef enum {
+ LWJSON_STREAM_STATE_WAITINGFIRSTCHAR = 0x00, /*!< State to wait for very first opening character */
+ LWJSON_STREAM_STATE_PARSING, /*!< In parsing of the first char state - detecting next character state */
+ LWJSON_STREAM_STATE_PARSING_STRING, /*!< Parse string primitive */
+ LWJSON_STREAM_STATE_PARSING_PRIMITIVE, /*!< Parse any primitive that is non-string, either "true", "false", "null" or a number */
+ LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END, /*!< Expecting ',', '}' or ']' */
+ LWJSON_STREAM_STATE_EXPECTING_COLON, /*!< Expecting ':' */
+} lwjson_stream_state_t;
+
+/* Forward declaration */
+struct lwjson_stream_parser;
+
+/**
+ * \brief Callback function for various events
+ *
+ */
+typedef void (*lwjson_stream_parser_callback_fn)(struct lwjson_stream_parser* jsp, lwjson_stream_type_t type);
+
+/**
+ * \brief LwJSON streaming structure
+ */
+typedef struct lwjson_stream_parser {
+ lwjson_stream_stack_t
+ stack[LWJSON_CFG_STREAM_STACK_SIZE]; /*!< Stack used for parsing. TODO: Add conditional compilation flag */
+ size_t stack_pos; /*!< Current stack position */
+
+ lwjson_stream_state_t parse_state; /*!< Parser state */
+
+ lwjson_stream_parser_callback_fn evt_fn; /*!< Event function for user */
+
+ void* user_data; /*!< User data for callback function */
+
+ /* State */
+ union {
+ struct {
+ char buff[LWJSON_CFG_STREAM_STRING_MAX_LEN
+ + 1]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */
+ size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */
+ size_t buff_total_pos; /*!< Total buffer position used up to now (in several data chunks) */
+ uint8_t is_last; /*!< Status indicates if this is the last part of the string */
+ } str; /*!< String structure. It is only used for keys and string objects.
+ Use primitive part for all other options */
+
+ struct {
+ char buff[LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN + 1]; /*!< Temporary write buffer */
+ size_t buff_pos; /*!< Buffer position for next write */
+ } prim; /*!< Primitive object. Used for all types, except key or string */
+
+ /* Todo: Add other types */
+ } data; /*!< Data union used to parse various */
+
+ char prev_c; /*!< History of characters */
+} lwjson_stream_parser_t;
+
+lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn);
+lwjsonr_t lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data);
+void* lwjson_stream_get_user_data(lwjson_stream_parser_t* jsp);
+lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp);
+lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c);
+
+/**
+ * \brief Get number of tokens used to parse JSON
+ * \param[in] lwobj: Pointer to LwJSON instance
+ * \return Number of tokens used to parse JSON
+ */
+#define lwjson_get_tokens_used(lwobj) (((lwobj) != NULL) ? ((lwobj)->next_free_token_pos + 1) : 0)
+
+/**
+ * \brief Get very first token of LwJSON instance
+ * \param[in] lwobj: Pointer to LwJSON instance
+ * \return Pointer to first token
+ */
+#define lwjson_get_first_token(lwobj) (((lwobj) != NULL) ? (&(lwobj)->first_token) : NULL)
+
+/**
+ * \brief Get token value for \ref LWJSON_TYPE_NUM_INT type
+ * \param[in] token: token with integer type
+ * \return Int number if type is integer, `0` otherwise
+ */
+#define lwjson_get_val_int(token) \
+ ((lwjson_int_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_INT) ? (token)->u.num_int : 0))
+
+/**
+ * \brief Get token value for \ref LWJSON_TYPE_NUM_REAL type
+ * \param[in] token: token with real type
+ * \return Real numbeer if type is real, `0` otherwise
+ */
+#define lwjson_get_val_real(token) \
+ ((lwjson_real_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_REAL) ? (token)->u.num_real : 0))
+
+/**
+ * \brief Get first child token for \ref LWJSON_TYPE_OBJECT or \ref LWJSON_TYPE_ARRAY types
+ * \param[in] token: token with integer type
+ * \return Pointer to first child or `NULL` if parent token is not object or array
+ */
+#define lwjson_get_first_child(token) \
+ (const void*)(((token) != NULL && ((token)->type == LWJSON_TYPE_OBJECT || (token)->type == LWJSON_TYPE_ARRAY)) \
+ ? (token)->u.first_child \
+ : NULL)
+
+/**
+ * \brief Get string value from JSON token
+ * \param[in] token: Token with string type
+ * \param[out] str_len: Pointer to variable holding length of string.
+ * Set to `NULL` if not used
+ * \return Pointer to string or `NULL` if invalid token type
+ */
+static inline const char*
+lwjson_get_val_string(const lwjson_token_t* token, size_t* str_len) {
+ if (token != NULL && token->type == LWJSON_TYPE_STRING) {
+ if (str_len != NULL) {
+ *str_len = token->u.str.token_value_len;
+ }
+ return token->u.str.token_value;
+ }
+ return NULL;
+}
+
+/**
+ * \brief Get length of string for \ref LWJSON_TYPE_STRING token type
+ * \param[in] token: token with string type
+ * \return Length of string in units of bytes
+ */
+#define lwjson_get_val_string_length(token) \
+ ((size_t)(((token) != NULL && (token)->type == LWJSON_TYPE_STRING) ? (token)->u.str.token_value_len : 0))
+
+/**
+ * \brief Compare string token with user input string for a case-sensitive match
+ * \param[in] token: Token with string type
+ * \param[in] str: NULL-terminated string to compare
+ * \return `1` if equal, `0` otherwise
+ */
+static inline uint8_t
+lwjson_string_compare(const lwjson_token_t* token, const char* str) {
+ if (token != NULL && token->type == LWJSON_TYPE_STRING) {
+ return strncmp(token->u.str.token_value, str, token->u.str.token_value_len) == 0;
+ }
+ return 0;
+}
+
+/**
+ * \brief Compare string token with user input string for a case-sensitive match
+ * \param[in] token: Token with string type
+ * \param[in] str: NULL-terminated string to compare
+ * \param[in] len: Length of the string in bytes
+ * \return `1` if equal, `0` otherwise
+ */
+static inline uint8_t
+lwjson_string_compare_n(const lwjson_token_t* token, const char* str, size_t len) {
+ if (token != NULL && token->type == LWJSON_TYPE_STRING && len <= token->u.str.token_value_len) {
+ return strncmp(token->u.str.token_value, str, len) == 0;
+ }
+ return 0;
+}
+
+/**
+ * \name LWJSON_STREAM_SEQ
+ * \brief Helper functions for stack analysis in a callback function
+ * \note Useful exclusively for streaming functions
+ * \{
+ */
+
+/**
+ * \brief Check the sequence of JSON stack, starting from start_number index
+ * \note This applies only to one sequence element. Other macros, starting with
+ * `lwjson_stack_seq_X` (where X is the sequence length), provide
+ * more parameters for longer sequences.
+ *
+ * \param[in] jsp: LwJSON stream instance
+ * \param[in] start_num: Start number in the stack. Typically starts with `0`, but user may choose another
+ * number, if intention is to check partial sequence only
+ * \param[in] sp0: Stream stack type. Value of \ref lwjson_stream_type_t, but only last part of the enum.
+ * If user is interested in the \ref LWJSON_STREAM_TYPE_OBJECT,
+ * you should only write `OBJECT` as parameter.
+ * Idea behind is to make code smaller and easier to read, especially when
+ * using other sequence values with more parameters.
+ * \return `0` if sequence doesn't match, non-zero otherwise
+ */
+#define lwjson_stack_seq_1(jsp, start_num, sp0) ((jsp)->stack[(start_num)].type == LWJSON_STREAM_TYPE_##sp0)
+#define lwjson_stack_seq_2(jsp, start_num, sp0, sp1) \
+ (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_1((jsp), (start_num) + 1, sp1))
+#define lwjson_stack_seq_3(jsp, start_num, sp0, sp1, sp2) \
+ (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_2((jsp), (start_num) + 1, sp1, sp2))
+#define lwjson_stack_seq_4(jsp, start_num, sp0, sp1, sp2, sp3) \
+ (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_3((jsp), (start_num) + 1, sp1, sp2, sp3))
+#define lwjson_stack_seq_5(jsp, start_num, sp0, sp1, sp2, sp3, sp4) \
+ (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_4((jsp), (start_num) + 1, sp1, sp2, sp3, sp4))
+#define lwjson_stack_seq_6(jsp, start_num, sp0, sp1, sp2, sp3, sp4, sp5) \
+ (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) \
+ && lwjson_stack_seq_5((jsp), (start_num) + 1, sp1, sp2, sp3, sp4, sp5))
+#define lwjson_stack_seq_7(jsp, start_num, sp0, sp1, sp2, sp3, sp4, sp5, sp6) \
+ (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) \
+ && lwjson_stack_seq_6((jsp), (start_num) + 1, sp1, sp2, sp3, sp4, sp5, sp6))
+#define lwjson_stack_seq_8(jsp, start_num, sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7) \
+ (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) \
+ && lwjson_stack_seq_7((jsp), (start_num) + 1, sp1, sp2, sp3, sp4, sp5, sp6, sp7))
+
+/**
+ * \}
+ */
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LWJSON_HDR_H */
diff --git a/common/lwjson/lwjson_opts.h b/common/lwjson/lwjson_opts.h
new file mode 100644
index 0000000..ae92127
--- /dev/null
+++ b/common/lwjson/lwjson_opts.h
@@ -0,0 +1,153 @@
+/**
+ * \file lwjson_opt.h
+ * \brief LwJSON options
+ */
+
+/*
+ * Copyright (c) 2024 Tilen MAJERLE
+ *
+ * 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.
+ *
+ * This file is part of LwJSON - Lightweight JSON format parser.
+ *
+ * Author: Tilen MAJERLE
+ * Version: v1.7.0
+ */
+#ifndef LWJSON_OPT_HDR_H
+#define LWJSON_OPT_HDR_H
+
+/* Uncomment to ignore user options (or set macro in compiler flags) */
+/* #define LWJSON_IGNORE_USER_OPTS */
+
+/* Include application options */
+#ifndef LWJSON_IGNORE_USER_OPTS
+#include "lwjson_opts.h"
+#endif /* LWJSON_IGNORE_USER_OPTS */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * \defgroup LWJSON_OPT Configuration
+ * \brief LwJSON options
+ * \{
+ */
+
+/**
+ * \brief Real data type used to parse numbers with floating point number
+ * \note Data type must be signed, normally `float` or `double`
+ *
+ * This is used for numbers in \ref LWJSON_TYPE_NUM_REAL token data type.
+ */
+#ifndef LWJSON_CFG_REAL_TYPE
+#define LWJSON_CFG_REAL_TYPE float
+#endif
+
+/**
+ * \brief Integer type used to parse numbers
+ * \note Data type must be signed integer
+ *
+ * This is used for numbers in \ref LWJSON_TYPE_NUM_INT token data type.
+ */
+#ifndef LWJSON_CFG_INT_TYPE
+#define LWJSON_CFG_INT_TYPE long long
+#endif
+
+/**
+ * \brief Enables `1` or disables `0` support for inline comments
+ *
+ * Default set to `0` to be JSON compliant
+ */
+#ifndef LWJSON_CFG_COMMENTS
+#define LWJSON_CFG_COMMENTS 0
+#endif
+
+/**
+ * \brief Memory set function
+ *
+ * \note Function footprint is the same as \ref memset
+ */
+#ifndef LWJSON_MEMSET
+#define LWJSON_MEMSET(dst, val, len) memset((dst), (val), (len))
+#endif
+
+/**
+ * \brief Memory copy function
+ *
+ * \note Function footprint is the same as \ref memcpy
+ */
+#ifndef LWJSON_MEMCPY
+#define LWJSON_MEMCPY(dst, src, len) memcpy((dst), (src), (len))
+#endif
+
+/**
+ * \defgroup LWJSON_OPT_STREAM JSON stream
+ * \brief JSON streaming confiuration
+ * \{
+ */
+
+/**
+ * \brief Max length of token key (object key name) to be available for stack storage
+ *
+ */
+#ifndef LWJSON_CFG_STREAM_KEY_MAX_LEN
+#define LWJSON_CFG_STREAM_KEY_MAX_LEN 32
+#endif
+
+/**
+ * \brief Max stack size (depth) in units of \ref lwjson_stream_stack_t structure
+ *
+ */
+#ifndef LWJSON_CFG_STREAM_STACK_SIZE
+#define LWJSON_CFG_STREAM_STACK_SIZE 16
+#endif
+
+/**
+ * \brief Max size of string for single parsing in units of bytes
+ *
+ */
+#ifndef LWJSON_CFG_STREAM_STRING_MAX_LEN
+#define LWJSON_CFG_STREAM_STRING_MAX_LEN 256
+#endif
+
+/**
+ * \brief Max number of bytes used to parse primitive.
+ *
+ * Primitives are all numbers and logical values (null, true, false)
+ */
+#ifndef LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN
+#define LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN 32
+#endif
+
+/**
+ * \}
+ */
+
+/**
+ * \}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LWJSON_OPT_HDR_H */