ソースを参照

feat: 添加了aFun的基础部件

SongZihuan 3 年 前
コミット
cde511d8b9

+ 59 - 0
.gitignore

@@ -0,0 +1,59 @@
+# C Language
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+
+#IDE
+.idea
+.vs
+.vscode
+cmake-*

+ 47 - 0
CMakeLists.txt

@@ -0,0 +1,47 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
+SET(CMAKE_C_STANDARD 11)
+PROJECT(aFun LANGUAGES C)
+
+SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
+SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib)  # 设置输出路径
+SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH})
+SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH})
+
+SET(BUILD_SHARED_LIBS TRUE)  # 默认编译动态库
+
+IF(MSVC)
+    SET(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
+    MESSAGE("Build with MSVC")
+ELSE()
+    ADD_COMPILE_OPTIONS(-fPIC)  # 启用fPIC参数
+    MESSAGE("Not build with MSVC")
+ENDIF()
+
+SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")  # 设置安装路径
+IF (build_with_rpath)
+    SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+    MESSAGE("SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) success!")
+ENDIF ()
+
+MESSAGE("CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}")
+MESSAGE("CMAKE_C_COMPILER:   ${CMAKE_C_COMPILER}")
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)  # 添加include路径
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include/cJson)
+
+if (MSVC)
+    INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include/dlfcn)
+endif ()
+
+ADD_DEFINITIONS(-DCC="${CMAKE_C_COMPILER}")
+ADD_DEFINITIONS(-DCXXC="${CMAKE_CXX_COMPILER}")
+ADD_DEFINITIONS(-DSRC_PATH="${CMAKE_SOURCE_DIR}")
+
+ADD_SUBDIRECTORY(src)
+
+IF(ENABLE_aFunTEST)
+    ENABLE_TESTING()
+    ADD_SUBDIRECTORY(test)
+ENDIF()
+
+INSTALL(TARGETS aFun)

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 aFunlang
+
+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.

+ 56 - 0
README.md

@@ -0,0 +1,56 @@
+# aFunlang 编程语言
+
+## aFun简洁
+aFun是一款通用的动态解释型编程语言,具有函数式编程范式和面向对象编程范式。它可以与C语言联动,可以调用C的高效率函数,同时不失编程的抽象性。
+
+### aFun的设计哲学
+
+* 保持简单
+* 和谐统一更优雅
+* 关注做什么而不是怎么做
+* 状态可在运行时被修改
+* 灵活性与安全可以并存
+* 注释和提示信息很重要
+* 计算机的时间比人的时间廉价
+
+**保持简单,可以使用更简洁的语法就不使用复杂的方法。**
+1. 在编写aFun代码时,可以使用字面量解决的问题就不要使用函数回调;
+2. 尽量为函数添加默认参数,简化函数调用时的参数负担;
+3. 不是编写一个为用户完成所有决定的程序,相反程序应该有更多的参数供用户定制,但是需要一个合适的默认参数。
+4. 扁平胜于嵌套,但若嵌套更能表现意思则应使用嵌套而非扁平。
+5. 变量名应体现具体意思,可以利用符号。如`int->str`比`int_to_str`更优雅。
+
+**和谐统一更优雅,在aFun中追求和谐与统一。正是因为这种和谐、统一,令aFun有更少的公理,使得aFun更容易被理解与上手。**
+1. 内置对象和非内置对象的地位是相同的,底层实现也是相似的、统一的。
+2. if分支、for分支底层都是通过函数实现的,甚至可以添加任意新分支或去除任意分支。
+3. 无论是用户拓展的字面量还是基础的字面量(如`int`),都是通过`.afg`文件中正则表达式实现匹配的。
+4. 和谐与统一不应仅仅在表面上体现为统一,这往往会造成漏洞。
+5. 和谐统一的界面、接口比混乱不堪明显更加优雅。
+
+**关注做什么而不是怎么做,合理的模块化代码,通过OOP将代码分类。同时,使用类库而不是自己造轮子。**
+1. 例如,使用`map`函数用于遍历列表,而不是手写一个`for`循环遍历列表。
+2. 例如,计算图形面积时,应该为不同图形封装一个类,而不是把所有图形面积计算的代码放在一个函数中。
+3. 代码应该抽象,而不总是关注底层。
+
+**状态可在运行时被修改,代码的语义、对象的标识等都可在运行时被修改。**
+1. 代码可以生成代码、分析代码、修改代码。
+2. 前缀的语义可以在运行时被改变。
+3. 对象的类型(如,是否可被继承、是否内联函数)可在运行时被修改。
+4. 运行时状态修改有时候会带来灾难,但是一定程度上提高了灵活性。`afc`提供了大量的API供这种运行时状态修改。
+
+**灵活性与安全性并存,灵活性与安全性之间可以有所取舍和偏袒。**
+1. 合约、权限限定等都是可选的。
+2. 语言的动态性,运行时状态修改为aFun提供了很大的灵活性。
+3. `afc`提供的API时aFun灵活性的有力支持。
+4. 对象为`afc`提供的API是`afc`运行的有力支持。
+5. 一次性任务的代码以及激进的大型项目代码应追求灵活性,而稳定的大型项目的代码应追求安全性。
+
+**注释和提示信息很重要,aFun会保存注释信息,而不仅仅是无视他。**
+1. 使用函数前,查阅函数的注释信息。
+2. 使用变量、函数前,查阅其合约信息以及权限信息。
+3. aFun会将注释信息、合约信息、权限信息处理为一个文本,记录下来。
+
+**计算机的时间比人的时间廉价,编码时应思考问题的解决方案,而不是思考二进制代码。**
+1. 为了提供逻辑与思维的便利,可以适度割让一定的效率。
+2. 现在对硬件的浪费,在未来或许只是一种寻常事。
+3. 与关注做什么而不是怎么做类似,怎么做是计算机的事情,aFun通常提供了高效的实现函数。

+ 4 - 0
include/aFun.h

@@ -0,0 +1,4 @@
+#ifndef AFUN__H
+#define AFUN__H
+
+#endif //AFUN__H

+ 20 - 0
include/cJSON/LICENSE

@@ -0,0 +1,20 @@
+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.
+

+ 293 - 0
include/cJSON/cJSON.h

@@ -0,0 +1,293 @@
+/*
+  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 14
+
+#include <stddef.h>
+
+/* 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
+
+/* 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 adress 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);
+
+/* 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

+ 8 - 0
include/cjson.h

@@ -0,0 +1,8 @@
+#ifndef CJSON__H
+#define CJSON__H
+
+#include "cJSON/cJSON.h"
+void cJsonInit();
+cJSON *parseJsonFile(FILE *file);
+
+#endif // CJSON__H

+ 17 - 0
include/dlfcn/COPYING

@@ -0,0 +1,17 @@
+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.

+ 94 - 0
include/dlfcn/dlfcn.h

@@ -0,0 +1,94 @@
+/*
+ * dlfcn-win32
+ * Copyright (c) 2007 Ramiro Polla
+ *
+ * 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 DLFCN_H
+#define DLFCN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(DLFCN_WIN32_SHARED)
+#if defined(DLFCN_WIN32_EXPORTS)
+#   define DLFCN_EXPORT __declspec(dllexport)
+#else
+#   define DLFCN_EXPORT __declspec(dllimport)
+#endif
+#else
+#   define DLFCN_EXPORT
+#endif
+
+/* Relocations are performed when the object is loaded. */
+#define RTLD_NOW    0
+
+/* Relocations are performed at an implementation-defined time.
+ * Windows API does not support lazy symbol resolving (when first reference
+ * to a given symbol occurs). So RTLD_LAZY implementation is same as RTLD_NOW.
+ */
+#define RTLD_LAZY   RTLD_NOW
+
+/* All symbols are available for relocation processing of other modules. */
+#define RTLD_GLOBAL (1 << 1)
+
+/* All symbols are not made available for relocation processing by other modules. */
+#define RTLD_LOCAL  (1 << 2)
+
+/* These two were added in The Open Group Base Specifications Issue 6.
+ * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant.
+ */
+
+/* The symbol lookup happens in the normal global scope. */
+#define RTLD_DEFAULT    ((void *)0)
+
+/* Specifies the next object after this one that defines name. */
+#define RTLD_NEXT       ((void *)-1)
+
+/* Structure filled in by dladdr() */
+typedef struct dl_info
+{
+   const char *dli_fname;  /* Filename of defining object (thread unsafe and reused on every call to dladdr) */
+   void       *dli_fbase;  /* Load address of that object */
+   const char *dli_sname;  /* Name of nearest lower symbol */
+   void       *dli_saddr;  /* Exact value of nearest symbol */
+} Dl_info;
+
+/* Open a symbol table handle. */
+DLFCN_EXPORT void *dlopen(const char *file, int mode);
+
+/* Close a symbol table handle. */
+DLFCN_EXPORT int dlclose(void *handle);
+
+/* Get the address of a symbol from a symbol table handle. */
+DLFCN_EXPORT void *dlsym(void *handle, const char *name);
+
+/* Get diagnostic information. */
+DLFCN_EXPORT char *dlerror(void);
+
+/* Translate address to symbolic information (no POSIX standard) */
+DLFCN_EXPORT int dladdr(const void *addr, Dl_info *info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DLFCN_H */

+ 22 - 0
include/macro.h

@@ -0,0 +1,22 @@
+/*
+ * 文件名: macro.h
+ * 目标: 定义公共宏
+ */
+
+#ifndef MACRO__H
+#define MACRO__H
+#include <stdbool.h>
+
+#ifndef __bool_true_false_are_defined
+#define bool int
+#define true (1)
+#define false (0)
+#endif
+
+#define NUL ((char)0)
+#define W_NUL ((wchar_t)0)
+
+typedef int fline;
+typedef char *fpath;
+
+#endif //MACRO__H

+ 45 - 0
include/mem.h

@@ -0,0 +1,45 @@
+/*
+ * 文件名: mem.h
+ * 目标: 内存管理工具的头文件
+ */
+
+#ifndef MEM__H
+#define MEM__H
+
+#include "stdlib.h"
+
+// 开关
+#define BUILD_VTMEM 1
+
+// 默认情况
+#define safeCalloc(n, size) (calloc((n), (size)))
+#define safeFree(p) ((((p)!=NULL) ? free(p) : NULL), (p)=NULL)
+#define print_memInfo() NULL
+#define safeFree_ free
+
+#if BUILD_VTMEM
+#undef safeCalloc
+
+void *safeCalloc(size_t n, size_t size);
+
+#if DEBUG_VTMEM
+#undef safeFree
+#undef safeFree_
+#undef print_memInfo
+
+void safeFreeDebug(void *p);
+int print_memInfo();
+
+#define safeFree safeFreeDebug
+#define safeFree_ safeFreeDebug
+#else
+
+#undef safeFree_
+void safeFree_(void *p);
+
+#endif  // DEBUG_VTMEM
+#elif DEBUG_VTMEM
+#error "The option \"debug afmem\" can be turned on only when the option \"build afmem\" is turned on"
+#endif  // BUILD_VTMEM
+
+#endif  // MEM__H

+ 126 - 0
include/tool.h

@@ -0,0 +1,126 @@
+/*
+ * 文件名: tool.h
+ * 目标: aFun tool公共API
+ * aFunTool是aFun实用工具库, 内含aFun调用的实用函数
+ */
+
+#ifndef TOOL__H
+#define TOOL__H
+#include <wchar.h>
+#include <string.h>
+#include <signal.h>
+#include <dlfcn.h>
+
+#include "macro.h"
+
+// md5 工具
+#define READ_DATA_SIZE	(1024)
+#define MD5_SIZE (16)
+#define MD5_STR_LEN (MD5_SIZE * 2)
+#define MD5_STRING (MD5_STR_LEN + 1)
+int getFileMd5(const char *path, char *md5str);
+
+// hash 工具
+typedef long int time33_t;
+
+time33_t time33(char *str);
+time33_t w_time33(wchar_t *str);
+
+// string 工具
+#define EQ_STR(str1, str2) (!strcmp((str1), (str2)))
+#define EQ_WSTR(wid1, wid2) (!wcscmp((wid1), (wid2)))
+#define NEW_STR(size) (char *)safeCalloc((size) + 1, sizeof(char))
+#define NEW_WSTR(size) (wchar_t *)safeCalloc((size) + 1, sizeof(wchar_t))
+#define STR_LEN(p) (((p) == NULL) ? 0 : strlen((p)))
+#define WSTR_LEN(p) (((p) == NULL) ? 0 : wcslen((p)))
+char *strCopy(const char *str);
+wchar_t *wstrCopy(const wchar_t *str);
+wchar_t *wstrWithWchar(wchar_t *str, size_t size, int free_old, ...);
+wchar_t *wstrWithWchar_(wchar_t *str, wint_t new, bool free_old);
+wchar_t *wstrExpansion(wchar_t *str, size_t size, bool free_old);
+char *strJoinIter(char *base, int free_base, ...);
+char *strJoin(char *first, char *second, bool free_first, bool free_last);
+char *strJoin_(char *first, char *second, bool free_first, bool free_last);
+wchar_t *wstrJoin(wchar_t *first, wchar_t *second, bool free_first, bool free_last);
+wchar_t *wstrJoin_(wchar_t *first, wchar_t *second, bool free_first, bool free_last);
+wchar_t *wstrCopySelf(wchar_t *str, long times);
+wchar_t *wstrReverse(wchar_t *str);
+wchar_t *convertToWstr(char *str, bool free_old);
+char *convertToStr(wchar_t *wstr, bool free_old);
+
+// file 工具
+#ifdef __linux__
+
+#define SEP "/"
+#define SEP_CH '/'
+#define SHARED_MARK ".so"
+
+#else
+
+#define SEP "\\"
+#define SEP_CH '\\'
+#define SHARED_MARK ".dll"
+
+#endif
+
+int checkFile(char *path);
+char *getFileName(char *path_1);
+char *fileNameToVar(char *name, bool need_free);
+char *findPath(char *path, char *env, bool need_free);
+
+// signal 工具
+typedef int vsignal;
+typedef struct SignalTag SignalTag;
+struct SignalTag{
+    volatile vsignal signum;  // 信号
+    volatile enum SignalType{
+        signal_reset=0,  // 没有信号
+        signal_appear,  // 信号未被处理
+    } status;
+};
+
+extern volatile struct SignalTag signal_tag;  // 在tool.c中定义
+void afSignalHandler(int signum);
+
+// time 工具
+void safeSleep(double ms);
+
+// dlc 工具
+
+/*
+ * NEW_DLC_SYMBOL: 用于定义指定类型的symbol结构体
+ * DLC_SYMBOL: 指定类型的symbol结构体名
+ * GET_SYMBOL: 访问symbol成员
+ * MAKE_SYMBOL: 生成一个symbol
+ * COPY_SYMBOL: 拷贝一个symbol(拷贝其引用)
+ * READ_SYMBOL: 在dlc中获取一个symbol
+ * FREE_SYMBOL: 释放symbol
+ *
+ * openLibary: 打开动态库
+ * freeLibary: 释放动态库
+ * dlcExit: 释放所有动态库
+ */
+#define NEW_DLC_SYMBOL(TYPE, NAME) struct DLC##NAME##SYMBOL { \
+TYPE *symbol; \
+struct DlcHandle *dlc; \
+}
+
+#define DLC_SYMBOL(NAME) struct DLC##NAME##SYMBOL
+#define GET_SYMBOL(SYMBOL) (*((SYMBOL)->symbol))
+#define MAKE_SYMBOL(symbol, TYPE) ((struct DLC##TYPE##SYMBOL *) (makeSymbol_(symbol)))
+#define COPY_SYMBOL(ds, TYPE) ((struct DLC##TYPE##SYMBOL *) (copySymbol_((DlcSymbol_ *)(ds))))
+#define READ_SYMBOL(dlc, name, TYPE) ((struct DLC##TYPE##SYMBOL *) (getSymbol_((dlc), (name))))
+#define FREE_SYMBOL(symbol) ((symbol) != NULL ? (freeSymbol_((DlcSymbol_ *)(symbol))) : NULL)
+
+typedef struct DlcSymbol_ DlcSymbol_;
+typedef struct DlcHandle DlcHandle;
+
+struct DlcHandle *openLibary(const char *file, int mode);
+struct DlcSymbol_ *makeSymbol_(void *symbol);
+struct DlcSymbol_ *copySymbol_(struct DlcSymbol_ *ds);
+struct DlcSymbol_ *getSymbol_(struct DlcHandle *dlc, const char *name);
+void freeSymbol_(struct DlcSymbol_ *symbol);
+bool freeLibary(struct DlcHandle *dlc);
+void dlcExit();
+
+#endif //TOOL__H

+ 13 - 0
src/CMakeLists.txt

@@ -0,0 +1,13 @@
+IF (MSVC)
+    # 编译dlfcn-win32
+    ADD_SUBDIRECTORY(dlfcn)
+ENDIF()
+
+ADD_SUBDIRECTORY(memory)
+ADD_SUBDIRECTORY(tool)
+ADD_SUBDIRECTORY(cjson)
+
+SET(libary af_memory)
+
+ADD_EXECUTABLE(aFun main.c)
+TARGET_LINK_LIBRARIES(aFun ${libary})

+ 10 - 0
src/cjson/CMakeLists.txt

@@ -0,0 +1,10 @@
+PROJECT(cJson LANGUAGES C)
+ADD_LIBRARY(cJson cJSON.c)
+
+
+PROJECT(af_json LANGUAGES C)
+ADD_LIBRARY(af_json tool.c)
+SET_TARGET_PROPERTIES(af_json PROPERTIES OUTPUT_NAME "aFunJson")
+TARGET_LINK_LIBRARIES(af_json af_memory af_tool cJson)
+
+INSTALL(TARGETS af_json cJson)

+ 20 - 0
src/cjson/LICENSE

@@ -0,0 +1,20 @@
+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.
+

+ 3095 - 0
src/cjson/cJSON.c

@@ -0,0 +1,3095 @@
+/*
+  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 <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <float.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON/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
+#define NAN 0.0/0.0
+#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 != 14)
+    #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);
+        }
+        if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+        {
+            global_hooks.deallocate(item->string);
+        }
+        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;
+}
+
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+    char *copy = NULL;
+    /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+    if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference))
+    {
+        return NULL;
+    }
+    if (strlen(valuestring) <= strlen(object->valuestring))
+    {
+        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;
+        }
+        if (newbuffer)
+        {
+            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
+    {
+        /* 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);
+    }
+
+    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);
+    }
+
+    return printed;
+
+fail:
+    if (buffer->buffer != NULL)
+    {
+        hooks->deallocate(buffer->buffer);
+    }
+
+    if (printed != NULL)
+    {
+        hooks->deallocate(printed);
+    }
+
+    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);
+        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;
+        }
+
+        /* 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))
+    {
+        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)
+    {
+        return false;
+    }
+
+    after_inserted = get_array_item(array, (size_t)which);
+    if (after_inserted == NULL)
+    {
+        return add_item_to_array(array, newitem);
+    }
+
+    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) || (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);
+    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;
+    }
+    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;
+    }
+    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;
+    }
+    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;
+    }
+    a->child->prev = n;
+
+    return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, 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)
+    {
+        newchild = cJSON_Duplicate(child, 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)) || cJSON_IsInvalid(a))
+    {
+        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);
+}

+ 34 - 0
src/cjson/tool.c

@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include "mem.h"
+#include "tool.h"
+#include "cJSON/cJSON.h"
+
+#ifndef FSTRING_SIZE
+#define FSTRING_SIZE (100)
+#endif
+
+static void *safeMalloc(size_t size) {
+    return safeCalloc(1, size);
+}
+
+void cJsonInit() {
+    static cJSON_Hooks hooks = {.malloc_fn=safeMalloc, .free_fn=safeFree_};
+    cJSON_InitHooks(&hooks);
+}
+
+cJSON *parseJsonFile(FILE *file) {
+    char *tmp = NULL;
+    char buffer[FSTRING_SIZE + 1];
+    cJSON *re;
+
+    while (1) {
+        fgets(buffer, FSTRING_SIZE + 1, file);
+        tmp = strJoin(tmp, buffer, true, false);
+        if (ferror(file) || feof(file))
+            break;
+    }
+
+    re = cJSON_Parse(tmp);
+    safeFree_(tmp);
+    return re;
+}

+ 3 - 0
src/dlfcn/CMakeLists.txt

@@ -0,0 +1,3 @@
+PROJECT(dl LANGUAGES C)
+ADD_LIBRARY(dl dlfcn.c)
+INSTALL(TARGETS dl)

+ 17 - 0
src/dlfcn/COPYING

@@ -0,0 +1,17 @@
+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.

+ 785 - 0
src/dlfcn/dlfcn.c

@@ -0,0 +1,785 @@
+/*
+ * dlfcn-win32
+ * Copyright (c) 2007 Ramiro Polla
+ * Copyright (c) 2015 Tiancheng "Timothy" Gu
+ * Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
+ * Copyright (c) 2020 Ralf Habacker <ralf.habacker@freenet.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifdef _DEBUG
+#define _CRTDBG_MAP_ALLOC
+#include <stdlib.h>
+#include <crtdbg.h>
+#endif
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Older versions do not have this type */
+#if _WIN32_WINNT < 0x0500
+typedef ULONG ULONG_PTR;
+#endif
+
+/* Older SDK versions do not have these macros */
+#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
+#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4
+#endif
+#ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
+#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2
+#endif
+
+#ifdef _MSC_VER
+/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
+#pragma intrinsic( _ReturnAddress )
+#else
+/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
+#ifndef _ReturnAddress
+#define _ReturnAddress( ) ( __builtin_extract_return_addr( __builtin_return_address( 0 ) ) )
+#endif
+#endif
+
+#ifdef DLFCN_WIN32_SHARED
+#define DLFCN_WIN32_EXPORTS
+#endif
+#include "dlfcn.h"
+
+#if defined( _MSC_VER ) && _MSC_VER >= 1300
+/* https://docs.microsoft.com/en-us/cpp/cpp/noinline */
+#define DLFCN_NOINLINE __declspec( noinline )
+#elif defined( __GNUC__ ) && ( ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) )
+/* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */
+#define DLFCN_NOINLINE __attribute__(( noinline ))
+#else
+#define DLFCN_NOINLINE
+#endif
+
+/* Note:
+ * MSDN says these functions are not thread-safe. We make no efforts to have
+ * any kind of thread safety.
+ */
+
+typedef struct local_object {
+    HMODULE hModule;
+    struct local_object *previous;
+    struct local_object *next;
+} local_object;
+
+static local_object first_object;
+
+/* These functions implement a double linked list for the local objects. */
+static local_object *local_search( HMODULE hModule )
+{
+    local_object *pobject;
+
+    if( hModule == NULL )
+        return NULL;
+
+    for( pobject = &first_object; pobject; pobject = pobject->next )
+        if( pobject->hModule == hModule )
+            return pobject;
+
+    return NULL;
+}
+
+static BOOL local_add( HMODULE hModule )
+{
+    local_object *pobject;
+    local_object *nobject;
+
+    if( hModule == NULL )
+        return TRUE;
+
+    pobject = local_search( hModule );
+
+    /* Do not add object again if it's already on the list */
+    if( pobject != NULL )
+        return TRUE;
+
+    for( pobject = &first_object; pobject->next; pobject = pobject->next );
+
+    nobject = (local_object *) malloc( sizeof( local_object ) );
+
+    if( !nobject )
+        return FALSE;
+
+    pobject->next = nobject;
+    nobject->next = NULL;
+    nobject->previous = pobject;
+    nobject->hModule = hModule;
+
+    return TRUE;
+}
+
+static void local_rem( HMODULE hModule )
+{
+    local_object *pobject;
+
+    if( hModule == NULL )
+        return;
+
+    pobject = local_search( hModule );
+
+    if( pobject == NULL )
+        return;
+
+    if( pobject->next )
+        pobject->next->previous = pobject->previous;
+    if( pobject->previous )
+        pobject->previous->next = pobject->next;
+
+    free( pobject );
+}
+
+/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
+ * static buffer.
+ * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
+ * the limit.
+ */
+static char error_buffer[65535];
+static BOOL error_occurred;
+
+static void save_err_str( const char *str, DWORD dwMessageId )
+{
+    DWORD ret;
+    size_t pos, len;
+
+    len = strlen( str );
+    if( len > sizeof( error_buffer ) - 5 )
+        len = sizeof( error_buffer ) - 5;
+
+    /* Format error message to:
+     * "<argument to function that failed>": <Windows localized error message>
+      */
+    pos = 0;
+    error_buffer[pos++] = '"';
+    memcpy( error_buffer + pos, str, len );
+    pos += len;
+    error_buffer[pos++] = '"';
+    error_buffer[pos++] = ':';
+    error_buffer[pos++] = ' ';
+
+    ret = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId,
+        MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
+        error_buffer + pos, (DWORD) ( sizeof( error_buffer ) - pos ), NULL );
+    pos += ret;
+
+    /* When FormatMessageA() fails it returns zero and does not touch buffer
+     * so add trailing null byte */
+    if( ret == 0 )
+        error_buffer[pos] = '\0';
+
+    if( pos > 1 )
+    {
+        /* POSIX says the string must not have trailing <newline> */
+        if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' )
+            error_buffer[pos-2] = '\0';
+    }
+
+    error_occurred = TRUE;
+}
+
+static void save_err_ptr_str( const void *ptr, DWORD dwMessageId )
+{
+    char ptr_buf[2 + 2 * sizeof( ptr ) + 1];
+    char num;
+    size_t i;
+
+    ptr_buf[0] = '0';
+    ptr_buf[1] = 'x';
+
+    for( i = 0; i < 2 * sizeof( ptr ); i++ )
+    {
+        num = (char) ( ( ( (ULONG_PTR) ptr ) >> ( 8 * sizeof( ptr ) - 4 * ( i + 1 ) ) ) & 0xF );
+        ptr_buf[2 + i] = num + ( ( num < 0xA ) ? '0' : ( 'A' - 0xA ) );
+    }
+
+    ptr_buf[2 + 2 * sizeof( ptr )] = 0;
+
+    save_err_str( ptr_buf, dwMessageId );
+}
+
+static HMODULE MyGetModuleHandleFromAddress( const void *addr )
+{
+    static BOOL (WINAPI *GetModuleHandleExAPtr)(DWORD, LPCSTR, HMODULE *) = NULL;
+    static BOOL failed = FALSE;
+    HMODULE kernel32;
+    HMODULE hModule;
+    MEMORY_BASIC_INFORMATION info;
+    SIZE_T sLen;
+
+    if( !failed && GetModuleHandleExAPtr == NULL )
+    {
+        kernel32 = GetModuleHandleA( "Kernel32.dll" );
+        if( kernel32 != NULL )
+            GetModuleHandleExAPtr = (BOOL (WINAPI *)(DWORD, LPCSTR, HMODULE *)) GetProcAddress( kernel32, "GetModuleHandleExA" );
+        if( GetModuleHandleExAPtr == NULL )
+            failed = TRUE;
+    }
+
+    if( !failed )
+    {
+        /* If GetModuleHandleExA is available use it with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS */
+        if( !GetModuleHandleExAPtr( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) )
+            return NULL;
+    }
+    else
+    {
+        /* To get HMODULE from address use undocumented hack from https://stackoverflow.com/a/2396380
+         * The HMODULE of a DLL is the same value as the module's base address.
+         */
+        sLen = VirtualQuery( addr, &info, sizeof( info ) );
+        if( sLen != sizeof( info ) )
+            return NULL;
+        hModule = (HMODULE) info.AllocationBase;
+    }
+
+    return hModule;
+}
+
+/* Load Psapi.dll at runtime, this avoids linking caveat */
+static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded )
+{
+    static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD) = NULL;
+    static BOOL failed = FALSE;
+    UINT uMode;
+    HMODULE psapi;
+
+    if( failed )
+        return FALSE;
+
+    if( EnumProcessModulesPtr == NULL )
+    {
+        /* Windows 7 and newer versions have K32EnumProcessModules in Kernel32.dll which is always pre-loaded */
+        psapi = GetModuleHandleA( "Kernel32.dll" );
+        if( psapi != NULL )
+            EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "K32EnumProcessModules" );
+
+        /* Windows Vista and older version have EnumProcessModules in Psapi.dll which needs to be loaded */
+        if( EnumProcessModulesPtr == NULL )
+        {
+            /* Do not let Windows display the critical-error-handler message box */
+            uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
+            psapi = LoadLibraryA( "Psapi.dll" );
+            if( psapi != NULL )
+            {
+                EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "EnumProcessModules" );
+                if( EnumProcessModulesPtr == NULL )
+                    FreeLibrary( psapi );
+            }
+            SetErrorMode( uMode );
+        }
+
+        if( EnumProcessModulesPtr == NULL )
+        {
+            failed = TRUE;
+            return FALSE;
+        }
+    }
+
+    return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded );
+}
+
+DLFCN_EXPORT
+void *dlopen( const char *file, int mode )
+{
+    HMODULE hModule;
+    UINT uMode;
+
+    error_occurred = FALSE;
+
+    /* Do not let Windows display the critical-error-handler message box */
+    uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
+
+    if( file == NULL )
+    {
+        /* POSIX says that if the value of file is NULL, a handle on a global
+         * symbol object must be provided. That object must be able to access
+         * all symbols from the original program file, and any objects loaded
+         * with the RTLD_GLOBAL flag.
+         * The return value from GetModuleHandle( ) allows us to retrieve
+         * symbols only from the original program file. EnumProcessModules() is
+         * used to access symbols from other libraries. For objects loaded
+         * with the RTLD_LOCAL flag, we create our own list later on. They are
+         * excluded from EnumProcessModules() iteration.
+         */
+        hModule = GetModuleHandle( NULL );
+
+        if( !hModule )
+            save_err_str( "(null)", GetLastError( ) );
+    }
+    else
+    {
+        HANDLE hCurrentProc;
+        DWORD dwProcModsBefore, dwProcModsAfter;
+        char lpFileName[MAX_PATH];
+        size_t i, len;
+
+        len = strlen( file );
+
+        if( len >= sizeof( lpFileName ) )
+        {
+            save_err_str( file, ERROR_FILENAME_EXCED_RANGE );
+            hModule = NULL;
+        }
+        else
+        {
+            /* MSDN says backslashes *must* be used instead of forward slashes. */
+            for( i = 0; i < len; i++ )
+            {
+                if( file[i] == '/' )
+                    lpFileName[i] = '\\';
+                else
+                    lpFileName[i] = file[i];
+            }
+            lpFileName[len] = '\0';
+
+            hCurrentProc = GetCurrentProcess( );
+
+            if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 )
+                dwProcModsBefore = 0;
+
+            /* POSIX says the search path is implementation-defined.
+             * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
+             * to UNIX's search paths (start with system folders instead of current
+             * folder).
+             */
+            hModule = LoadLibraryExA( lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
+
+            if( !hModule )
+            {
+                save_err_str( lpFileName, GetLastError( ) );
+            }
+            else
+            {
+                if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 )
+                    dwProcModsAfter = 0;
+
+                /* If the object was loaded with RTLD_LOCAL, add it to list of local
+                 * objects, so that its symbols cannot be retrieved even if the handle for
+                 * the original program file is passed. POSIX says that if the same
+                 * file is specified in multiple invocations, and any of them are
+                 * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
+                 * symbols will remain global. If number of loaded modules was not
+                 * changed after calling LoadLibraryEx(), it means that library was
+                 * already loaded.
+                 */
+                if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter )
+                {
+                    if( !local_add( hModule ) )
+                    {
+                        save_err_str( lpFileName, ERROR_NOT_ENOUGH_MEMORY );
+                        FreeLibrary( hModule );
+                        hModule = NULL;
+                    }
+                }
+                else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter )
+                {
+                    local_rem( hModule );
+                }
+            }
+        }
+    }
+
+    /* Return to previous state of the error-mode bit flags. */
+    SetErrorMode( uMode );
+
+    return (void *) hModule;
+}
+
+DLFCN_EXPORT
+int dlclose( void *handle )
+{
+    HMODULE hModule = (HMODULE) handle;
+    BOOL ret;
+
+    error_occurred = FALSE;
+
+    ret = FreeLibrary( hModule );
+
+    /* If the object was loaded with RTLD_LOCAL, remove it from list of local
+     * objects.
+     */
+    if( ret )
+        local_rem( hModule );
+    else
+        save_err_ptr_str( handle, GetLastError( ) );
+
+    /* dlclose's return value in inverted in relation to FreeLibrary's. */
+    ret = !ret;
+
+    return (int) ret;
+}
+
+DLFCN_NOINLINE /* Needed for _ReturnAddress() */
+DLFCN_EXPORT
+void *dlsym( void *handle, const char *name )
+{
+    FARPROC symbol;
+    HMODULE hCaller;
+    HMODULE hModule;
+    DWORD dwMessageId;
+
+    error_occurred = FALSE;
+
+    symbol = NULL;
+    hCaller = NULL;
+    hModule = GetModuleHandle( NULL );
+    dwMessageId = 0;
+
+    if( handle == RTLD_DEFAULT )
+    {
+        /* The symbol lookup happens in the normal global scope; that is,
+         * a search for a symbol using this handle would find the same
+         * definition as a direct use of this symbol in the program code.
+         * So use same lookup procedure as when filename is NULL.
+         */
+        handle = hModule;
+    }
+    else if( handle == RTLD_NEXT )
+    {
+        /* Specifies the next object after this one that defines name.
+         * This one refers to the object containing the invocation of dlsym().
+         * The next object is the one found upon the application of a load
+         * order symbol resolution algorithm. To get caller function of dlsym()
+         * use _ReturnAddress() intrinsic. To get HMODULE of caller function
+         * use MyGetModuleHandleFromAddress() which calls either standard
+         * GetModuleHandleExA() function or hack via VirtualQuery().
+         */
+        hCaller = MyGetModuleHandleFromAddress( _ReturnAddress( ) );
+
+        if( hCaller == NULL )
+        {
+            dwMessageId = ERROR_INVALID_PARAMETER;
+            goto end;
+        }
+    }
+
+    if( handle != RTLD_NEXT )
+    {
+        symbol = GetProcAddress( (HMODULE) handle, name );
+
+        if( symbol != NULL )
+            goto end;
+    }
+
+    /* If the handle for the original program file is passed, also search
+     * in all globally loaded objects.
+     */
+
+    if( hModule == handle || handle == RTLD_NEXT )
+    {
+        HANDLE hCurrentProc;
+        HMODULE *modules;
+        DWORD cbNeeded;
+        DWORD dwSize;
+        size_t i;
+
+        hCurrentProc = GetCurrentProcess( );
+
+        /* GetModuleHandle( NULL ) only returns the current program file. So
+         * if we want to get ALL loaded module including those in linked DLLs,
+         * we have to use EnumProcessModules( ).
+         */
+        if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 )
+        {
+            modules = malloc( dwSize );
+            if( modules )
+            {
+                if( MyEnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded )
+                {
+                    for( i = 0; i < dwSize / sizeof( HMODULE ); i++ )
+                    {
+                        if( handle == RTLD_NEXT && hCaller )
+                        {
+                            /* Next modules can be used for RTLD_NEXT */
+                            if( hCaller == modules[i] )
+                                hCaller = NULL;
+                            continue;
+                        }
+                        if( local_search( modules[i] ) )
+                            continue;
+                        symbol = GetProcAddress( modules[i], name );
+                        if( symbol != NULL )
+                        {
+                            free( modules );
+                            goto end;
+                        }
+                    }
+
+                }
+                free( modules );
+            }
+            else
+            {
+                dwMessageId = ERROR_NOT_ENOUGH_MEMORY;
+                goto end;
+            }
+        }
+    }
+
+end:
+    if( symbol == NULL )
+    {
+        if( !dwMessageId )
+            dwMessageId = ERROR_PROC_NOT_FOUND;
+        save_err_str( name, dwMessageId );
+    }
+
+    return *(void **) (&symbol);
+}
+
+DLFCN_EXPORT
+char *dlerror( void )
+{
+    /* If this is the second consecutive call to dlerror, return NULL */
+    if( !error_occurred )
+        return NULL;
+
+    /* POSIX says that invoking dlerror( ) a second time, immediately following
+     * a prior invocation, shall result in NULL being returned.
+     */
+    error_occurred = FALSE;
+
+    return error_buffer;
+}
+
+/* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2
+ * for details */
+
+/* Get specific image section */
+static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *size )
+{
+    IMAGE_DOS_HEADER *dosHeader;
+    IMAGE_OPTIONAL_HEADER *optionalHeader;
+
+    dosHeader = (IMAGE_DOS_HEADER *) module;
+
+    if( dosHeader->e_magic != 0x5A4D )
+        return FALSE;
+
+    optionalHeader = (IMAGE_OPTIONAL_HEADER *) ( (BYTE *) module + dosHeader->e_lfanew + 24 );
+
+    if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC )
+        return FALSE;
+
+    if( index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR )
+        return FALSE;
+
+    if( optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0 )
+        return FALSE;
+
+    if( size != NULL )
+        *size = optionalHeader->DataDirectory[index].Size;
+
+    *ptr = (void *)( (BYTE *) module + optionalHeader->DataDirectory[index].VirtualAddress );
+
+    return TRUE;
+}
+
+/* Return symbol name for a given address from export table */
+static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, const void *addr, void **func_address )
+{
+    DWORD i;
+    void *candidateAddr = NULL;
+    int candidateIndex = -1;
+    BYTE *base = (BYTE *) module;
+    DWORD *functionAddressesOffsets = (DWORD *) (base + ied->AddressOfFunctions);
+    DWORD *functionNamesOffsets = (DWORD *) (base + ied->AddressOfNames);
+    USHORT *functionNameOrdinalsIndexes = (USHORT *) (base + ied->AddressOfNameOrdinals);
+
+    for( i = 0; i < ied->NumberOfFunctions; i++ )
+    {
+        if( (void *) ( base + functionAddressesOffsets[i] ) > addr || candidateAddr >= (void *) ( base + functionAddressesOffsets[i] ) )
+            continue;
+
+        candidateAddr = (void *) ( base + functionAddressesOffsets[i] );
+        candidateIndex = i;
+    }
+
+    if( candidateIndex == -1 )
+        return NULL;
+
+    *func_address = candidateAddr;
+
+    for( i = 0; i < ied->NumberOfNames; i++ )
+    {
+        if( functionNameOrdinalsIndexes[i] == candidateIndex )
+            return (const char *) ( base + functionNamesOffsets[i] );
+    }
+
+    return NULL;
+}
+
+static BOOL is_valid_address( const void *addr )
+{
+    MEMORY_BASIC_INFORMATION info;
+    SIZE_T result;
+
+    if( addr == NULL )
+        return FALSE;
+
+    /* check valid pointer */
+    result = VirtualQuery( addr, &info, sizeof( info ) );
+
+    if( result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS )
+        return FALSE;
+
+    return TRUE;
+}
+
+/* Return state if address points to an import thunk
+ *
+ * An import thunk is setup with a 'jmp' instruction followed by an
+ * absolute address (32bit) or relative offset (64bit) pointing into
+ * the import address table (iat), which is partially maintained by
+ * the runtime linker.
+ */
+static BOOL is_import_thunk( const void *addr )
+{
+    return *(short *) addr == 0x25ff ? TRUE : FALSE;
+}
+
+/* Return adress from the import address table (iat),
+ * if the original address points to a thunk table entry.
+ */
+static void *get_address_from_import_address_table( void *iat, DWORD iat_size, const void *addr )
+{
+    BYTE *thkp = (BYTE *) addr;
+    /* Get offset from thunk table (after instruction 0xff 0x25)
+     *   4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00
+     */
+    ULONG offset = *(ULONG *)( thkp + 2 );
+#ifdef _WIN64
+    /* On 64 bit the offset is relative
+     *      4018c8:   ff 25 4a 8a 00 00    jmpq    *0x8a4a(%rip)    # 40a318 <__imp_VirtualQuery>
+     * And can be also negative (MSVC in WDK)
+     *   100002f20:   ff 25 3a e1 ff ff    jmpq   *-0x1ec6(%rip)    # 0x100001060
+     * So cast to signed LONG type
+     */
+    BYTE *ptr = (BYTE *)( thkp + 6 + (LONG) offset );
+#else
+    /* On 32 bit the offset is absolute
+     *   4019b4:    ff 25 90 71 40 00    jmp    *0x40719
+     */
+    BYTE *ptr = (BYTE *) offset;
+#endif
+
+    if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size )
+        return NULL;
+
+    return *(void **) ptr;
+}
+
+/* Holds module filename */
+static char module_filename[2*MAX_PATH];
+
+static BOOL fill_info( const void *addr, Dl_info *info )
+{
+    HMODULE hModule;
+    DWORD dwSize;
+    IMAGE_EXPORT_DIRECTORY *ied;
+    void *funcAddress = NULL;
+
+    /* Get module of the specified address */
+    hModule = MyGetModuleHandleFromAddress( addr );
+
+    if( hModule == NULL )
+        return FALSE;
+
+    dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) );
+
+    if( dwSize == 0 || dwSize == sizeof( module_filename ) )
+        return FALSE;
+
+    info->dli_fname = module_filename;
+    info->dli_fbase = (void *) hModule;
+
+    /* Find function name and function address in module's export table */
+    if( get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void **) &ied, NULL ) )
+        info->dli_sname = get_export_symbol_name( hModule, ied, addr, &funcAddress );
+    else
+        info->dli_sname = NULL;
+
+    info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : (void *) addr;
+
+    return TRUE;
+}
+
+DLFCN_EXPORT
+int dladdr( const void *addr, Dl_info *info )
+{
+    if( info == NULL )
+        return 0;
+
+    if( !is_valid_address( addr ) )
+        return 0;
+
+    if( is_import_thunk( addr ) )
+    {
+        void *iat;
+        DWORD iatSize;
+        HMODULE hModule;
+
+        /* Get module of the import thunk address */
+        hModule = MyGetModuleHandleFromAddress( addr );
+
+        if( hModule == NULL )
+            return 0;
+
+        if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) )
+        {
+            /* Fallback for cases where the iat is not defined,
+             * for example i586-mingw32msvc-gcc */
+            IMAGE_IMPORT_DESCRIPTOR *iid;
+            DWORD iidSize;
+
+            if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) )
+                return 0;
+
+            if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 )
+                return 0;
+
+            iat = (void *)( (BYTE *) hModule + iid->FirstThunk );
+            /* We assume that in this case iid and iat's are in linear order */
+            iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid );
+        }
+
+        addr = get_address_from_import_address_table( iat, iatSize, addr );
+
+        if( !is_valid_address( addr ) )
+            return 0;
+    }
+
+    if( !fill_info( addr, info ) )
+        return 0;
+
+    return 1;
+}
+
+#ifdef DLFCN_WIN32_SHARED
+BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
+{
+    (void) hinstDLL;
+    (void) fdwReason;
+    (void) lpvReserved;
+    return TRUE;
+}
+#endif

+ 6 - 0
src/main.c

@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main() {
+    printf("Hello World\n");
+    return 0;
+}

+ 6 - 0
src/memory/CMakeLists.txt

@@ -0,0 +1,6 @@
+PROJECT(af_memory LANGUAGES C)
+
+ADD_LIBRARY(af_memory ${af_memory_SOURCE_DIR}/mem.c)
+SET_TARGET_PROPERTIES(af_memory PROPERTIES OUTPUT_NAME "aFunMemory")
+
+INSTALL(TARGETS af_memory)

+ 63 - 0
src/memory/mem.c

@@ -0,0 +1,63 @@
+/*
+ * 文件名: mem.c
+ * 目标: 内存管理
+ * 对应头文件: mem.h
+ * 由宏 BUILD_VTMEM 和宏 DEBUG_VTMEM 联合控制行为
+ * BUILD_VTMEM 开启时启用该组件, 关闭时safeCalloc等函数与原生的calloc相同
+ * DEBUG_VTMEM 开启时会自动统计申请成功的内存总量和释放的内存总量。
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "mem.h"
+
+#if BUILD_VTMEM
+#if DEBUG_VTMEM
+#include <stdio.h>
+static unsigned long long used_mem = 0;  // 已经使用的内存
+
+struct afmem_block {
+    size_t size;
+};
+
+void safeFreeDebug(void *p) {
+    if (p == NULL)
+        return;
+    p -= sizeof(struct afmem_block);  // 往回偏移, 得到malloc函数分配的真实指针
+    used_mem -= ((struct afmem_block *)p)->size;
+    free(p);  // 偏移
+}
+
+void *safeCalloc(size_t n, size_t size) {
+    size_t r_size = n * size + sizeof(struct afmem_block);
+    void *re = malloc(r_size);
+    if (re == NULL)
+        exit(1);
+    used_mem += n * size;
+    memset(re, 0, r_size);  // 清空为0, 用malloc模拟calloc的行为
+    ((struct afmem_block *)re)->size = n * size;  // 记录大小
+    return (char *)re + sizeof(struct afmem_block);  // 偏移
+}
+
+int print_memInfo() {
+    return printf("used_mem = %llu\n", used_mem);
+}
+
+#else
+// 无 void safeFree_(void *p);
+// 无 print_memInfo();
+
+void *safeCalloc(size_t n, size_t size) {
+    void *re = calloc(n, size);
+    if (re == NULL)
+        exit(1);
+    return re;
+}
+
+void safeFree_(void *p) {
+    if (p != NULL)
+        free(p);
+}
+
+#endif
+#endif

+ 9 - 0
src/tool/CMakeLists.txt

@@ -0,0 +1,9 @@
+PROJECT(af_tool LANGUAGES C)
+
+AUX_SOURCE_DIRECTORY(${af_tool_SOURCE_DIR} src)  # 寻找源文件
+ADD_LIBRARY(af_tool ${src})
+
+SET_TARGET_PROPERTIES(af_tool PROPERTIES OUTPUT_NAME "aFunTool")
+TARGET_LINK_LIBRARIES(af_tool af_memory dl)
+
+INSTALL(TARGETS af_tool)

+ 37 - 0
src/tool/__md5.h

@@ -0,0 +1,37 @@
+#ifndef MD5__H
+#define MD5__H
+
+#define F(x,y,z) ((x & y) | (~x & z))
+#define G(x,y,z) ((x & z) | (y & ~z))
+#define H(x,y,z) (x^y^z)
+#define I(x,y,z) (y ^ (x | ~z))
+#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
+#define FF(a,b,c,d,x,s,ac) \
+          do { \
+          a += F(b,c,d) + x + ac; \
+          a = ROTATE_LEFT(a, (unsigned)s); \
+          a += b; \
+          } while(0)
+
+#define GG(a,b,c,d,x,s,ac) \
+          do { \
+          a += G(b,c,d) + x + ac; \
+          a = ROTATE_LEFT(a, (unsigned)s); \
+          a += b; \
+          } while(0)
+
+#define HH(a,b,c,d,x,s,ac) \
+          do { \
+          a += H(b,c,d) + x + ac; \
+          a = ROTATE_LEFT(a, (unsigned)s); \
+          a += b; \
+          } while(0)
+
+#define II(a,b,c,d,x,s,ac) \
+          do { \
+          a += I(b,c,d) + x + ac; \
+          a = ROTATE_LEFT(a, (unsigned)s); \
+          a += b; \
+          } while(0)
+
+#endif //MD5__H

+ 140 - 0
src/tool/dlc.c

@@ -0,0 +1,140 @@
+#include "mem.h"
+#include "tool.h"
+
+#ifndef GC_SZIE
+#define GC_SZIE (0)
+#endif
+
+struct DlcSymbol_ {
+    void *symbol;
+    struct DlcHandle *dlc;
+};
+
+struct DlcHandle {
+    void *handle;
+    long long int link;  // 引用计数
+    long long int count;  // 开启计数
+    struct DlcHandle *next;
+    struct DlcHandle *last;
+};
+
+static struct DlcHandle *dlc_l = NULL;
+static int gc_count = 0;
+
+#include <stdio.h>
+
+struct DlcHandle *openLibary(const char *file, int mode) {
+    printf("file = %s\n", file);
+    void *handle = dlopen(file, mode);
+    struct DlcHandle *dlc;
+
+    if (handle == NULL)
+        return NULL;
+
+    for (struct DlcHandle *tmp = dlc_l; tmp != NULL; tmp = tmp->next) {
+        if (tmp->handle == handle) {
+            dlclose(handle);  // 减少dlopen时对handle的引用计数
+            tmp->count++;
+            return tmp;
+        }
+    }
+
+    dlc = safeCalloc(1, sizeof(struct DlcHandle));
+    dlc->handle = handle;
+
+    dlc->count = 1;
+    dlc->next = dlc_l;
+    dlc->last = NULL;
+    if (dlc_l != NULL)
+        dlc_l->last = dlc;
+    dlc_l = dlc;
+
+    return dlc;
+}
+
+bool freeLibary(struct DlcHandle *dlc) {
+    if (dlc->count == 0)
+        return false;
+    dlc->count--;
+    return true;
+}
+
+static bool freeLibary_(struct DlcHandle *dlc, bool f) {
+    if (dlc->link != 0 && !f)  // f - 强制释放
+        return false;
+
+    dlclose(dlc->handle);
+
+    if (dlc->last == NULL)
+        dlc_l = dlc->next;
+    else
+        dlc->last->next = dlc->next;
+
+    if (dlc->next != NULL)
+        dlc->next->last = dlc->last;
+
+    safeFree_(dlc);
+    return true;
+}
+
+struct DlcSymbol_ *makeSymbol_(void *symbol) {
+    struct DlcSymbol_ *ds = safeCalloc(1, sizeof(struct DlcSymbol_));
+    ds->symbol = symbol;
+    ds->dlc = NULL;
+    return ds;
+}
+
+static void blindSymbol(struct DlcSymbol_ *ds, struct DlcHandle *dlc) {
+    if (ds->dlc != NULL)
+        ds->dlc->link--;
+
+    ds->dlc = dlc;
+    dlc->link++;
+}
+
+struct DlcSymbol_ *copySymbol_(struct DlcSymbol_ *ds) {
+    if (ds == NULL)
+        return NULL;
+
+    struct DlcSymbol_ *new = safeCalloc(1, sizeof(struct DlcSymbol_));
+    new->symbol = ds->symbol;
+    if (ds->dlc != NULL)
+        blindSymbol(new, ds->dlc);
+    return new;
+}
+
+struct DlcSymbol_ *getSymbol_(struct DlcHandle *dlc, const char *name) {
+    void *symbol = dlsym(dlc->handle, name);
+    if (symbol == NULL)
+        return NULL;
+
+    struct DlcSymbol_ *ds = safeCalloc(1, sizeof(struct DlcSymbol_));
+    ds->symbol = symbol;
+    blindSymbol(ds, dlc);
+    return ds;
+}
+
+void dlcGC() {
+    for (struct DlcHandle *tmp = dlc_l, *next; tmp != NULL; tmp = next) {
+        next = tmp->next;
+        if (tmp->link == 0 && tmp->count == 0)
+            freeLibary_(tmp, false);
+    }
+}
+
+void freeSymbol_(struct DlcSymbol_ *symbol) {
+    if (symbol->dlc != NULL) {
+        symbol->dlc->link--;
+        gc_count++;
+    }
+
+    safeFree_(symbol);
+
+    if (gc_count >= GC_SZIE)
+        dlcGC();
+}
+
+void dlcExit() {
+    while (dlc_l != NULL)
+        freeLibary_(dlc_l, true);
+}

+ 106 - 0
src/tool/file.c

@@ -0,0 +1,106 @@
+/*
+ * 文件名: file.c
+ * 目标: 关于文件读取的实用函数
+ */
+
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include "tool.h"
+#include "mem.h"
+
+#ifndef S_ISREG
+#define	S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
+#endif
+
+#ifndef S_ISDIR
+#define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
+#endif
+
+/*
+ * 函数名: checkFile
+ * 目标判断文件类型, 若是普通文件返回1, 若是文件夹返回2, 其他遇到错误返回0
+ */
+int checkFile(char *path){
+    struct stat my_stat;
+    if (path == NULL || stat(path, &my_stat) != 0)
+        return 0;
+    if (S_ISREG(my_stat.st_mode))  // 普通文件
+        return 1;
+    else if (S_ISDIR(my_stat.st_mode))
+        return 2;
+    else
+        return 0;
+}
+
+/*
+ * 函数: getFileName
+ * 目标: 给定路径获取该路径所指定的文件名
+ */
+char *getFileName(char *path_1){
+    char *slash = NULL;  // 名字开始的字符的指针
+    char *point = NULL;  // 后缀名.所在的字符的指针
+    char *name = NULL;  // 返回值
+    char *path = strCopy(path_1);  // 复制数组, 避免path_1是常量字符串导致无法修改其值
+
+    if (path[STR_LEN(path) - 1] == SEP_CH)  // 若路径的最后一个字符为SEP, 则忽略此SEP
+        path[STR_LEN(path) - 1] = NUL;
+
+    if ((slash = strrchr(path, SEP_CH)) == NULL)
+        slash = path;
+    else
+        slash++;
+
+    if ((point = strchr(path, '.')) != NULL)
+        *point = NUL;
+
+    name = strCopy(slash);
+    safeFree_(path);
+
+    if (!isalpha(*name) && *name != '_')
+        name = strJoin("_", name, false, true);
+    for (char *tmp = name; *tmp != 0; tmp++)
+        if (!isalnum(*tmp) &&'_' != *tmp)
+            *tmp = '_';
+    return name;
+}
+
+/*
+ * 函数名: fileNameToVar
+ * 目标: 把一个文件名转换为合法的变量名(替换不合法字符为_)
+ */
+char *fileNameToVar(char *name, bool need_free){
+    char *var;
+    if (need_free)
+        var = name;  // 在原数据上修改
+    else
+        var = strCopy(name);  // 复制新的数据再修改
+
+    if (!isalpha(*var) && *var != '_')
+        var = strJoin("_", var, false, true);
+    for (char *tmp = var; *tmp != 0; tmp++)
+        if (!isalnum(*tmp) &&'_' != *tmp)
+            *tmp = '_';
+    return var;
+}
+
+/*
+ * 函数名: findPath
+ * 目标: 转换路径为合法路径(相对路径->绝对路径, 绝对路径保持不变)
+ */
+#include "stdio.h"
+char *findPath(char *path, char *env, bool need_free){
+    assert(env[STR_LEN(env) - 1] == SEP_CH);  // env 必须以 SEP 结尾
+#ifdef __linux
+    if (path[0] != SEP_CH) { // 不以 / 开头
+#else
+    if (!(isupper(path[0]) && (path)[1] == ':')) {  // 不以盘符开头
+#endif
+        return strJoin(env, path, false, need_free);  // 调整为相对路径模式
+    } else if (!need_free) { // 若设置了need_free为true, 则等于先复制在释放原来的, 等于没有复制, 所以不做操作
+        return strCopy(path);
+    } else {
+        return path;
+    }
+}

+ 20 - 0
src/tool/hash.c

@@ -0,0 +1,20 @@
+/*
+ * 文件名: hash.c
+ * 目标: 关于哈希表的实用函数
+ */
+
+#include "tool.h"
+
+time33_t time33(char *str) {
+    unsigned int hash = 5381;
+    while(*str)
+        hash += (hash << 5 ) + (*str++);
+    return (hash & 0x7FFFFFFF);
+}
+
+time33_t w_time33(wchar_t *str) {
+    unsigned int hash = 5381;
+    while(*str)
+        hash += (hash << 5 ) + (*str++);
+    return (hash & 0x7FFFFFFF);
+}

+ 219 - 0
src/tool/md5.c

@@ -0,0 +1,219 @@
+/**
+ * 文件名: md5.c
+ * 目标: 计算文件的md5值
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "tool.h"
+#include "__md5.h"
+
+struct MD5_CTX {
+    unsigned int count[2];
+    unsigned int state[4];
+    unsigned char buffer[64];
+};
+
+typedef struct MD5_CTX MD5_CTX;
+
+static void MD5Init(MD5_CTX *context);
+static void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int input_len);
+static void MD5Final(MD5_CTX *context,unsigned char digest[16]);
+static void MD5Transform(unsigned int state[4],unsigned char block[64]);
+static void MD5Encode(unsigned char *output,const unsigned int *input,unsigned int len);
+static void MD5Decode(unsigned int *output,const unsigned char *input,unsigned int len);
+
+unsigned char PADDING[] = {
+                0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+        };
+
+static void MD5Init(MD5_CTX *context) {
+    context->count[0] = 0;
+    context->count[1] = 0;
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+}
+
+static void MD5Update(MD5_CTX *context, unsigned char *input, unsigned int input_len) {
+    unsigned int i;
+    unsigned int index;
+    unsigned int part_len;
+
+    index = (context->count[0] >> (unsigned)3) & (unsigned)0x3F;
+    part_len = 64 - index;
+    context->count[0] += input_len << (unsigned)3;
+
+    if(context->count[0] < (input_len << (unsigned)3))
+        context->count[1]++;
+    context->count[1] += input_len >> (unsigned)29;
+
+    if(input_len >= part_len) {
+        memcpy(&context->buffer[index], input, part_len);
+        MD5Transform(context->state, context->buffer);
+
+        for(i = part_len; i + 64 <= input_len; i+=64)
+            MD5Transform(context->state, &input[i]);
+
+        index = 0;
+    }
+    else
+        i = 0;
+    memcpy(&context->buffer[index], &input[i], input_len - i);
+}
+
+static void MD5Final(MD5_CTX *context, unsigned char digest[16]) {
+    unsigned int index;
+    unsigned int pad_len;
+    unsigned char bits[8];
+
+    index = (context->count[0] >> (unsigned)3) & (unsigned)0x3F;
+    pad_len = (index < 56) ? (56 - index) : (120 - index);
+    MD5Encode(bits, context->count, 8);
+    MD5Update(context, PADDING, pad_len);
+    MD5Update(context, bits, 8);
+    MD5Encode(digest, context->state, 16);
+}
+
+static void MD5Encode(unsigned char *output,const unsigned int *input, unsigned int len) {
+    unsigned int i = 0;
+    unsigned int j = 0;
+
+    while(j < len)
+    {
+        output[j] = input[i] & (unsigned)0xFF;
+        output[j+1] = (input[i] >> (unsigned)8) & (unsigned)0xFF;
+        output[j+2] = (input[i] >> (unsigned)16) & (unsigned)0xFF;
+        output[j+3] = (input[i] >> (unsigned)24) & (unsigned)0xFF;
+        i++;
+        j += 4;
+    }
+}
+
+static void MD5Decode(unsigned int *output, const unsigned char *input, unsigned int len) {
+    for (unsigned int i=0, j=0; j < len; i++, j+=4) {
+        output[i] = (input[j]) |
+                    (input[j+1] << (unsigned)8) |
+                    (input[j+2] << (unsigned)16) |
+                    (input[j+3] << (unsigned)24);
+    }
+}
+
+static void MD5Transform(unsigned int state[4], unsigned char block[64]) {
+    unsigned int a = state[0];
+    unsigned int b = state[1];
+    unsigned int c = state[2];
+    unsigned int d = state[3];
+    unsigned int x[64];
+
+    MD5Decode(x,block,64);
+
+    FF(a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */
+    FF(d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */
+    FF(c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */
+    FF(b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */
+    FF(a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */
+    FF(d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */
+    FF(c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */
+    FF(b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */
+    FF(a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */
+    FF(d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */
+    FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
+    FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
+    FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
+    FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
+    FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
+    FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
+
+    /* Round 2 */
+    GG(a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */
+    GG(d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */
+    GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
+    GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */
+    GG(a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */
+    GG(d, a, b, c, x[10], 9,  0x2441453); /* 22 */
+    GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
+    GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */
+    GG(a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */
+    GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
+    GG(c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */
+    GG(b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */
+    GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
+    GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */
+    GG(c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */
+    GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
+
+    /* Round 3 */
+    HH(a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */
+    HH(d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */
+    HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
+    HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
+    HH(a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */
+    HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */
+    HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */
+    HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
+    HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
+    HH(d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */
+    HH(c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */
+    HH(b, c, d, a, x[ 6], 23,  0x4881d05); /* 44 */
+    HH(a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */
+    HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
+    HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
+    HH(b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */
+
+    /* Round 4 */
+    II(a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */
+    II(d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */
+    II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
+    II(b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */
+    II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
+    II(d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */
+    II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
+    II(b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */
+    II(a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */
+    II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
+    II(c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */
+    II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
+    II(a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */
+    II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
+    II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */
+    II(b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+}
+
+
+int getFileMd5(const char *path, char *md5str) {
+    FILE *fd;
+
+    unsigned long ret;
+    unsigned char data[READ_DATA_SIZE];
+    unsigned char md5_value[MD5_SIZE];
+    MD5_CTX md5;
+
+    if ((fd = fopen(path, "rb")) == NULL)
+        return -1;
+
+    MD5Init(&md5);
+    while (1) {
+        ret = fread(data, 1, READ_DATA_SIZE, fd);
+        MD5Update(&md5, data, ret);
+        if (ret < READ_DATA_SIZE)
+            break;
+    }
+
+    fclose(fd);
+    MD5Final(&md5, md5_value);
+
+    for(int i = 0; i < MD5_SIZE; i++)
+        snprintf(md5str + i * 2, 2 + 1, "%02x", md5_value[i]);
+
+    return 0;
+}

+ 15 - 0
src/tool/signal.c

@@ -0,0 +1,15 @@
+/*
+ * 文件名: signal.c
+ * 目标: 信号处理的相关实用函数
+ */
+
+#include <signal.h>
+#include "tool.h"
+
+volatile struct SignalTag signal_tag = {.signum=0, .status=signal_reset};
+
+void afSignalHandler(int signum) {
+    signal_tag.status = signal_appear;
+    signal_tag.signum = signum;
+    signal(signum, afSignalHandler);  // afSignalHandler 触发后,会和信号解除绑定,因此必须再次绑定
+}

+ 228 - 0
src/tool/string.c

@@ -0,0 +1,228 @@
+/*
+ * 文件名: string.c
+ * 目标: 关于char和wchar_t的实用函数
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "tool.h"
+#include "mem.h"
+
+char *strCopy(const char *str){
+    char *tmp = NEW_STR(STR_LEN(str));
+    if (str != NULL)
+        strcpy(tmp, str);
+    return tmp;
+}
+
+wchar_t *wstrCopy(const wchar_t *str){
+    wchar_t *tmp = NEW_WSTR(WSTR_LEN(str));
+    if (str != NULL)
+        wcscpy(tmp, str);
+    return tmp;
+}
+
+/*
+ * 函数名: wstrWithWchar
+ * 目标: 赋值str到新空间,并且扩展 size 个大小, 若write为true则为扩展到大小写入...字符
+ */
+wchar_t *wstrWithWchar(wchar_t *str, size_t size, int free_old, ...) {  // free_base使用int而不是bool, 是因为va_start
+    size_t base_len = WSTR_LEN(str);
+    wchar_t *tmp = NEW_WSTR(base_len + size);
+    if (base_len != 0)
+        wcscpy(tmp, str);
+    va_list va;
+    va_start(va, free_old);
+    for (int i = 0; i < size; i++)
+        tmp[base_len + i] = (wchar_t)va_arg(va, int);
+    va_end(va);
+    if (free_old)
+        safeFree_(str);
+    return tmp;
+}
+
+/*
+ * 函数名: wstrWithWchar_
+ * 目标: 在str后增加一个新的字符
+ */
+wchar_t *wstrWithWchar_(wchar_t *str, wint_t new, bool free_old) {
+    size_t base_len = WSTR_LEN(str);
+    wchar_t *tmp = NEW_WSTR(base_len + 1);
+    if (base_len != 0)
+        wcscpy(tmp, str);
+    tmp[base_len] = new;
+    if (free_old)
+        safeFree_(str);
+    return tmp;
+}
+
+/*
+ * 函数名: wstrExpansion
+ * 目标: 把str复制到新的空间, 并拓展其大小
+ */
+wchar_t *wstrExpansion(wchar_t *str, size_t size, bool free_old) {
+    size_t base_len = WSTR_LEN(str);
+    wchar_t *tmp = NEW_WSTR(base_len + size);
+    if (base_len != 0)
+        wcscpy(tmp, str);
+    if (free_old)
+        safeFree_(str);
+    return tmp;
+}
+
+/*
+ * 函数名: strJoinIter
+ * 目标: 在base后面拼接...字符串,...必须以NULL结尾
+ */
+char *strJoinIter(char *base, int free_base, ...) {  // free_base使用int而不是bool, 是因为va_start
+    va_list ap;
+    va_start(ap, free_base);
+    for (char *ch = va_arg(ap, char *); ch != NULL; ch = va_arg(ap, char *)) {
+        base = strJoin(base, ch, free_base, false);
+        free_base = true;
+    }
+    va_end(ap);
+    return base;
+}
+
+/*
+ * 函数名: strJoin
+ * 目标: 拼接两个字符串
+ */
+char *strJoin(char *first, char *second, bool free_first, bool free_last) {
+    if (first == NULL && second == NULL)
+        return NULL;
+    else if (first == NULL){
+        first = second;
+        second = NULL;
+        free_first = free_last;
+        free_last = false;
+    }
+
+    char *new = NEW_STR(STR_LEN(first) + STR_LEN(second));
+    strcat(new, first);
+    if (second != NULL)
+        strcat(new, second);
+
+    if (free_first)
+        safeFree_(first);
+    if (free_last)
+        safeFree_(second);
+    return new;
+}
+
+/*
+ * 函数名: strJoin_
+ * 目标: 拼接两个字符串, 比 strJoin 更快
+ */
+char *strJoin_(char *first, char *second, bool free_first, bool free_last) {
+    char *new = NEW_STR(STR_LEN(first) + STR_LEN(second));
+    strcat(new, first);
+    if (second != NULL)
+        strcat(new, second);
+    if (free_first)
+        safeFree_(first);
+    if (free_last)
+        safeFree_(second);
+    return new;
+}
+
+/*
+ * 函数名: wstrJoin
+ * 目标: 拼接两个宽字符串
+ */
+wchar_t *wstrJoin(wchar_t *first, wchar_t *second, bool free_first, bool free_last) {
+    if (first == NULL && second == NULL)
+        return NULL;
+    else if (first == NULL){
+        first = second;
+        second = NULL;
+        free_first = free_last;
+        free_last = false;
+    }
+
+    wchar_t *new = wstrExpansion(first, WSTR_LEN(second), false);
+    if (second != NULL)
+        wcscat(new, second);
+
+    if (free_first)
+        safeFree_(first);
+    if (free_last)
+        safeFree_(second);
+    return new;
+}
+
+/*
+ * 函数名: wstrJoin_
+ * 目标: 拼接两个宽字符串, 比 wstrJoin 更快
+ */
+wchar_t *wstrJoin_(wchar_t *first, wchar_t *second, bool free_first, bool free_last) {
+    wchar_t *new = wstrExpansion(first, WSTR_LEN(second), false);
+    if (second != NULL)
+        wcscat(new, second);
+
+    if (free_first)
+        safeFree_(first);
+    if (free_last)
+        safeFree_(second);
+    return new;
+}
+
+/*
+ * 函数名: wstrCopySelf
+ * 目标: 自我赋值 times 次, 若times小于0则会反转字符串
+ */
+wchar_t *wstrCopySelf(wchar_t *str, long times){
+    bool need_free = false;
+    wchar_t *new_str = NULL;
+    if (times < 0){
+        str = wstrReverse(str);
+        times = -times;
+        need_free = true;
+    }
+    for (long i=0; i < times; i++)
+        new_str = wstrJoin_(new_str, str, true, false);
+    if (need_free)
+        safeFree_(str);
+    return new_str;
+}
+
+/*
+ * 函数名: wstrReverse
+ * 目标: 反转字符串
+ */
+wchar_t *wstrReverse(wchar_t *str){
+    size_t len_str = WSTR_LEN(str);
+    wchar_t *new_str = NEW_WSTR(len_str);
+    for (int i = 0;i < len_str;i++)
+        new_str[i] = str[len_str - i - 1];
+    return new_str;
+}
+
+/*
+ * 函数名: convertToWstr
+ * 目标: char *转换为wchar_t *
+ */
+wchar_t *convertToWstr(char *str, bool free_old) {
+    size_t len = STR_LEN(str);
+    wchar_t *tmp = NEW_WSTR(len);
+    mbstowcs(tmp, str, len);
+    if (free_old)
+        safeFree_(str);
+    return tmp;
+}
+
+/*
+ * 函数名: convertToStr
+ * 目标: wchar_t *转换为char *
+ */
+char *convertToStr(wchar_t *wstr, bool free_old) {
+    size_t len = WSTR_LEN(wstr);
+    char *tmp = NEW_STR(len);
+    wcstombs(tmp, wstr, len);
+    if (free_old)
+        safeFree_(wstr);
+    return tmp;
+}

+ 22 - 0
src/tool/time.c

@@ -0,0 +1,22 @@
+/*
+ * 文件名: time.c
+ * 目标: 关于time的实用函数
+ */
+
+#include <time.h>
+#include "tool.h"
+
+/*
+ * 函数名: safe_sleep
+ * 目标: 等待指定的秒数(ms) 支持小数
+ */
+void safeSleep(double ms) {
+    time_t start = clock();
+    time_t now;
+    time_t d_time;
+    time_t ms_t = (time_t) (ms * CLOCKS_PER_SEC);
+    do {
+        now = clock();
+        d_time = now - start;
+    } while (d_time < ms_t);
+}

+ 32 - 0
test/CMakeLists.txt

@@ -0,0 +1,32 @@
+SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
+
+SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})  # 统一输出到bin目录
+SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+
+FUNCTION(ADD_aFunTest TEST_NAME TEST_SOURCE)
+    ADD_EXECUTABLE(aFunTest_${TEST_NAME} ${TEST_SOURCE})
+    TARGET_LINK_LIBRARIES(aFunTest_${TEST_NAME} af_memory af_tool af_json)
+    ADD_TEST(NAME aFunTest_${TEST_NAME} COMMAND $<TARGET_FILE:aFunTest_${TEST_NAME}>)
+ENDFUNCTION()
+
+FUNCTION(SET_PASS TEST_NAME PASS_STR)
+    SET_TESTS_PROPERTIES(aFunTest_${TEST_NAME} PROPERTIES PASS_REGULAR_EXPRESSION ${PASS_STR})
+ENDFUNCTION()
+
+FUNCTION(SET_LINK TEST_NAME LIB)
+    TARGET_LINK_LIBRARIES(aFunTest_${TEST_NAME} ${LIB})
+ENDFUNCTION()
+
+ADD_DEFINITIONS(-DTEST_LIB_PATH="${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")  # 设置宏: lib的所在路径
+
+ADD_LIBRARY(lib_Test1 test_lib.c)  # 测试程序需要使用的动态库
+SET_TARGET_PROPERTIES(lib_Test1 PROPERTIES OUTPUT_NAME "Test1")
+
+ADD_aFunTest(mem test_mem.c)
+ADD_aFunTest(lib test_test_lib.c)
+ADD_aFunTest(dlc test_dlc.c)
+
+SET_LINK(lib lib_Test1)  # 链接测试程序需要的动态库
+
+SET_PASS(lib "num = 100 test = 110")
+SET_PASS(dlc "a = 100, test = 110")

+ 32 - 0
test/test_dlc.c

@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "tool.h"
+
+int main() {
+    atexit(dlcExit);
+
+    DlcHandle *dlc = openLibary(TEST_LIB_PATH "/libTest1" SHARED_MARK, RTLD_NOW);  // TEST_LIB_PATH 传进来的分隔符 都是 "/"
+    if (dlc == NULL) {
+        fprintf(stderr, "libary not found!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    typedef int func(int a);
+    NEW_DLC_SYMBOL(int, INT);
+    NEW_DLC_SYMBOL(func, FUNC);
+
+    DLC_SYMBOL(INT) *a;
+    DLC_SYMBOL(FUNC) *fun;
+
+    a = READ_SYMBOL(dlc, "num", INT);
+    fun = READ_SYMBOL(dlc, "test", FUNC);
+
+    printf("a = %d, test = %d\n", GET_SYMBOL(a), GET_SYMBOL(fun)(GET_SYMBOL(a)));
+
+    FREE_SYMBOL(a);
+    FREE_SYMBOL(fun);
+
+    if (!freeLibary(dlc))
+        exit(EXIT_FAILURE);
+    return 0;
+}

+ 5 - 0
test/test_lib.c

@@ -0,0 +1,5 @@
+int num = 100;
+
+int test(int a) {
+    return 10 + a;
+}

+ 12 - 0
test/test_mem.c

@@ -0,0 +1,12 @@
+#include "mem.h"
+
+int main() {
+    int *p = safeCalloc(1, sizeof(int));
+    *p = 10;
+    safeFree(p);
+
+    p = safeCalloc(1, sizeof(int));
+    *p = 10;
+    safeFree_(p);
+    return 0;
+}

+ 9 - 0
test/test_test_lib.c

@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+extern int num;
+int test(int a);
+
+int main() {
+    printf("num = %d test = %d\n", num, test(num));
+    return 0;
+}