├── .gitignore ├── CMakeLists.txt ├── README.md ├── cmake_uninstall.cmake.in ├── project.yml ├── src ├── CMakeLists.txt ├── cJSON.c ├── cJSON.h ├── j1939decode.c └── j1939decode.h └── test ├── support └── .gitkeep └── test_j1939decode.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.json 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | 3 | # Parse the current version from the version header 4 | file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/src/j1939decode.h" version_defines 5 | REGEX "#define J1939DECODE_VERSION_(MAJOR|MINOR|PATCH)") 6 | foreach(ver ${version_defines}) 7 | if(ver MATCHES "#define J1939DECODE_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") 8 | set(VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "") 9 | endif() 10 | endforeach() 11 | set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) 12 | message(STATUS "j1939decode version: ${VERSION}") 13 | 14 | project(j1939decode 15 | VERSION "${VERSION}" 16 | LANGUAGES C) 17 | 18 | set(CMAKE_C_STANDARD 99) 19 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") 20 | 21 | set(STATIC_LIB static) 22 | set(SHARED_LIB shared) 23 | 24 | # uninstall target 25 | if(NOT TARGET uninstall) 26 | configure_file( 27 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" 28 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 29 | IMMEDIATE @ONLY) 30 | add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) 31 | endif() 32 | 33 | add_subdirectory(src) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # J1939 decode library 2 | 3 | Decode J1939 CAN bus messages into a JSON string representation of what the payload data means based on the SAE standard. 4 | 5 | This library by default will attempt to read the file `J1939db.json` from the current directory to load in the J1939 database. 6 | If this file cannot be read, J1939 decoding will not be possible. 7 | This file name and path can changed by redefining the `J1939DECODE_DB` C macro to point to a different filename. 8 | 9 | ***See below for how to generate the J1939 database file.*** 10 | 11 | ## Building 12 | 13 | Create a build directory relative to project root, run cmake and then make. 14 | 15 | ``` 16 | mkdir -p build 17 | cd build 18 | cmake .. 19 | make -j 20 | ``` 21 | 22 | ## Cleaning 23 | 24 | Remove generated directories: `build/` 25 | 26 | ## Install 27 | 28 | To install first build the library, then run `sudo make install` from within the build directory. 29 | 30 | ## Uninstall 31 | 32 | To uninstall run `sudo make uninstall` from within the build directory. 33 | 34 | ## Testing 35 | 36 | Unit tests make use of the [Unity testing framework](http://www.throwtheswitch.org/unity) which is used by the [Ceedling](http://www.throwtheswitch.org/ceedling) build system. 37 | 38 | To start all tests run `ceedling test:all`. 39 | 40 | ## Library usage 41 | 42 | Call `j1939decode_init()` first _before_ calling `j1939decode_to_json()`. 43 | 44 | When done, call `j1939decode_deinit()` to free memory allocated by `j1939decode_init()` and also remember to free the string pointer returned by `j1939decode_to_json()`. 45 | 46 | ### User-supplied log handler 47 | 48 | `j1939decode_set_log_fn()` can be used to set a user-supplied log handler function. 49 | `j1939decode_set_log_fn()` should be called _before_ calling `j1939decode_init()` since library initialization may generate log messages. 50 | 51 | The replacement log handler should have the following signature: 52 | 53 | ```c 54 | void custom_log_handler(const char * msg); 55 | ``` 56 | 57 | If NULL is supplied for j1939decode_set_log_fn or it is not called at all, then the default logger function will be used, which prints all log messages to stderr. 58 | 59 | ## J1939 database file generation 60 | 61 | The `J1939db.json` database file is a JSON formatted file that contains all of the PGN, SPN, and SA lookup data needed for decoding J1939 messages. 62 | 63 | This database file can be generated using the `create_j1939db-json.py` script provided by [pretty\_j1939](https://github.com/nmfta-repo/pretty_j1939) repository. 64 | A copy of the [SAE J1939 Digital Annex](https://www.sae.org/standards/content/j1939da_201704/) spreadsheet is required. 65 | 66 | For example: 67 | 68 | ``` 69 | create_j1939db-json.py -f J1939DA_201704.xls -w J1939db.json 70 | ``` 71 | 72 | ## JSON format 73 | 74 | The output JSON string generated by `j1939decode_to_json()` contains the following fields. 75 | Where possible, the decoder will attempt to decode the CAN message according to the J1939 protocol spec. 76 | 77 | * "_ID_": CAN identifier 78 | * "_Priority_": message priority 79 | * "_PGN_": parameter group number 80 | * "_PGNName_": parameter group number descriptive name 81 | * "_SA_": source address 82 | * "_SAName_": source address descriptive name 83 | * "_DLC_": data length code 84 | * "_DataRaw_": array of CAN data bytes, MSB first 85 | * "_Decoded_": boolean indicating if J1939 decoding was successful (i.e. one or more SPNs decoded) 86 | * "_SPNs_": object containing all decoded suspect parameter numbers associated with the given PGN 87 | 88 | SPN objects use their SPN number as their name, and contain a sub-object with members specific to that SPN. 89 | 90 | The following members are found within an SPN sub-object. 91 | All members relate to the specific SPN under which they are found. 92 | 93 | * "_Name_": suspect parameter number descriptive name 94 | * "_DataRange_": valid data value range as a human-readable string 95 | * "_OperationalRange_": valid data value range (in decoded data value units) 96 | * "_OperationalHigh_": maximum valid data value (in decoded data value units) 97 | * "_OperationalLow_": minimum valid data value (in decoded data value units) 98 | * "_StartBit_": starting bit number of data within 64 bit data field 99 | * "_SPNLength_": length of data in bits 100 | * "_Resolution_": scaling multiplier 101 | * "_Offset_": linear offset 102 | * "_ValueRaw_": raw data value without resolution and offset applied 103 | * "_ValueDecoded_": decoded data value 104 | * "_Units_": units of the decoded data value 105 | * "_Valid_": boolean indicating if the decoded data value is valid (i.e. is within operational data range) 106 | 107 | Example JSON object for a fully decoded J1939 CAN message: 108 | 109 | ```json 110 | { 111 | "ID": 217056256, 112 | "Priority": 3, 113 | "PGN": 61444, 114 | "PGNName": "Electronic Engine Controller 1", 115 | "SA": 0, 116 | "SAName": "Engine #1", 117 | "DLC": 8, 118 | "DataRaw": [240, 125, 131, 131, 23, 0, 255, 131], 119 | "Decoded": true, 120 | "SPNs": { 121 | "190": { 122 | "Name": "Engine Speed", 123 | "DataRange": "0 to 8,031.875 rpm", 124 | "OperationalRange": "", 125 | "OperationalHigh": 8031.875, 126 | "OperationalLow": 0, 127 | "StartBit": 24, 128 | "SPNLength": 16, 129 | "Resolution": 0.125, 130 | "Offset": 0, 131 | "ValueRaw": 6019, 132 | "ValueDecoded": 752.375, 133 | "Units": "rpm", 134 | "Valid": true 135 | }, 136 | ... 137 | } 138 | } 139 | ``` 140 | 141 | The ellipses shows where additional SPN sub-objects would appear. 142 | 143 | #### Important notes: 144 | 145 | * "_Decoded_" boolean should be checked before using the members in the SPN sub-object(s); if it is _false_, then the "_SPNs_" object may not even exist 146 | * "_Valid_" boolean from within an SPN sub-object should be checked before using the decoded data value; most often if the decoded data value is not valid, it will be set to the string "Not Available" 147 | * "_PGNName_" and "_SPNName_" values are found using a simple lookup table based on their number values; if the decoded CAN message is not a real J1939 message these members may still exist if the CAN identifier bits happen to match a value found in the J1939 protocol spec although this does not necessarily mean that the CAN message was a valid J1939 message 148 | 149 | ## TODO 150 | 151 | * Improve speed of J1939 DB lookup. 152 | At the moment the processing speed to decode a message is not great and at a high message rate it can lag behind if decoding in real-time. 153 | The `hash-tables` branch uses C++ container objects (hash maps) to improve the lookup speed however it is no longer pure C code. 154 | 155 | -------------------------------------------------------------------------------- /cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") 2 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") 3 | endif() 4 | 5 | file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) 6 | string(REGEX REPLACE "\n" ";" files "${files}") 7 | foreach(file ${files}) 8 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 9 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 10 | exec_program( 11 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 12 | OUTPUT_VARIABLE rm_out 13 | RETURN_VALUE rm_retval 14 | ) 15 | if(NOT "${rm_retval}" STREQUAL 0) 16 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 17 | endif() 18 | else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 19 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.") 20 | endif() 21 | endforeach() -------------------------------------------------------------------------------- /project.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Notes: 4 | # Sample project C code is not presently written to produce a release artifact. 5 | # As such, release build options are disabled. 6 | # This sample, therefore, only demonstrates running a collection of unit tests. 7 | 8 | :project: 9 | :use_exceptions: FALSE 10 | :use_test_preprocessor: TRUE 11 | :use_auxiliary_dependencies: TRUE 12 | :build_root: build 13 | # :release_build: TRUE 14 | :test_file_prefix: test_ 15 | :which_ceedling: gem 16 | :default_tasks: 17 | - test:all 18 | 19 | #:test_build: 20 | # :use_assembly: TRUE 21 | 22 | #:release_build: 23 | # :output: MyApp.out 24 | # :use_assembly: FALSE 25 | 26 | :environment: 27 | 28 | :extension: 29 | :executable: .out 30 | 31 | :paths: 32 | :test: 33 | - +:test/** 34 | - -:test/support 35 | :source: 36 | - src/** 37 | :support: 38 | - test/support 39 | 40 | :defines: 41 | # in order to add common defines: 42 | # 1) remove the trailing [] from the :common: section 43 | # 2) add entries to the :common: section (e.g. :test: has TEST defined) 44 | :common: &common_defines [] 45 | :test: 46 | - *common_defines 47 | - TEST 48 | :test_preprocess: 49 | - *common_defines 50 | - TEST 51 | 52 | :cmock: 53 | :mock_prefix: mock_ 54 | :when_no_prototypes: :warn 55 | :enforce_strict_ordering: TRUE 56 | :plugins: 57 | - :ignore 58 | - :callback 59 | :treat_as: 60 | uint8: HEX8 61 | uint16: HEX16 62 | uint32: UINT32 63 | int8: INT8 64 | bool: UINT8 65 | 66 | # Add -gcov to the plugins list to make sure of the gcov plugin 67 | # You will need to have gcov and gcovr both installed to make it work. 68 | # For more information on these options, see docs in plugins/gcov 69 | :gcov: 70 | :html_report: TRUE 71 | :html_report_type: detailed 72 | :html_medium_threshold: 75 73 | :html_high_threshold: 90 74 | :xml_report: FALSE 75 | 76 | #:tools: 77 | # Ceedling defaults to using gcc for compiling, linking, etc. 78 | # As [:tools] is blank, gcc will be used (so long as it's in your system path) 79 | # See documentation to configure a given toolchain for use 80 | 81 | # LIBRARIES 82 | # These libraries are automatically injected into the build process. Those specified as 83 | # common will be used in all types of builds. Otherwise, libraries can be injected in just 84 | # tests or releases. These options are MERGED with the options in supplemental yaml files. 85 | :libraries: 86 | :placement: :end 87 | :flag: "${1}" # or "-L ${1}" for example 88 | :test: [] 89 | :release: [] 90 | 91 | :plugins: 92 | :load_paths: 93 | - "#{Ceedling.load_path}" 94 | :enabled: 95 | - stdout_pretty_tests_report 96 | - module_generator 97 | ... 98 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES 2 | j1939decode.c j1939decode.h 3 | cJSON.c cJSON.h 4 | ) 5 | 6 | add_library(${STATIC_LIB} STATIC ${SOURCES}) 7 | add_library(${SHARED_LIB} SHARED ${SOURCES}) 8 | 9 | set_target_properties(${STATIC_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME} CLEAN_DIRECT_OUTPUT 1) 10 | set_target_properties(${SHARED_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME} CLEAN_DIRECT_OUTPUT 1) 11 | 12 | # Install static lib 13 | install(TARGETS ${STATIC_LIB} 14 | ARCHIVE DESTINATION lib) 15 | 16 | # Install shared lib 17 | install(TARGETS ${SHARED_LIB} 18 | LIBRARY DESTINATION lib) 19 | 20 | # Install headers 21 | set(HEADERS j1939decode.h) 22 | set(HEADER_PATH ${CMAKE_PROJECT_NAME}) 23 | install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${HEADER_PATH}) -------------------------------------------------------------------------------- /src/cJSON.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* cJSON */ 24 | /* JSON parser in C. */ 25 | 26 | /* disable warnings about old C89 functions in MSVC */ 27 | #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) 28 | #define _CRT_SECURE_NO_DEPRECATE 29 | #endif 30 | 31 | #ifdef __GNUC__ 32 | #pragma GCC visibility push(default) 33 | #endif 34 | #if defined(_MSC_VER) 35 | #pragma warning (push) 36 | /* disable warning about single line comments in system headers */ 37 | #pragma warning (disable : 4001) 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #ifdef ENABLE_LOCALES 48 | #include 49 | #endif 50 | 51 | #if defined(_MSC_VER) 52 | #pragma warning (pop) 53 | #endif 54 | #ifdef __GNUC__ 55 | #pragma GCC visibility pop 56 | #endif 57 | 58 | #include "cJSON.h" 59 | 60 | /* define our own boolean type */ 61 | #ifdef true 62 | #undef true 63 | #endif 64 | #define true ((cJSON_bool)1) 65 | 66 | #ifdef false 67 | #undef false 68 | #endif 69 | #define false ((cJSON_bool)0) 70 | 71 | typedef struct { 72 | const unsigned char *json; 73 | size_t position; 74 | } error; 75 | static error global_error = { NULL, 0 }; 76 | 77 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) 78 | { 79 | return (const char*) (global_error.json + global_error.position); 80 | } 81 | 82 | CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { 83 | if (!cJSON_IsString(item)) { 84 | return NULL; 85 | } 86 | 87 | return item->valuestring; 88 | } 89 | 90 | /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ 91 | #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 12) 92 | #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. 93 | #endif 94 | 95 | CJSON_PUBLIC(const char*) cJSON_Version(void) 96 | { 97 | static char version[15]; 98 | sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); 99 | 100 | return version; 101 | } 102 | 103 | /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ 104 | static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) 105 | { 106 | if ((string1 == NULL) || (string2 == NULL)) 107 | { 108 | return 1; 109 | } 110 | 111 | if (string1 == string2) 112 | { 113 | return 0; 114 | } 115 | 116 | for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) 117 | { 118 | if (*string1 == '\0') 119 | { 120 | return 0; 121 | } 122 | } 123 | 124 | return tolower(*string1) - tolower(*string2); 125 | } 126 | 127 | typedef struct internal_hooks 128 | { 129 | void *(CJSON_CDECL *allocate)(size_t size); 130 | void (CJSON_CDECL *deallocate)(void *pointer); 131 | void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); 132 | } internal_hooks; 133 | 134 | #if defined(_MSC_VER) 135 | /* work around MSVC error C2322: '...' address of dillimport '...' is not static */ 136 | static void * CJSON_CDECL internal_malloc(size_t size) 137 | { 138 | return malloc(size); 139 | } 140 | static void CJSON_CDECL internal_free(void *pointer) 141 | { 142 | free(pointer); 143 | } 144 | static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) 145 | { 146 | return realloc(pointer, size); 147 | } 148 | #else 149 | #define internal_malloc malloc 150 | #define internal_free free 151 | #define internal_realloc realloc 152 | #endif 153 | 154 | /* strlen of character literals resolved at compile time */ 155 | #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) 156 | 157 | static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; 158 | 159 | static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) 160 | { 161 | size_t length = 0; 162 | unsigned char *copy = NULL; 163 | 164 | if (string == NULL) 165 | { 166 | return NULL; 167 | } 168 | 169 | length = strlen((const char*)string) + sizeof(""); 170 | copy = (unsigned char*)hooks->allocate(length); 171 | if (copy == NULL) 172 | { 173 | return NULL; 174 | } 175 | memcpy(copy, string, length); 176 | 177 | return copy; 178 | } 179 | 180 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) 181 | { 182 | if (hooks == NULL) 183 | { 184 | /* Reset hooks */ 185 | global_hooks.allocate = malloc; 186 | global_hooks.deallocate = free; 187 | global_hooks.reallocate = realloc; 188 | return; 189 | } 190 | 191 | global_hooks.allocate = malloc; 192 | if (hooks->malloc_fn != NULL) 193 | { 194 | global_hooks.allocate = hooks->malloc_fn; 195 | } 196 | 197 | global_hooks.deallocate = free; 198 | if (hooks->free_fn != NULL) 199 | { 200 | global_hooks.deallocate = hooks->free_fn; 201 | } 202 | 203 | /* use realloc only if both free and malloc are used */ 204 | global_hooks.reallocate = NULL; 205 | if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) 206 | { 207 | global_hooks.reallocate = realloc; 208 | } 209 | } 210 | 211 | /* Internal constructor. */ 212 | static cJSON *cJSON_New_Item(const internal_hooks * const hooks) 213 | { 214 | cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); 215 | if (node) 216 | { 217 | memset(node, '\0', sizeof(cJSON)); 218 | } 219 | 220 | return node; 221 | } 222 | 223 | /* Delete a cJSON structure. */ 224 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) 225 | { 226 | cJSON *next = NULL; 227 | while (item != NULL) 228 | { 229 | next = item->next; 230 | if (!(item->type & cJSON_IsReference) && (item->child != NULL)) 231 | { 232 | cJSON_Delete(item->child); 233 | } 234 | if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) 235 | { 236 | global_hooks.deallocate(item->valuestring); 237 | } 238 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) 239 | { 240 | global_hooks.deallocate(item->string); 241 | } 242 | global_hooks.deallocate(item); 243 | item = next; 244 | } 245 | } 246 | 247 | /* get the decimal point character of the current locale */ 248 | static unsigned char get_decimal_point(void) 249 | { 250 | #ifdef ENABLE_LOCALES 251 | struct lconv *lconv = localeconv(); 252 | return (unsigned char) lconv->decimal_point[0]; 253 | #else 254 | return '.'; 255 | #endif 256 | } 257 | 258 | typedef struct 259 | { 260 | const unsigned char *content; 261 | size_t length; 262 | size_t offset; 263 | size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ 264 | internal_hooks hooks; 265 | } parse_buffer; 266 | 267 | /* check if the given size is left to read in a given parse buffer (starting with 1) */ 268 | #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) 269 | /* check if the buffer can be accessed at the given index (starting with 0) */ 270 | #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) 271 | #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) 272 | /* get a pointer to the buffer at the position */ 273 | #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) 274 | 275 | /* Parse the input text to generate a number, and populate the result into item. */ 276 | static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) 277 | { 278 | double number = 0; 279 | unsigned char *after_end = NULL; 280 | unsigned char number_c_string[64]; 281 | unsigned char decimal_point = get_decimal_point(); 282 | size_t i = 0; 283 | 284 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) 285 | { 286 | return false; 287 | } 288 | 289 | /* copy the number into a temporary buffer and replace '.' with the decimal point 290 | * of the current locale (for strtod) 291 | * This also takes care of '\0' not necessarily being available for marking the end of the input */ 292 | for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) 293 | { 294 | switch (buffer_at_offset(input_buffer)[i]) 295 | { 296 | case '0': 297 | case '1': 298 | case '2': 299 | case '3': 300 | case '4': 301 | case '5': 302 | case '6': 303 | case '7': 304 | case '8': 305 | case '9': 306 | case '+': 307 | case '-': 308 | case 'e': 309 | case 'E': 310 | number_c_string[i] = buffer_at_offset(input_buffer)[i]; 311 | break; 312 | 313 | case '.': 314 | number_c_string[i] = decimal_point; 315 | break; 316 | 317 | default: 318 | goto loop_end; 319 | } 320 | } 321 | loop_end: 322 | number_c_string[i] = '\0'; 323 | 324 | number = strtod((const char*)number_c_string, (char**)&after_end); 325 | if (number_c_string == after_end) 326 | { 327 | return false; /* parse_error */ 328 | } 329 | 330 | item->valuedouble = number; 331 | 332 | /* use saturation in case of overflow */ 333 | if (number >= INT_MAX) 334 | { 335 | item->valueint = INT_MAX; 336 | } 337 | else if (number <= (double)INT_MIN) 338 | { 339 | item->valueint = INT_MIN; 340 | } 341 | else 342 | { 343 | item->valueint = (int)number; 344 | } 345 | 346 | item->type = cJSON_Number; 347 | 348 | input_buffer->offset += (size_t)(after_end - number_c_string); 349 | return true; 350 | } 351 | 352 | /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ 353 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) 354 | { 355 | if (number >= INT_MAX) 356 | { 357 | object->valueint = INT_MAX; 358 | } 359 | else if (number <= (double)INT_MIN) 360 | { 361 | object->valueint = INT_MIN; 362 | } 363 | else 364 | { 365 | object->valueint = (int)number; 366 | } 367 | 368 | return object->valuedouble = number; 369 | } 370 | 371 | typedef struct 372 | { 373 | unsigned char *buffer; 374 | size_t length; 375 | size_t offset; 376 | size_t depth; /* current nesting depth (for formatted printing) */ 377 | cJSON_bool noalloc; 378 | cJSON_bool format; /* is this print a formatted print */ 379 | internal_hooks hooks; 380 | } printbuffer; 381 | 382 | /* realloc printbuffer if necessary to have at least "needed" bytes more */ 383 | static unsigned char* ensure(printbuffer * const p, size_t needed) 384 | { 385 | unsigned char *newbuffer = NULL; 386 | size_t newsize = 0; 387 | 388 | if ((p == NULL) || (p->buffer == NULL)) 389 | { 390 | return NULL; 391 | } 392 | 393 | if ((p->length > 0) && (p->offset >= p->length)) 394 | { 395 | /* make sure that offset is valid */ 396 | return NULL; 397 | } 398 | 399 | if (needed > INT_MAX) 400 | { 401 | /* sizes bigger than INT_MAX are currently not supported */ 402 | return NULL; 403 | } 404 | 405 | needed += p->offset + 1; 406 | if (needed <= p->length) 407 | { 408 | return p->buffer + p->offset; 409 | } 410 | 411 | if (p->noalloc) { 412 | return NULL; 413 | } 414 | 415 | /* calculate new buffer size */ 416 | if (needed > (INT_MAX / 2)) 417 | { 418 | /* overflow of int, use INT_MAX if possible */ 419 | if (needed <= INT_MAX) 420 | { 421 | newsize = INT_MAX; 422 | } 423 | else 424 | { 425 | return NULL; 426 | } 427 | } 428 | else 429 | { 430 | newsize = needed * 2; 431 | } 432 | 433 | if (p->hooks.reallocate != NULL) 434 | { 435 | /* reallocate with realloc if available */ 436 | newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); 437 | if (newbuffer == NULL) 438 | { 439 | p->hooks.deallocate(p->buffer); 440 | p->length = 0; 441 | p->buffer = NULL; 442 | 443 | return NULL; 444 | } 445 | } 446 | else 447 | { 448 | /* otherwise reallocate manually */ 449 | newbuffer = (unsigned char*)p->hooks.allocate(newsize); 450 | if (!newbuffer) 451 | { 452 | p->hooks.deallocate(p->buffer); 453 | p->length = 0; 454 | p->buffer = NULL; 455 | 456 | return NULL; 457 | } 458 | if (newbuffer) 459 | { 460 | memcpy(newbuffer, p->buffer, p->offset + 1); 461 | } 462 | p->hooks.deallocate(p->buffer); 463 | } 464 | p->length = newsize; 465 | p->buffer = newbuffer; 466 | 467 | return newbuffer + p->offset; 468 | } 469 | 470 | /* calculate the new length of the string in a printbuffer and update the offset */ 471 | static void update_offset(printbuffer * const buffer) 472 | { 473 | const unsigned char *buffer_pointer = NULL; 474 | if ((buffer == NULL) || (buffer->buffer == NULL)) 475 | { 476 | return; 477 | } 478 | buffer_pointer = buffer->buffer + buffer->offset; 479 | 480 | buffer->offset += strlen((const char*)buffer_pointer); 481 | } 482 | 483 | /* Render the number nicely from the given item into a string. */ 484 | static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) 485 | { 486 | unsigned char *output_pointer = NULL; 487 | double d = item->valuedouble; 488 | int length = 0; 489 | size_t i = 0; 490 | unsigned char number_buffer[26]; /* temporary buffer to print the number into */ 491 | unsigned char decimal_point = get_decimal_point(); 492 | double test; 493 | 494 | if (output_buffer == NULL) 495 | { 496 | return false; 497 | } 498 | 499 | /* This checks for NaN and Infinity */ 500 | if ((d * 0) != 0) 501 | { 502 | length = sprintf((char*)number_buffer, "null"); 503 | } 504 | else 505 | { 506 | /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ 507 | length = sprintf((char*)number_buffer, "%1.15g", d); 508 | 509 | /* Check whether the original double can be recovered */ 510 | if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) 511 | { 512 | /* If not, print with 17 decimal places of precision */ 513 | length = sprintf((char*)number_buffer, "%1.17g", d); 514 | } 515 | } 516 | 517 | /* sprintf failed or buffer overrun occurred */ 518 | if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) 519 | { 520 | return false; 521 | } 522 | 523 | /* reserve appropriate space in the output */ 524 | output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); 525 | if (output_pointer == NULL) 526 | { 527 | return false; 528 | } 529 | 530 | /* copy the printed number to the output and replace locale 531 | * dependent decimal point with '.' */ 532 | for (i = 0; i < ((size_t)length); i++) 533 | { 534 | if (number_buffer[i] == decimal_point) 535 | { 536 | output_pointer[i] = '.'; 537 | continue; 538 | } 539 | 540 | output_pointer[i] = number_buffer[i]; 541 | } 542 | output_pointer[i] = '\0'; 543 | 544 | output_buffer->offset += (size_t)length; 545 | 546 | return true; 547 | } 548 | 549 | /* parse 4 digit hexadecimal number */ 550 | static unsigned parse_hex4(const unsigned char * const input) 551 | { 552 | unsigned int h = 0; 553 | size_t i = 0; 554 | 555 | for (i = 0; i < 4; i++) 556 | { 557 | /* parse digit */ 558 | if ((input[i] >= '0') && (input[i] <= '9')) 559 | { 560 | h += (unsigned int) input[i] - '0'; 561 | } 562 | else if ((input[i] >= 'A') && (input[i] <= 'F')) 563 | { 564 | h += (unsigned int) 10 + input[i] - 'A'; 565 | } 566 | else if ((input[i] >= 'a') && (input[i] <= 'f')) 567 | { 568 | h += (unsigned int) 10 + input[i] - 'a'; 569 | } 570 | else /* invalid */ 571 | { 572 | return 0; 573 | } 574 | 575 | if (i < 3) 576 | { 577 | /* shift left to make place for the next nibble */ 578 | h = h << 4; 579 | } 580 | } 581 | 582 | return h; 583 | } 584 | 585 | /* converts a UTF-16 literal to UTF-8 586 | * A literal can be one or two sequences of the form \uXXXX */ 587 | static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) 588 | { 589 | long unsigned int codepoint = 0; 590 | unsigned int first_code = 0; 591 | const unsigned char *first_sequence = input_pointer; 592 | unsigned char utf8_length = 0; 593 | unsigned char utf8_position = 0; 594 | unsigned char sequence_length = 0; 595 | unsigned char first_byte_mark = 0; 596 | 597 | if ((input_end - first_sequence) < 6) 598 | { 599 | /* input ends unexpectedly */ 600 | goto fail; 601 | } 602 | 603 | /* get the first utf16 sequence */ 604 | first_code = parse_hex4(first_sequence + 2); 605 | 606 | /* check that the code is valid */ 607 | if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) 608 | { 609 | goto fail; 610 | } 611 | 612 | /* UTF16 surrogate pair */ 613 | if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) 614 | { 615 | const unsigned char *second_sequence = first_sequence + 6; 616 | unsigned int second_code = 0; 617 | sequence_length = 12; /* \uXXXX\uXXXX */ 618 | 619 | if ((input_end - second_sequence) < 6) 620 | { 621 | /* input ends unexpectedly */ 622 | goto fail; 623 | } 624 | 625 | if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) 626 | { 627 | /* missing second half of the surrogate pair */ 628 | goto fail; 629 | } 630 | 631 | /* get the second utf16 sequence */ 632 | second_code = parse_hex4(second_sequence + 2); 633 | /* check that the code is valid */ 634 | if ((second_code < 0xDC00) || (second_code > 0xDFFF)) 635 | { 636 | /* invalid second half of the surrogate pair */ 637 | goto fail; 638 | } 639 | 640 | 641 | /* calculate the unicode codepoint from the surrogate pair */ 642 | codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); 643 | } 644 | else 645 | { 646 | sequence_length = 6; /* \uXXXX */ 647 | codepoint = first_code; 648 | } 649 | 650 | /* encode as UTF-8 651 | * takes at maximum 4 bytes to encode: 652 | * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ 653 | if (codepoint < 0x80) 654 | { 655 | /* normal ascii, encoding 0xxxxxxx */ 656 | utf8_length = 1; 657 | } 658 | else if (codepoint < 0x800) 659 | { 660 | /* two bytes, encoding 110xxxxx 10xxxxxx */ 661 | utf8_length = 2; 662 | first_byte_mark = 0xC0; /* 11000000 */ 663 | } 664 | else if (codepoint < 0x10000) 665 | { 666 | /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ 667 | utf8_length = 3; 668 | first_byte_mark = 0xE0; /* 11100000 */ 669 | } 670 | else if (codepoint <= 0x10FFFF) 671 | { 672 | /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ 673 | utf8_length = 4; 674 | first_byte_mark = 0xF0; /* 11110000 */ 675 | } 676 | else 677 | { 678 | /* invalid unicode codepoint */ 679 | goto fail; 680 | } 681 | 682 | /* encode as utf8 */ 683 | for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) 684 | { 685 | /* 10xxxxxx */ 686 | (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); 687 | codepoint >>= 6; 688 | } 689 | /* encode first byte */ 690 | if (utf8_length > 1) 691 | { 692 | (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); 693 | } 694 | else 695 | { 696 | (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); 697 | } 698 | 699 | *output_pointer += utf8_length; 700 | 701 | return sequence_length; 702 | 703 | fail: 704 | return 0; 705 | } 706 | 707 | /* Parse the input text into an unescaped cinput, and populate item. */ 708 | static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) 709 | { 710 | const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; 711 | const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; 712 | unsigned char *output_pointer = NULL; 713 | unsigned char *output = NULL; 714 | 715 | /* not a string */ 716 | if (buffer_at_offset(input_buffer)[0] != '\"') 717 | { 718 | goto fail; 719 | } 720 | 721 | { 722 | /* calculate approximate size of the output (overestimate) */ 723 | size_t allocation_length = 0; 724 | size_t skipped_bytes = 0; 725 | while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) 726 | { 727 | /* is escape sequence */ 728 | if (input_end[0] == '\\') 729 | { 730 | if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) 731 | { 732 | /* prevent buffer overflow when last input character is a backslash */ 733 | goto fail; 734 | } 735 | skipped_bytes++; 736 | input_end++; 737 | } 738 | input_end++; 739 | } 740 | if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) 741 | { 742 | goto fail; /* string ended unexpectedly */ 743 | } 744 | 745 | /* This is at most how much we need for the output */ 746 | allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; 747 | output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); 748 | if (output == NULL) 749 | { 750 | goto fail; /* allocation failure */ 751 | } 752 | } 753 | 754 | output_pointer = output; 755 | /* loop through the string literal */ 756 | while (input_pointer < input_end) 757 | { 758 | if (*input_pointer != '\\') 759 | { 760 | *output_pointer++ = *input_pointer++; 761 | } 762 | /* escape sequence */ 763 | else 764 | { 765 | unsigned char sequence_length = 2; 766 | if ((input_end - input_pointer) < 1) 767 | { 768 | goto fail; 769 | } 770 | 771 | switch (input_pointer[1]) 772 | { 773 | case 'b': 774 | *output_pointer++ = '\b'; 775 | break; 776 | case 'f': 777 | *output_pointer++ = '\f'; 778 | break; 779 | case 'n': 780 | *output_pointer++ = '\n'; 781 | break; 782 | case 'r': 783 | *output_pointer++ = '\r'; 784 | break; 785 | case 't': 786 | *output_pointer++ = '\t'; 787 | break; 788 | case '\"': 789 | case '\\': 790 | case '/': 791 | *output_pointer++ = input_pointer[1]; 792 | break; 793 | 794 | /* UTF-16 literal */ 795 | case 'u': 796 | sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); 797 | if (sequence_length == 0) 798 | { 799 | /* failed to convert UTF16-literal to UTF-8 */ 800 | goto fail; 801 | } 802 | break; 803 | 804 | default: 805 | goto fail; 806 | } 807 | input_pointer += sequence_length; 808 | } 809 | } 810 | 811 | /* zero terminate the output */ 812 | *output_pointer = '\0'; 813 | 814 | item->type = cJSON_String; 815 | item->valuestring = (char*)output; 816 | 817 | input_buffer->offset = (size_t) (input_end - input_buffer->content); 818 | input_buffer->offset++; 819 | 820 | return true; 821 | 822 | fail: 823 | if (output != NULL) 824 | { 825 | input_buffer->hooks.deallocate(output); 826 | } 827 | 828 | if (input_pointer != NULL) 829 | { 830 | input_buffer->offset = (size_t)(input_pointer - input_buffer->content); 831 | } 832 | 833 | return false; 834 | } 835 | 836 | /* Render the cstring provided to an escaped version that can be printed. */ 837 | static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) 838 | { 839 | const unsigned char *input_pointer = NULL; 840 | unsigned char *output = NULL; 841 | unsigned char *output_pointer = NULL; 842 | size_t output_length = 0; 843 | /* numbers of additional characters needed for escaping */ 844 | size_t escape_characters = 0; 845 | 846 | if (output_buffer == NULL) 847 | { 848 | return false; 849 | } 850 | 851 | /* empty string */ 852 | if (input == NULL) 853 | { 854 | output = ensure(output_buffer, sizeof("\"\"")); 855 | if (output == NULL) 856 | { 857 | return false; 858 | } 859 | strcpy((char*)output, "\"\""); 860 | 861 | return true; 862 | } 863 | 864 | /* set "flag" to 1 if something needs to be escaped */ 865 | for (input_pointer = input; *input_pointer; input_pointer++) 866 | { 867 | switch (*input_pointer) 868 | { 869 | case '\"': 870 | case '\\': 871 | case '\b': 872 | case '\f': 873 | case '\n': 874 | case '\r': 875 | case '\t': 876 | /* one character escape sequence */ 877 | escape_characters++; 878 | break; 879 | default: 880 | if (*input_pointer < 32) 881 | { 882 | /* UTF-16 escape sequence uXXXX */ 883 | escape_characters += 5; 884 | } 885 | break; 886 | } 887 | } 888 | output_length = (size_t)(input_pointer - input) + escape_characters; 889 | 890 | output = ensure(output_buffer, output_length + sizeof("\"\"")); 891 | if (output == NULL) 892 | { 893 | return false; 894 | } 895 | 896 | /* no characters have to be escaped */ 897 | if (escape_characters == 0) 898 | { 899 | output[0] = '\"'; 900 | memcpy(output + 1, input, output_length); 901 | output[output_length + 1] = '\"'; 902 | output[output_length + 2] = '\0'; 903 | 904 | return true; 905 | } 906 | 907 | output[0] = '\"'; 908 | output_pointer = output + 1; 909 | /* copy the string */ 910 | for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) 911 | { 912 | if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) 913 | { 914 | /* normal character, copy */ 915 | *output_pointer = *input_pointer; 916 | } 917 | else 918 | { 919 | /* character needs to be escaped */ 920 | *output_pointer++ = '\\'; 921 | switch (*input_pointer) 922 | { 923 | case '\\': 924 | *output_pointer = '\\'; 925 | break; 926 | case '\"': 927 | *output_pointer = '\"'; 928 | break; 929 | case '\b': 930 | *output_pointer = 'b'; 931 | break; 932 | case '\f': 933 | *output_pointer = 'f'; 934 | break; 935 | case '\n': 936 | *output_pointer = 'n'; 937 | break; 938 | case '\r': 939 | *output_pointer = 'r'; 940 | break; 941 | case '\t': 942 | *output_pointer = 't'; 943 | break; 944 | default: 945 | /* escape and print as unicode codepoint */ 946 | sprintf((char*)output_pointer, "u%04x", *input_pointer); 947 | output_pointer += 4; 948 | break; 949 | } 950 | } 951 | } 952 | output[output_length + 1] = '\"'; 953 | output[output_length + 2] = '\0'; 954 | 955 | return true; 956 | } 957 | 958 | /* Invoke print_string_ptr (which is useful) on an item. */ 959 | static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) 960 | { 961 | return print_string_ptr((unsigned char*)item->valuestring, p); 962 | } 963 | 964 | /* Predeclare these prototypes. */ 965 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); 966 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); 967 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); 968 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); 969 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); 970 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); 971 | 972 | /* Utility to jump whitespace and cr/lf */ 973 | static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) 974 | { 975 | if ((buffer == NULL) || (buffer->content == NULL)) 976 | { 977 | return NULL; 978 | } 979 | 980 | while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) 981 | { 982 | buffer->offset++; 983 | } 984 | 985 | if (buffer->offset == buffer->length) 986 | { 987 | buffer->offset--; 988 | } 989 | 990 | return buffer; 991 | } 992 | 993 | /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ 994 | static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) 995 | { 996 | if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) 997 | { 998 | return NULL; 999 | } 1000 | 1001 | if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) 1002 | { 1003 | buffer->offset += 3; 1004 | } 1005 | 1006 | return buffer; 1007 | } 1008 | 1009 | /* Parse an object - create a new root, and populate. */ 1010 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) 1011 | { 1012 | parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; 1013 | cJSON *item = NULL; 1014 | 1015 | /* reset error position */ 1016 | global_error.json = NULL; 1017 | global_error.position = 0; 1018 | 1019 | if (value == NULL) 1020 | { 1021 | goto fail; 1022 | } 1023 | 1024 | buffer.content = (const unsigned char*)value; 1025 | buffer.length = strlen((const char*)value) + sizeof(""); 1026 | buffer.offset = 0; 1027 | buffer.hooks = global_hooks; 1028 | 1029 | item = cJSON_New_Item(&global_hooks); 1030 | if (item == NULL) /* memory fail */ 1031 | { 1032 | goto fail; 1033 | } 1034 | 1035 | if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) 1036 | { 1037 | /* parse failure. ep is set. */ 1038 | goto fail; 1039 | } 1040 | 1041 | /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ 1042 | if (require_null_terminated) 1043 | { 1044 | buffer_skip_whitespace(&buffer); 1045 | if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') 1046 | { 1047 | goto fail; 1048 | } 1049 | } 1050 | if (return_parse_end) 1051 | { 1052 | *return_parse_end = (const char*)buffer_at_offset(&buffer); 1053 | } 1054 | 1055 | return item; 1056 | 1057 | fail: 1058 | if (item != NULL) 1059 | { 1060 | cJSON_Delete(item); 1061 | } 1062 | 1063 | if (value != NULL) 1064 | { 1065 | error local_error; 1066 | local_error.json = (const unsigned char*)value; 1067 | local_error.position = 0; 1068 | 1069 | if (buffer.offset < buffer.length) 1070 | { 1071 | local_error.position = buffer.offset; 1072 | } 1073 | else if (buffer.length > 0) 1074 | { 1075 | local_error.position = buffer.length - 1; 1076 | } 1077 | 1078 | if (return_parse_end != NULL) 1079 | { 1080 | *return_parse_end = (const char*)local_error.json + local_error.position; 1081 | } 1082 | 1083 | global_error = local_error; 1084 | } 1085 | 1086 | return NULL; 1087 | } 1088 | 1089 | /* Default options for cJSON_Parse */ 1090 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) 1091 | { 1092 | return cJSON_ParseWithOpts(value, 0, 0); 1093 | } 1094 | 1095 | #define cjson_min(a, b) ((a < b) ? a : b) 1096 | 1097 | static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) 1098 | { 1099 | static const size_t default_buffer_size = 256; 1100 | printbuffer buffer[1]; 1101 | unsigned char *printed = NULL; 1102 | 1103 | memset(buffer, 0, sizeof(buffer)); 1104 | 1105 | /* create buffer */ 1106 | buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); 1107 | buffer->length = default_buffer_size; 1108 | buffer->format = format; 1109 | buffer->hooks = *hooks; 1110 | if (buffer->buffer == NULL) 1111 | { 1112 | goto fail; 1113 | } 1114 | 1115 | /* print the value */ 1116 | if (!print_value(item, buffer)) 1117 | { 1118 | goto fail; 1119 | } 1120 | update_offset(buffer); 1121 | 1122 | /* check if reallocate is available */ 1123 | if (hooks->reallocate != NULL) 1124 | { 1125 | printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); 1126 | if (printed == NULL) { 1127 | goto fail; 1128 | } 1129 | buffer->buffer = NULL; 1130 | } 1131 | else /* otherwise copy the JSON over to a new buffer */ 1132 | { 1133 | printed = (unsigned char*) hooks->allocate(buffer->offset + 1); 1134 | if (printed == NULL) 1135 | { 1136 | goto fail; 1137 | } 1138 | memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); 1139 | printed[buffer->offset] = '\0'; /* just to be sure */ 1140 | 1141 | /* free the buffer */ 1142 | hooks->deallocate(buffer->buffer); 1143 | } 1144 | 1145 | return printed; 1146 | 1147 | fail: 1148 | if (buffer->buffer != NULL) 1149 | { 1150 | hooks->deallocate(buffer->buffer); 1151 | } 1152 | 1153 | if (printed != NULL) 1154 | { 1155 | hooks->deallocate(printed); 1156 | } 1157 | 1158 | return NULL; 1159 | } 1160 | 1161 | /* Render a cJSON item/entity/structure to text. */ 1162 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) 1163 | { 1164 | return (char*)print(item, true, &global_hooks); 1165 | } 1166 | 1167 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) 1168 | { 1169 | return (char*)print(item, false, &global_hooks); 1170 | } 1171 | 1172 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) 1173 | { 1174 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; 1175 | 1176 | if (prebuffer < 0) 1177 | { 1178 | return NULL; 1179 | } 1180 | 1181 | p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); 1182 | if (!p.buffer) 1183 | { 1184 | return NULL; 1185 | } 1186 | 1187 | p.length = (size_t)prebuffer; 1188 | p.offset = 0; 1189 | p.noalloc = false; 1190 | p.format = fmt; 1191 | p.hooks = global_hooks; 1192 | 1193 | if (!print_value(item, &p)) 1194 | { 1195 | global_hooks.deallocate(p.buffer); 1196 | return NULL; 1197 | } 1198 | 1199 | return (char*)p.buffer; 1200 | } 1201 | 1202 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) 1203 | { 1204 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; 1205 | 1206 | if ((len < 0) || (buf == NULL)) 1207 | { 1208 | return false; 1209 | } 1210 | 1211 | p.buffer = (unsigned char*)buf; 1212 | p.length = (size_t)len; 1213 | p.offset = 0; 1214 | p.noalloc = true; 1215 | p.format = fmt; 1216 | p.hooks = global_hooks; 1217 | 1218 | return print_value(item, &p); 1219 | } 1220 | 1221 | /* Parser core - when encountering text, process appropriately. */ 1222 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) 1223 | { 1224 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) 1225 | { 1226 | return false; /* no input */ 1227 | } 1228 | 1229 | /* parse the different types of values */ 1230 | /* null */ 1231 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) 1232 | { 1233 | item->type = cJSON_NULL; 1234 | input_buffer->offset += 4; 1235 | return true; 1236 | } 1237 | /* false */ 1238 | if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) 1239 | { 1240 | item->type = cJSON_False; 1241 | input_buffer->offset += 5; 1242 | return true; 1243 | } 1244 | /* true */ 1245 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) 1246 | { 1247 | item->type = cJSON_True; 1248 | item->valueint = 1; 1249 | input_buffer->offset += 4; 1250 | return true; 1251 | } 1252 | /* string */ 1253 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) 1254 | { 1255 | return parse_string(item, input_buffer); 1256 | } 1257 | /* number */ 1258 | 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')))) 1259 | { 1260 | return parse_number(item, input_buffer); 1261 | } 1262 | /* array */ 1263 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) 1264 | { 1265 | return parse_array(item, input_buffer); 1266 | } 1267 | /* object */ 1268 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) 1269 | { 1270 | return parse_object(item, input_buffer); 1271 | } 1272 | 1273 | return false; 1274 | } 1275 | 1276 | /* Render a value to text. */ 1277 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) 1278 | { 1279 | unsigned char *output = NULL; 1280 | 1281 | if ((item == NULL) || (output_buffer == NULL)) 1282 | { 1283 | return false; 1284 | } 1285 | 1286 | switch ((item->type) & 0xFF) 1287 | { 1288 | case cJSON_NULL: 1289 | output = ensure(output_buffer, 5); 1290 | if (output == NULL) 1291 | { 1292 | return false; 1293 | } 1294 | strcpy((char*)output, "null"); 1295 | return true; 1296 | 1297 | case cJSON_False: 1298 | output = ensure(output_buffer, 6); 1299 | if (output == NULL) 1300 | { 1301 | return false; 1302 | } 1303 | strcpy((char*)output, "false"); 1304 | return true; 1305 | 1306 | case cJSON_True: 1307 | output = ensure(output_buffer, 5); 1308 | if (output == NULL) 1309 | { 1310 | return false; 1311 | } 1312 | strcpy((char*)output, "true"); 1313 | return true; 1314 | 1315 | case cJSON_Number: 1316 | return print_number(item, output_buffer); 1317 | 1318 | case cJSON_Raw: 1319 | { 1320 | size_t raw_length = 0; 1321 | if (item->valuestring == NULL) 1322 | { 1323 | return false; 1324 | } 1325 | 1326 | raw_length = strlen(item->valuestring) + sizeof(""); 1327 | output = ensure(output_buffer, raw_length); 1328 | if (output == NULL) 1329 | { 1330 | return false; 1331 | } 1332 | memcpy(output, item->valuestring, raw_length); 1333 | return true; 1334 | } 1335 | 1336 | case cJSON_String: 1337 | return print_string(item, output_buffer); 1338 | 1339 | case cJSON_Array: 1340 | return print_array(item, output_buffer); 1341 | 1342 | case cJSON_Object: 1343 | return print_object(item, output_buffer); 1344 | 1345 | default: 1346 | return false; 1347 | } 1348 | } 1349 | 1350 | /* Build an array from input text. */ 1351 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) 1352 | { 1353 | cJSON *head = NULL; /* head of the linked list */ 1354 | cJSON *current_item = NULL; 1355 | 1356 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) 1357 | { 1358 | return false; /* to deeply nested */ 1359 | } 1360 | input_buffer->depth++; 1361 | 1362 | if (buffer_at_offset(input_buffer)[0] != '[') 1363 | { 1364 | /* not an array */ 1365 | goto fail; 1366 | } 1367 | 1368 | input_buffer->offset++; 1369 | buffer_skip_whitespace(input_buffer); 1370 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) 1371 | { 1372 | /* empty array */ 1373 | goto success; 1374 | } 1375 | 1376 | /* check if we skipped to the end of the buffer */ 1377 | if (cannot_access_at_index(input_buffer, 0)) 1378 | { 1379 | input_buffer->offset--; 1380 | goto fail; 1381 | } 1382 | 1383 | /* step back to character in front of the first element */ 1384 | input_buffer->offset--; 1385 | /* loop through the comma separated array elements */ 1386 | do 1387 | { 1388 | /* allocate next item */ 1389 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); 1390 | if (new_item == NULL) 1391 | { 1392 | goto fail; /* allocation failure */ 1393 | } 1394 | 1395 | /* attach next item to list */ 1396 | if (head == NULL) 1397 | { 1398 | /* start the linked list */ 1399 | current_item = head = new_item; 1400 | } 1401 | else 1402 | { 1403 | /* add to the end and advance */ 1404 | current_item->next = new_item; 1405 | new_item->prev = current_item; 1406 | current_item = new_item; 1407 | } 1408 | 1409 | /* parse next value */ 1410 | input_buffer->offset++; 1411 | buffer_skip_whitespace(input_buffer); 1412 | if (!parse_value(current_item, input_buffer)) 1413 | { 1414 | goto fail; /* failed to parse value */ 1415 | } 1416 | buffer_skip_whitespace(input_buffer); 1417 | } 1418 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 1419 | 1420 | if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') 1421 | { 1422 | goto fail; /* expected end of array */ 1423 | } 1424 | 1425 | success: 1426 | input_buffer->depth--; 1427 | 1428 | item->type = cJSON_Array; 1429 | item->child = head; 1430 | 1431 | input_buffer->offset++; 1432 | 1433 | return true; 1434 | 1435 | fail: 1436 | if (head != NULL) 1437 | { 1438 | cJSON_Delete(head); 1439 | } 1440 | 1441 | return false; 1442 | } 1443 | 1444 | /* Render an array to text */ 1445 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) 1446 | { 1447 | unsigned char *output_pointer = NULL; 1448 | size_t length = 0; 1449 | cJSON *current_element = item->child; 1450 | 1451 | if (output_buffer == NULL) 1452 | { 1453 | return false; 1454 | } 1455 | 1456 | /* Compose the output array. */ 1457 | /* opening square bracket */ 1458 | output_pointer = ensure(output_buffer, 1); 1459 | if (output_pointer == NULL) 1460 | { 1461 | return false; 1462 | } 1463 | 1464 | *output_pointer = '['; 1465 | output_buffer->offset++; 1466 | output_buffer->depth++; 1467 | 1468 | while (current_element != NULL) 1469 | { 1470 | if (!print_value(current_element, output_buffer)) 1471 | { 1472 | return false; 1473 | } 1474 | update_offset(output_buffer); 1475 | if (current_element->next) 1476 | { 1477 | length = (size_t) (output_buffer->format ? 2 : 1); 1478 | output_pointer = ensure(output_buffer, length + 1); 1479 | if (output_pointer == NULL) 1480 | { 1481 | return false; 1482 | } 1483 | *output_pointer++ = ','; 1484 | if(output_buffer->format) 1485 | { 1486 | *output_pointer++ = ' '; 1487 | } 1488 | *output_pointer = '\0'; 1489 | output_buffer->offset += length; 1490 | } 1491 | current_element = current_element->next; 1492 | } 1493 | 1494 | output_pointer = ensure(output_buffer, 2); 1495 | if (output_pointer == NULL) 1496 | { 1497 | return false; 1498 | } 1499 | *output_pointer++ = ']'; 1500 | *output_pointer = '\0'; 1501 | output_buffer->depth--; 1502 | 1503 | return true; 1504 | } 1505 | 1506 | /* Build an object from the text. */ 1507 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) 1508 | { 1509 | cJSON *head = NULL; /* linked list head */ 1510 | cJSON *current_item = NULL; 1511 | 1512 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) 1513 | { 1514 | return false; /* to deeply nested */ 1515 | } 1516 | input_buffer->depth++; 1517 | 1518 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) 1519 | { 1520 | goto fail; /* not an object */ 1521 | } 1522 | 1523 | input_buffer->offset++; 1524 | buffer_skip_whitespace(input_buffer); 1525 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) 1526 | { 1527 | goto success; /* empty object */ 1528 | } 1529 | 1530 | /* check if we skipped to the end of the buffer */ 1531 | if (cannot_access_at_index(input_buffer, 0)) 1532 | { 1533 | input_buffer->offset--; 1534 | goto fail; 1535 | } 1536 | 1537 | /* step back to character in front of the first element */ 1538 | input_buffer->offset--; 1539 | /* loop through the comma separated array elements */ 1540 | do 1541 | { 1542 | /* allocate next item */ 1543 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); 1544 | if (new_item == NULL) 1545 | { 1546 | goto fail; /* allocation failure */ 1547 | } 1548 | 1549 | /* attach next item to list */ 1550 | if (head == NULL) 1551 | { 1552 | /* start the linked list */ 1553 | current_item = head = new_item; 1554 | } 1555 | else 1556 | { 1557 | /* add to the end and advance */ 1558 | current_item->next = new_item; 1559 | new_item->prev = current_item; 1560 | current_item = new_item; 1561 | } 1562 | 1563 | /* parse the name of the child */ 1564 | input_buffer->offset++; 1565 | buffer_skip_whitespace(input_buffer); 1566 | if (!parse_string(current_item, input_buffer)) 1567 | { 1568 | goto fail; /* failed to parse name */ 1569 | } 1570 | buffer_skip_whitespace(input_buffer); 1571 | 1572 | /* swap valuestring and string, because we parsed the name */ 1573 | current_item->string = current_item->valuestring; 1574 | current_item->valuestring = NULL; 1575 | 1576 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) 1577 | { 1578 | goto fail; /* invalid object */ 1579 | } 1580 | 1581 | /* parse the value */ 1582 | input_buffer->offset++; 1583 | buffer_skip_whitespace(input_buffer); 1584 | if (!parse_value(current_item, input_buffer)) 1585 | { 1586 | goto fail; /* failed to parse value */ 1587 | } 1588 | buffer_skip_whitespace(input_buffer); 1589 | } 1590 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 1591 | 1592 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) 1593 | { 1594 | goto fail; /* expected end of object */ 1595 | } 1596 | 1597 | success: 1598 | input_buffer->depth--; 1599 | 1600 | item->type = cJSON_Object; 1601 | item->child = head; 1602 | 1603 | input_buffer->offset++; 1604 | return true; 1605 | 1606 | fail: 1607 | if (head != NULL) 1608 | { 1609 | cJSON_Delete(head); 1610 | } 1611 | 1612 | return false; 1613 | } 1614 | 1615 | /* Render an object to text. */ 1616 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) 1617 | { 1618 | unsigned char *output_pointer = NULL; 1619 | size_t length = 0; 1620 | cJSON *current_item = item->child; 1621 | 1622 | if (output_buffer == NULL) 1623 | { 1624 | return false; 1625 | } 1626 | 1627 | /* Compose the output: */ 1628 | length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ 1629 | output_pointer = ensure(output_buffer, length + 1); 1630 | if (output_pointer == NULL) 1631 | { 1632 | return false; 1633 | } 1634 | 1635 | *output_pointer++ = '{'; 1636 | output_buffer->depth++; 1637 | if (output_buffer->format) 1638 | { 1639 | *output_pointer++ = '\n'; 1640 | } 1641 | output_buffer->offset += length; 1642 | 1643 | while (current_item) 1644 | { 1645 | if (output_buffer->format) 1646 | { 1647 | size_t i; 1648 | output_pointer = ensure(output_buffer, output_buffer->depth); 1649 | if (output_pointer == NULL) 1650 | { 1651 | return false; 1652 | } 1653 | for (i = 0; i < output_buffer->depth; i++) 1654 | { 1655 | *output_pointer++ = '\t'; 1656 | } 1657 | output_buffer->offset += output_buffer->depth; 1658 | } 1659 | 1660 | /* print key */ 1661 | if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) 1662 | { 1663 | return false; 1664 | } 1665 | update_offset(output_buffer); 1666 | 1667 | length = (size_t) (output_buffer->format ? 2 : 1); 1668 | output_pointer = ensure(output_buffer, length); 1669 | if (output_pointer == NULL) 1670 | { 1671 | return false; 1672 | } 1673 | *output_pointer++ = ':'; 1674 | if (output_buffer->format) 1675 | { 1676 | *output_pointer++ = '\t'; 1677 | } 1678 | output_buffer->offset += length; 1679 | 1680 | /* print value */ 1681 | if (!print_value(current_item, output_buffer)) 1682 | { 1683 | return false; 1684 | } 1685 | update_offset(output_buffer); 1686 | 1687 | /* print comma if not last */ 1688 | length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); 1689 | output_pointer = ensure(output_buffer, length + 1); 1690 | if (output_pointer == NULL) 1691 | { 1692 | return false; 1693 | } 1694 | if (current_item->next) 1695 | { 1696 | *output_pointer++ = ','; 1697 | } 1698 | 1699 | if (output_buffer->format) 1700 | { 1701 | *output_pointer++ = '\n'; 1702 | } 1703 | *output_pointer = '\0'; 1704 | output_buffer->offset += length; 1705 | 1706 | current_item = current_item->next; 1707 | } 1708 | 1709 | output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); 1710 | if (output_pointer == NULL) 1711 | { 1712 | return false; 1713 | } 1714 | if (output_buffer->format) 1715 | { 1716 | size_t i; 1717 | for (i = 0; i < (output_buffer->depth - 1); i++) 1718 | { 1719 | *output_pointer++ = '\t'; 1720 | } 1721 | } 1722 | *output_pointer++ = '}'; 1723 | *output_pointer = '\0'; 1724 | output_buffer->depth--; 1725 | 1726 | return true; 1727 | } 1728 | 1729 | /* Get Array size/item / object item. */ 1730 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) 1731 | { 1732 | cJSON *child = NULL; 1733 | size_t size = 0; 1734 | 1735 | if (array == NULL) 1736 | { 1737 | return 0; 1738 | } 1739 | 1740 | child = array->child; 1741 | 1742 | while(child != NULL) 1743 | { 1744 | size++; 1745 | child = child->next; 1746 | } 1747 | 1748 | /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ 1749 | 1750 | return (int)size; 1751 | } 1752 | 1753 | static cJSON* get_array_item(const cJSON *array, size_t index) 1754 | { 1755 | cJSON *current_child = NULL; 1756 | 1757 | if (array == NULL) 1758 | { 1759 | return NULL; 1760 | } 1761 | 1762 | current_child = array->child; 1763 | while ((current_child != NULL) && (index > 0)) 1764 | { 1765 | index--; 1766 | current_child = current_child->next; 1767 | } 1768 | 1769 | return current_child; 1770 | } 1771 | 1772 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) 1773 | { 1774 | if (index < 0) 1775 | { 1776 | return NULL; 1777 | } 1778 | 1779 | return get_array_item(array, (size_t)index); 1780 | } 1781 | 1782 | static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) 1783 | { 1784 | cJSON *current_element = NULL; 1785 | 1786 | if ((object == NULL) || (name == NULL)) 1787 | { 1788 | return NULL; 1789 | } 1790 | 1791 | current_element = object->child; 1792 | if (case_sensitive) 1793 | { 1794 | while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) 1795 | { 1796 | current_element = current_element->next; 1797 | } 1798 | } 1799 | else 1800 | { 1801 | while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) 1802 | { 1803 | current_element = current_element->next; 1804 | } 1805 | } 1806 | 1807 | if ((current_element == NULL) || (current_element->string == NULL)) { 1808 | return NULL; 1809 | } 1810 | 1811 | return current_element; 1812 | } 1813 | 1814 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) 1815 | { 1816 | return get_object_item(object, string, false); 1817 | } 1818 | 1819 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) 1820 | { 1821 | return get_object_item(object, string, true); 1822 | } 1823 | 1824 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) 1825 | { 1826 | return cJSON_GetObjectItem(object, string) ? 1 : 0; 1827 | } 1828 | 1829 | /* Utility for array list handling. */ 1830 | static void suffix_object(cJSON *prev, cJSON *item) 1831 | { 1832 | prev->next = item; 1833 | item->prev = prev; 1834 | } 1835 | 1836 | /* Utility for handling references. */ 1837 | static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) 1838 | { 1839 | cJSON *reference = NULL; 1840 | if (item == NULL) 1841 | { 1842 | return NULL; 1843 | } 1844 | 1845 | reference = cJSON_New_Item(hooks); 1846 | if (reference == NULL) 1847 | { 1848 | return NULL; 1849 | } 1850 | 1851 | memcpy(reference, item, sizeof(cJSON)); 1852 | reference->string = NULL; 1853 | reference->type |= cJSON_IsReference; 1854 | reference->next = reference->prev = NULL; 1855 | return reference; 1856 | } 1857 | 1858 | static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) 1859 | { 1860 | cJSON *child = NULL; 1861 | 1862 | if ((item == NULL) || (array == NULL)) 1863 | { 1864 | return false; 1865 | } 1866 | 1867 | child = array->child; 1868 | 1869 | if (child == NULL) 1870 | { 1871 | /* list is empty, start new one */ 1872 | array->child = item; 1873 | } 1874 | else 1875 | { 1876 | /* append to the end */ 1877 | while (child->next) 1878 | { 1879 | child = child->next; 1880 | } 1881 | suffix_object(child, item); 1882 | } 1883 | 1884 | return true; 1885 | } 1886 | 1887 | /* Add item to array/object. */ 1888 | CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) 1889 | { 1890 | add_item_to_array(array, item); 1891 | } 1892 | 1893 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 1894 | #pragma GCC diagnostic push 1895 | #endif 1896 | #ifdef __GNUC__ 1897 | #pragma GCC diagnostic ignored "-Wcast-qual" 1898 | #endif 1899 | /* helper function to cast away const */ 1900 | static void* cast_away_const(const void* string) 1901 | { 1902 | return (void*)string; 1903 | } 1904 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 1905 | #pragma GCC diagnostic pop 1906 | #endif 1907 | 1908 | 1909 | 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) 1910 | { 1911 | char *new_key = NULL; 1912 | int new_type = cJSON_Invalid; 1913 | 1914 | if ((object == NULL) || (string == NULL) || (item == NULL)) 1915 | { 1916 | return false; 1917 | } 1918 | 1919 | if (constant_key) 1920 | { 1921 | new_key = (char*)cast_away_const(string); 1922 | new_type = item->type | cJSON_StringIsConst; 1923 | } 1924 | else 1925 | { 1926 | new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); 1927 | if (new_key == NULL) 1928 | { 1929 | return false; 1930 | } 1931 | 1932 | new_type = item->type & ~cJSON_StringIsConst; 1933 | } 1934 | 1935 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) 1936 | { 1937 | hooks->deallocate(item->string); 1938 | } 1939 | 1940 | item->string = new_key; 1941 | item->type = new_type; 1942 | 1943 | return add_item_to_array(object, item); 1944 | } 1945 | 1946 | CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) 1947 | { 1948 | add_item_to_object(object, string, item, &global_hooks, false); 1949 | } 1950 | 1951 | /* Add an item to an object with constant string as key */ 1952 | CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) 1953 | { 1954 | add_item_to_object(object, string, item, &global_hooks, true); 1955 | } 1956 | 1957 | CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) 1958 | { 1959 | if (array == NULL) 1960 | { 1961 | return; 1962 | } 1963 | 1964 | add_item_to_array(array, create_reference(item, &global_hooks)); 1965 | } 1966 | 1967 | CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) 1968 | { 1969 | if ((object == NULL) || (string == NULL)) 1970 | { 1971 | return; 1972 | } 1973 | 1974 | add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); 1975 | } 1976 | 1977 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) 1978 | { 1979 | cJSON *null = cJSON_CreateNull(); 1980 | if (add_item_to_object(object, name, null, &global_hooks, false)) 1981 | { 1982 | return null; 1983 | } 1984 | 1985 | cJSON_Delete(null); 1986 | return NULL; 1987 | } 1988 | 1989 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) 1990 | { 1991 | cJSON *true_item = cJSON_CreateTrue(); 1992 | if (add_item_to_object(object, name, true_item, &global_hooks, false)) 1993 | { 1994 | return true_item; 1995 | } 1996 | 1997 | cJSON_Delete(true_item); 1998 | return NULL; 1999 | } 2000 | 2001 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) 2002 | { 2003 | cJSON *false_item = cJSON_CreateFalse(); 2004 | if (add_item_to_object(object, name, false_item, &global_hooks, false)) 2005 | { 2006 | return false_item; 2007 | } 2008 | 2009 | cJSON_Delete(false_item); 2010 | return NULL; 2011 | } 2012 | 2013 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) 2014 | { 2015 | cJSON *bool_item = cJSON_CreateBool(boolean); 2016 | if (add_item_to_object(object, name, bool_item, &global_hooks, false)) 2017 | { 2018 | return bool_item; 2019 | } 2020 | 2021 | cJSON_Delete(bool_item); 2022 | return NULL; 2023 | } 2024 | 2025 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) 2026 | { 2027 | cJSON *number_item = cJSON_CreateNumber(number); 2028 | if (add_item_to_object(object, name, number_item, &global_hooks, false)) 2029 | { 2030 | return number_item; 2031 | } 2032 | 2033 | cJSON_Delete(number_item); 2034 | return NULL; 2035 | } 2036 | 2037 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) 2038 | { 2039 | cJSON *string_item = cJSON_CreateString(string); 2040 | if (add_item_to_object(object, name, string_item, &global_hooks, false)) 2041 | { 2042 | return string_item; 2043 | } 2044 | 2045 | cJSON_Delete(string_item); 2046 | return NULL; 2047 | } 2048 | 2049 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) 2050 | { 2051 | cJSON *raw_item = cJSON_CreateRaw(raw); 2052 | if (add_item_to_object(object, name, raw_item, &global_hooks, false)) 2053 | { 2054 | return raw_item; 2055 | } 2056 | 2057 | cJSON_Delete(raw_item); 2058 | return NULL; 2059 | } 2060 | 2061 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) 2062 | { 2063 | cJSON *object_item = cJSON_CreateObject(); 2064 | if (add_item_to_object(object, name, object_item, &global_hooks, false)) 2065 | { 2066 | return object_item; 2067 | } 2068 | 2069 | cJSON_Delete(object_item); 2070 | return NULL; 2071 | } 2072 | 2073 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) 2074 | { 2075 | cJSON *array = cJSON_CreateArray(); 2076 | if (add_item_to_object(object, name, array, &global_hooks, false)) 2077 | { 2078 | return array; 2079 | } 2080 | 2081 | cJSON_Delete(array); 2082 | return NULL; 2083 | } 2084 | 2085 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) 2086 | { 2087 | if ((parent == NULL) || (item == NULL)) 2088 | { 2089 | return NULL; 2090 | } 2091 | 2092 | if (item->prev != NULL) 2093 | { 2094 | /* not the first element */ 2095 | item->prev->next = item->next; 2096 | } 2097 | if (item->next != NULL) 2098 | { 2099 | /* not the last element */ 2100 | item->next->prev = item->prev; 2101 | } 2102 | 2103 | if (item == parent->child) 2104 | { 2105 | /* first element */ 2106 | parent->child = item->next; 2107 | } 2108 | /* make sure the detached item doesn't point anywhere anymore */ 2109 | item->prev = NULL; 2110 | item->next = NULL; 2111 | 2112 | return item; 2113 | } 2114 | 2115 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) 2116 | { 2117 | if (which < 0) 2118 | { 2119 | return NULL; 2120 | } 2121 | 2122 | return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); 2123 | } 2124 | 2125 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) 2126 | { 2127 | cJSON_Delete(cJSON_DetachItemFromArray(array, which)); 2128 | } 2129 | 2130 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) 2131 | { 2132 | cJSON *to_detach = cJSON_GetObjectItem(object, string); 2133 | 2134 | return cJSON_DetachItemViaPointer(object, to_detach); 2135 | } 2136 | 2137 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) 2138 | { 2139 | cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); 2140 | 2141 | return cJSON_DetachItemViaPointer(object, to_detach); 2142 | } 2143 | 2144 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) 2145 | { 2146 | cJSON_Delete(cJSON_DetachItemFromObject(object, string)); 2147 | } 2148 | 2149 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) 2150 | { 2151 | cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); 2152 | } 2153 | 2154 | /* Replace array/object items with new ones. */ 2155 | CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) 2156 | { 2157 | cJSON *after_inserted = NULL; 2158 | 2159 | if (which < 0) 2160 | { 2161 | return; 2162 | } 2163 | 2164 | after_inserted = get_array_item(array, (size_t)which); 2165 | if (after_inserted == NULL) 2166 | { 2167 | add_item_to_array(array, newitem); 2168 | return; 2169 | } 2170 | 2171 | newitem->next = after_inserted; 2172 | newitem->prev = after_inserted->prev; 2173 | after_inserted->prev = newitem; 2174 | if (after_inserted == array->child) 2175 | { 2176 | array->child = newitem; 2177 | } 2178 | else 2179 | { 2180 | newitem->prev->next = newitem; 2181 | } 2182 | } 2183 | 2184 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) 2185 | { 2186 | if ((parent == NULL) || (replacement == NULL) || (item == NULL)) 2187 | { 2188 | return false; 2189 | } 2190 | 2191 | if (replacement == item) 2192 | { 2193 | return true; 2194 | } 2195 | 2196 | replacement->next = item->next; 2197 | replacement->prev = item->prev; 2198 | 2199 | if (replacement->next != NULL) 2200 | { 2201 | replacement->next->prev = replacement; 2202 | } 2203 | if (replacement->prev != NULL) 2204 | { 2205 | replacement->prev->next = replacement; 2206 | } 2207 | if (parent->child == item) 2208 | { 2209 | parent->child = replacement; 2210 | } 2211 | 2212 | item->next = NULL; 2213 | item->prev = NULL; 2214 | cJSON_Delete(item); 2215 | 2216 | return true; 2217 | } 2218 | 2219 | CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) 2220 | { 2221 | if (which < 0) 2222 | { 2223 | return; 2224 | } 2225 | 2226 | cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); 2227 | } 2228 | 2229 | static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) 2230 | { 2231 | if ((replacement == NULL) || (string == NULL)) 2232 | { 2233 | return false; 2234 | } 2235 | 2236 | /* replace the name in the replacement */ 2237 | if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) 2238 | { 2239 | cJSON_free(replacement->string); 2240 | } 2241 | replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); 2242 | replacement->type &= ~cJSON_StringIsConst; 2243 | 2244 | cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); 2245 | 2246 | return true; 2247 | } 2248 | 2249 | CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) 2250 | { 2251 | replace_item_in_object(object, string, newitem, false); 2252 | } 2253 | 2254 | CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) 2255 | { 2256 | replace_item_in_object(object, string, newitem, true); 2257 | } 2258 | 2259 | /* Create basic types: */ 2260 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) 2261 | { 2262 | cJSON *item = cJSON_New_Item(&global_hooks); 2263 | if(item) 2264 | { 2265 | item->type = cJSON_NULL; 2266 | } 2267 | 2268 | return item; 2269 | } 2270 | 2271 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) 2272 | { 2273 | cJSON *item = cJSON_New_Item(&global_hooks); 2274 | if(item) 2275 | { 2276 | item->type = cJSON_True; 2277 | } 2278 | 2279 | return item; 2280 | } 2281 | 2282 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) 2283 | { 2284 | cJSON *item = cJSON_New_Item(&global_hooks); 2285 | if(item) 2286 | { 2287 | item->type = cJSON_False; 2288 | } 2289 | 2290 | return item; 2291 | } 2292 | 2293 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) 2294 | { 2295 | cJSON *item = cJSON_New_Item(&global_hooks); 2296 | if(item) 2297 | { 2298 | item->type = b ? cJSON_True : cJSON_False; 2299 | } 2300 | 2301 | return item; 2302 | } 2303 | 2304 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) 2305 | { 2306 | cJSON *item = cJSON_New_Item(&global_hooks); 2307 | if(item) 2308 | { 2309 | item->type = cJSON_Number; 2310 | item->valuedouble = num; 2311 | 2312 | /* use saturation in case of overflow */ 2313 | if (num >= INT_MAX) 2314 | { 2315 | item->valueint = INT_MAX; 2316 | } 2317 | else if (num <= (double)INT_MIN) 2318 | { 2319 | item->valueint = INT_MIN; 2320 | } 2321 | else 2322 | { 2323 | item->valueint = (int)num; 2324 | } 2325 | } 2326 | 2327 | return item; 2328 | } 2329 | 2330 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) 2331 | { 2332 | cJSON *item = cJSON_New_Item(&global_hooks); 2333 | if(item) 2334 | { 2335 | item->type = cJSON_String; 2336 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); 2337 | if(!item->valuestring) 2338 | { 2339 | cJSON_Delete(item); 2340 | return NULL; 2341 | } 2342 | } 2343 | 2344 | return item; 2345 | } 2346 | 2347 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) 2348 | { 2349 | cJSON *item = cJSON_New_Item(&global_hooks); 2350 | if (item != NULL) 2351 | { 2352 | item->type = cJSON_String | cJSON_IsReference; 2353 | item->valuestring = (char*)cast_away_const(string); 2354 | } 2355 | 2356 | return item; 2357 | } 2358 | 2359 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) 2360 | { 2361 | cJSON *item = cJSON_New_Item(&global_hooks); 2362 | if (item != NULL) { 2363 | item->type = cJSON_Object | cJSON_IsReference; 2364 | item->child = (cJSON*)cast_away_const(child); 2365 | } 2366 | 2367 | return item; 2368 | } 2369 | 2370 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { 2371 | cJSON *item = cJSON_New_Item(&global_hooks); 2372 | if (item != NULL) { 2373 | item->type = cJSON_Array | cJSON_IsReference; 2374 | item->child = (cJSON*)cast_away_const(child); 2375 | } 2376 | 2377 | return item; 2378 | } 2379 | 2380 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) 2381 | { 2382 | cJSON *item = cJSON_New_Item(&global_hooks); 2383 | if(item) 2384 | { 2385 | item->type = cJSON_Raw; 2386 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); 2387 | if(!item->valuestring) 2388 | { 2389 | cJSON_Delete(item); 2390 | return NULL; 2391 | } 2392 | } 2393 | 2394 | return item; 2395 | } 2396 | 2397 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) 2398 | { 2399 | cJSON *item = cJSON_New_Item(&global_hooks); 2400 | if(item) 2401 | { 2402 | item->type=cJSON_Array; 2403 | } 2404 | 2405 | return item; 2406 | } 2407 | 2408 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) 2409 | { 2410 | cJSON *item = cJSON_New_Item(&global_hooks); 2411 | if (item) 2412 | { 2413 | item->type = cJSON_Object; 2414 | } 2415 | 2416 | return item; 2417 | } 2418 | 2419 | /* Create Arrays: */ 2420 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) 2421 | { 2422 | size_t i = 0; 2423 | cJSON *n = NULL; 2424 | cJSON *p = NULL; 2425 | cJSON *a = NULL; 2426 | 2427 | if ((count < 0) || (numbers == NULL)) 2428 | { 2429 | return NULL; 2430 | } 2431 | 2432 | a = cJSON_CreateArray(); 2433 | for(i = 0; a && (i < (size_t)count); i++) 2434 | { 2435 | n = cJSON_CreateNumber(numbers[i]); 2436 | if (!n) 2437 | { 2438 | cJSON_Delete(a); 2439 | return NULL; 2440 | } 2441 | if(!i) 2442 | { 2443 | a->child = n; 2444 | } 2445 | else 2446 | { 2447 | suffix_object(p, n); 2448 | } 2449 | p = n; 2450 | } 2451 | 2452 | return a; 2453 | } 2454 | 2455 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) 2456 | { 2457 | size_t i = 0; 2458 | cJSON *n = NULL; 2459 | cJSON *p = NULL; 2460 | cJSON *a = NULL; 2461 | 2462 | if ((count < 0) || (numbers == NULL)) 2463 | { 2464 | return NULL; 2465 | } 2466 | 2467 | a = cJSON_CreateArray(); 2468 | 2469 | for(i = 0; a && (i < (size_t)count); i++) 2470 | { 2471 | n = cJSON_CreateNumber((double)numbers[i]); 2472 | if(!n) 2473 | { 2474 | cJSON_Delete(a); 2475 | return NULL; 2476 | } 2477 | if(!i) 2478 | { 2479 | a->child = n; 2480 | } 2481 | else 2482 | { 2483 | suffix_object(p, n); 2484 | } 2485 | p = n; 2486 | } 2487 | 2488 | return a; 2489 | } 2490 | 2491 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) 2492 | { 2493 | size_t i = 0; 2494 | cJSON *n = NULL; 2495 | cJSON *p = NULL; 2496 | cJSON *a = NULL; 2497 | 2498 | if ((count < 0) || (numbers == NULL)) 2499 | { 2500 | return NULL; 2501 | } 2502 | 2503 | a = cJSON_CreateArray(); 2504 | 2505 | for(i = 0;a && (i < (size_t)count); i++) 2506 | { 2507 | n = cJSON_CreateNumber(numbers[i]); 2508 | if(!n) 2509 | { 2510 | cJSON_Delete(a); 2511 | return NULL; 2512 | } 2513 | if(!i) 2514 | { 2515 | a->child = n; 2516 | } 2517 | else 2518 | { 2519 | suffix_object(p, n); 2520 | } 2521 | p = n; 2522 | } 2523 | 2524 | return a; 2525 | } 2526 | 2527 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) 2528 | { 2529 | size_t i = 0; 2530 | cJSON *n = NULL; 2531 | cJSON *p = NULL; 2532 | cJSON *a = NULL; 2533 | 2534 | if ((count < 0) || (strings == NULL)) 2535 | { 2536 | return NULL; 2537 | } 2538 | 2539 | a = cJSON_CreateArray(); 2540 | 2541 | for (i = 0; a && (i < (size_t)count); i++) 2542 | { 2543 | n = cJSON_CreateString(strings[i]); 2544 | if(!n) 2545 | { 2546 | cJSON_Delete(a); 2547 | return NULL; 2548 | } 2549 | if(!i) 2550 | { 2551 | a->child = n; 2552 | } 2553 | else 2554 | { 2555 | suffix_object(p,n); 2556 | } 2557 | p = n; 2558 | } 2559 | 2560 | return a; 2561 | } 2562 | 2563 | /* Duplication */ 2564 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) 2565 | { 2566 | cJSON *newitem = NULL; 2567 | cJSON *child = NULL; 2568 | cJSON *next = NULL; 2569 | cJSON *newchild = NULL; 2570 | 2571 | /* Bail on bad ptr */ 2572 | if (!item) 2573 | { 2574 | goto fail; 2575 | } 2576 | /* Create new item */ 2577 | newitem = cJSON_New_Item(&global_hooks); 2578 | if (!newitem) 2579 | { 2580 | goto fail; 2581 | } 2582 | /* Copy over all vars */ 2583 | newitem->type = item->type & (~cJSON_IsReference); 2584 | newitem->valueint = item->valueint; 2585 | newitem->valuedouble = item->valuedouble; 2586 | if (item->valuestring) 2587 | { 2588 | newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); 2589 | if (!newitem->valuestring) 2590 | { 2591 | goto fail; 2592 | } 2593 | } 2594 | if (item->string) 2595 | { 2596 | newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); 2597 | if (!newitem->string) 2598 | { 2599 | goto fail; 2600 | } 2601 | } 2602 | /* If non-recursive, then we're done! */ 2603 | if (!recurse) 2604 | { 2605 | return newitem; 2606 | } 2607 | /* Walk the ->next chain for the child. */ 2608 | child = item->child; 2609 | while (child != NULL) 2610 | { 2611 | newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ 2612 | if (!newchild) 2613 | { 2614 | goto fail; 2615 | } 2616 | if (next != NULL) 2617 | { 2618 | /* If newitem->child already set, then crosswire ->prev and ->next and move on */ 2619 | next->next = newchild; 2620 | newchild->prev = next; 2621 | next = newchild; 2622 | } 2623 | else 2624 | { 2625 | /* Set newitem->child and move to it */ 2626 | newitem->child = newchild; 2627 | next = newchild; 2628 | } 2629 | child = child->next; 2630 | } 2631 | 2632 | return newitem; 2633 | 2634 | fail: 2635 | if (newitem != NULL) 2636 | { 2637 | cJSON_Delete(newitem); 2638 | } 2639 | 2640 | return NULL; 2641 | } 2642 | 2643 | static void skip_oneline_comment(char **input) 2644 | { 2645 | *input += static_strlen("//"); 2646 | 2647 | for (; (*input)[0] != '\0'; ++(*input)) 2648 | { 2649 | if ((*input)[0] == '\n') { 2650 | *input += static_strlen("\n"); 2651 | return; 2652 | } 2653 | } 2654 | } 2655 | 2656 | static void skip_multiline_comment(char **input) 2657 | { 2658 | *input += static_strlen("/*"); 2659 | 2660 | for (; (*input)[0] != '\0'; ++(*input)) 2661 | { 2662 | if (((*input)[0] == '*') && ((*input)[1] == '/')) 2663 | { 2664 | *input += static_strlen("*/"); 2665 | return; 2666 | } 2667 | } 2668 | } 2669 | 2670 | static void minify_string(char **input, char **output) { 2671 | (*output)[0] = (*input)[0]; 2672 | *input += static_strlen("\""); 2673 | *output += static_strlen("\""); 2674 | 2675 | 2676 | for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { 2677 | (*output)[0] = (*input)[0]; 2678 | 2679 | if ((*input)[0] == '\"') { 2680 | (*output)[0] = '\"'; 2681 | *input += static_strlen("\""); 2682 | *output += static_strlen("\""); 2683 | return; 2684 | } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { 2685 | (*output)[1] = (*input)[1]; 2686 | *input += static_strlen("\""); 2687 | *output += static_strlen("\""); 2688 | } 2689 | } 2690 | } 2691 | 2692 | CJSON_PUBLIC(void) cJSON_Minify(char *json) 2693 | { 2694 | char *into = json; 2695 | 2696 | if (json == NULL) 2697 | { 2698 | return; 2699 | } 2700 | 2701 | while (json[0] != '\0') 2702 | { 2703 | switch (json[0]) 2704 | { 2705 | case ' ': 2706 | case '\t': 2707 | case '\r': 2708 | case '\n': 2709 | json++; 2710 | break; 2711 | 2712 | case '/': 2713 | if (json[1] == '/') 2714 | { 2715 | skip_oneline_comment(&json); 2716 | } 2717 | else if (json[1] == '*') 2718 | { 2719 | skip_multiline_comment(&json); 2720 | } else { 2721 | json++; 2722 | } 2723 | break; 2724 | 2725 | case '\"': 2726 | minify_string(&json, (char**)&into); 2727 | break; 2728 | 2729 | default: 2730 | into[0] = json[0]; 2731 | json++; 2732 | into++; 2733 | } 2734 | } 2735 | 2736 | /* and null-terminate. */ 2737 | *into = '\0'; 2738 | } 2739 | 2740 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) 2741 | { 2742 | if (item == NULL) 2743 | { 2744 | return false; 2745 | } 2746 | 2747 | return (item->type & 0xFF) == cJSON_Invalid; 2748 | } 2749 | 2750 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) 2751 | { 2752 | if (item == NULL) 2753 | { 2754 | return false; 2755 | } 2756 | 2757 | return (item->type & 0xFF) == cJSON_False; 2758 | } 2759 | 2760 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) 2761 | { 2762 | if (item == NULL) 2763 | { 2764 | return false; 2765 | } 2766 | 2767 | return (item->type & 0xff) == cJSON_True; 2768 | } 2769 | 2770 | 2771 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) 2772 | { 2773 | if (item == NULL) 2774 | { 2775 | return false; 2776 | } 2777 | 2778 | return (item->type & (cJSON_True | cJSON_False)) != 0; 2779 | } 2780 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) 2781 | { 2782 | if (item == NULL) 2783 | { 2784 | return false; 2785 | } 2786 | 2787 | return (item->type & 0xFF) == cJSON_NULL; 2788 | } 2789 | 2790 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) 2791 | { 2792 | if (item == NULL) 2793 | { 2794 | return false; 2795 | } 2796 | 2797 | return (item->type & 0xFF) == cJSON_Number; 2798 | } 2799 | 2800 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) 2801 | { 2802 | if (item == NULL) 2803 | { 2804 | return false; 2805 | } 2806 | 2807 | return (item->type & 0xFF) == cJSON_String; 2808 | } 2809 | 2810 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) 2811 | { 2812 | if (item == NULL) 2813 | { 2814 | return false; 2815 | } 2816 | 2817 | return (item->type & 0xFF) == cJSON_Array; 2818 | } 2819 | 2820 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) 2821 | { 2822 | if (item == NULL) 2823 | { 2824 | return false; 2825 | } 2826 | 2827 | return (item->type & 0xFF) == cJSON_Object; 2828 | } 2829 | 2830 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) 2831 | { 2832 | if (item == NULL) 2833 | { 2834 | return false; 2835 | } 2836 | 2837 | return (item->type & 0xFF) == cJSON_Raw; 2838 | } 2839 | 2840 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) 2841 | { 2842 | if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) 2843 | { 2844 | return false; 2845 | } 2846 | 2847 | /* check if type is valid */ 2848 | switch (a->type & 0xFF) 2849 | { 2850 | case cJSON_False: 2851 | case cJSON_True: 2852 | case cJSON_NULL: 2853 | case cJSON_Number: 2854 | case cJSON_String: 2855 | case cJSON_Raw: 2856 | case cJSON_Array: 2857 | case cJSON_Object: 2858 | break; 2859 | 2860 | default: 2861 | return false; 2862 | } 2863 | 2864 | /* identical objects are equal */ 2865 | if (a == b) 2866 | { 2867 | return true; 2868 | } 2869 | 2870 | switch (a->type & 0xFF) 2871 | { 2872 | /* in these cases and equal type is enough */ 2873 | case cJSON_False: 2874 | case cJSON_True: 2875 | case cJSON_NULL: 2876 | return true; 2877 | 2878 | case cJSON_Number: 2879 | if (a->valuedouble == b->valuedouble) 2880 | { 2881 | return true; 2882 | } 2883 | return false; 2884 | 2885 | case cJSON_String: 2886 | case cJSON_Raw: 2887 | if ((a->valuestring == NULL) || (b->valuestring == NULL)) 2888 | { 2889 | return false; 2890 | } 2891 | if (strcmp(a->valuestring, b->valuestring) == 0) 2892 | { 2893 | return true; 2894 | } 2895 | 2896 | return false; 2897 | 2898 | case cJSON_Array: 2899 | { 2900 | cJSON *a_element = a->child; 2901 | cJSON *b_element = b->child; 2902 | 2903 | for (; (a_element != NULL) && (b_element != NULL);) 2904 | { 2905 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) 2906 | { 2907 | return false; 2908 | } 2909 | 2910 | a_element = a_element->next; 2911 | b_element = b_element->next; 2912 | } 2913 | 2914 | /* one of the arrays is longer than the other */ 2915 | if (a_element != b_element) { 2916 | return false; 2917 | } 2918 | 2919 | return true; 2920 | } 2921 | 2922 | case cJSON_Object: 2923 | { 2924 | cJSON *a_element = NULL; 2925 | cJSON *b_element = NULL; 2926 | cJSON_ArrayForEach(a_element, a) 2927 | { 2928 | /* TODO This has O(n^2) runtime, which is horrible! */ 2929 | b_element = get_object_item(b, a_element->string, case_sensitive); 2930 | if (b_element == NULL) 2931 | { 2932 | return false; 2933 | } 2934 | 2935 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) 2936 | { 2937 | return false; 2938 | } 2939 | } 2940 | 2941 | /* doing this twice, once on a and b to prevent true comparison if a subset of b 2942 | * TODO: Do this the proper way, this is just a fix for now */ 2943 | cJSON_ArrayForEach(b_element, b) 2944 | { 2945 | a_element = get_object_item(a, b_element->string, case_sensitive); 2946 | if (a_element == NULL) 2947 | { 2948 | return false; 2949 | } 2950 | 2951 | if (!cJSON_Compare(b_element, a_element, case_sensitive)) 2952 | { 2953 | return false; 2954 | } 2955 | } 2956 | 2957 | return true; 2958 | } 2959 | 2960 | default: 2961 | return false; 2962 | } 2963 | } 2964 | 2965 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size) 2966 | { 2967 | return global_hooks.allocate(size); 2968 | } 2969 | 2970 | CJSON_PUBLIC(void) cJSON_free(void *object) 2971 | { 2972 | global_hooks.deallocate(object); 2973 | } 2974 | -------------------------------------------------------------------------------- /src/cJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef cJSON__h 24 | #define cJSON__h 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) 32 | #define __WINDOWS__ 33 | #endif 34 | 35 | #ifdef __WINDOWS__ 36 | 37 | /* 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: 38 | 39 | CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols 40 | CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) 41 | CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol 42 | 43 | For *nix builds that support visibility attribute, you can define similar behavior by 44 | 45 | setting default visibility to hidden by adding 46 | -fvisibility=hidden (for gcc) 47 | or 48 | -xldscope=hidden (for sun cc) 49 | to CFLAGS 50 | 51 | then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does 52 | 53 | */ 54 | 55 | #define CJSON_CDECL __cdecl 56 | #define CJSON_STDCALL __stdcall 57 | 58 | /* export symbols by default, this is necessary for copy pasting the C and header file */ 59 | #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) 60 | #define CJSON_EXPORT_SYMBOLS 61 | #endif 62 | 63 | #if defined(CJSON_HIDE_SYMBOLS) 64 | #define CJSON_PUBLIC(type) type CJSON_STDCALL 65 | #elif defined(CJSON_EXPORT_SYMBOLS) 66 | #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL 67 | #elif defined(CJSON_IMPORT_SYMBOLS) 68 | #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL 69 | #endif 70 | #else /* !__WINDOWS__ */ 71 | #define CJSON_CDECL 72 | #define CJSON_STDCALL 73 | 74 | #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) 75 | #define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type 76 | #else 77 | #define CJSON_PUBLIC(type) type 78 | #endif 79 | #endif 80 | 81 | /* project version */ 82 | #define CJSON_VERSION_MAJOR 1 83 | #define CJSON_VERSION_MINOR 7 84 | #define CJSON_VERSION_PATCH 12 85 | 86 | #include 87 | 88 | /* cJSON Types: */ 89 | #define cJSON_Invalid (0) 90 | #define cJSON_False (1 << 0) 91 | #define cJSON_True (1 << 1) 92 | #define cJSON_NULL (1 << 2) 93 | #define cJSON_Number (1 << 3) 94 | #define cJSON_String (1 << 4) 95 | #define cJSON_Array (1 << 5) 96 | #define cJSON_Object (1 << 6) 97 | #define cJSON_Raw (1 << 7) /* raw json */ 98 | 99 | #define cJSON_IsReference 256 100 | #define cJSON_StringIsConst 512 101 | 102 | /* The cJSON structure: */ 103 | typedef struct cJSON 104 | { 105 | /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ 106 | struct cJSON *next; 107 | struct cJSON *prev; 108 | /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ 109 | struct cJSON *child; 110 | 111 | /* The type of the item, as above. */ 112 | int type; 113 | 114 | /* The item's string, if type==cJSON_String and type == cJSON_Raw */ 115 | char *valuestring; 116 | /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ 117 | int valueint; 118 | /* The item's number, if type==cJSON_Number */ 119 | double valuedouble; 120 | 121 | /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ 122 | char *string; 123 | } cJSON; 124 | 125 | typedef struct cJSON_Hooks 126 | { 127 | /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ 128 | void *(CJSON_CDECL *malloc_fn)(size_t sz); 129 | void (CJSON_CDECL *free_fn)(void *ptr); 130 | } cJSON_Hooks; 131 | 132 | typedef int cJSON_bool; 133 | 134 | /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. 135 | * This is to prevent stack overflows. */ 136 | #ifndef CJSON_NESTING_LIMIT 137 | #define CJSON_NESTING_LIMIT 1000 138 | #endif 139 | 140 | /* returns the version of cJSON as a string */ 141 | CJSON_PUBLIC(const char*) cJSON_Version(void); 142 | 143 | /* Supply malloc, realloc and free functions to cJSON */ 144 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); 145 | 146 | /* 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. */ 147 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ 148 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); 149 | /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ 150 | /* 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(). */ 151 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); 152 | 153 | /* Render a cJSON entity to text for transfer/storage. */ 154 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); 155 | /* Render a cJSON entity to text for transfer/storage without any formatting. */ 156 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); 157 | /* 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 */ 158 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); 159 | /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ 160 | /* 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 */ 161 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); 162 | /* Delete a cJSON entity and all subentities. */ 163 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); 164 | 165 | /* Returns the number of items in an array (or object). */ 166 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); 167 | /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ 168 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); 169 | /* Get item "string" from object. Case insensitive. */ 170 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); 171 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); 172 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); 173 | /* 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. */ 174 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); 175 | 176 | /* Check if the item is a string and return its valuestring */ 177 | CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); 178 | 179 | /* These functions check the type of an item */ 180 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); 181 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); 182 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); 183 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); 184 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); 185 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); 186 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); 187 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); 188 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); 189 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); 190 | 191 | /* These calls create a cJSON item of the appropriate type. */ 192 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); 193 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); 194 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); 195 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); 196 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); 197 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); 198 | /* raw json */ 199 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); 200 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); 201 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); 202 | 203 | /* Create a string where valuestring references a string so 204 | * it will not be freed by cJSON_Delete */ 205 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); 206 | /* Create an object/arrray that only references it's elements so 207 | * they will not be freed by cJSON_Delete */ 208 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); 209 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); 210 | 211 | /* These utilities create an Array of count items. */ 212 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); 213 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); 214 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); 215 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); 216 | 217 | /* Append item to the specified array/object. */ 218 | CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); 219 | CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); 220 | /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. 221 | * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before 222 | * writing to `item->string` */ 223 | CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); 224 | /* 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. */ 225 | CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); 226 | CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); 227 | 228 | /* Remove/Detatch items from Arrays/Objects. */ 229 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); 230 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); 231 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); 232 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); 233 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); 234 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); 235 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); 236 | 237 | /* Update array items. */ 238 | CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ 239 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); 240 | CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); 241 | CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); 242 | CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); 243 | 244 | /* Duplicate a cJSON item */ 245 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); 246 | /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will 247 | need to be released. With recurse!=0, it will duplicate any children connected to the item. 248 | The item->next and ->prev pointers are always zero on return from Duplicate. */ 249 | /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. 250 | * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ 251 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); 252 | 253 | 254 | CJSON_PUBLIC(void) cJSON_Minify(char *json); 255 | 256 | /* Helper functions for creating and adding items to an object at the same time. 257 | * They return the added item or NULL on failure. */ 258 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); 259 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); 260 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); 261 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); 262 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); 263 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); 264 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); 265 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); 266 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); 267 | 268 | /* When assigning an integer value, it needs to be propagated to valuedouble too. */ 269 | #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) 270 | /* helper for the cJSON_SetNumberValue macro */ 271 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); 272 | #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) 273 | 274 | /* Macro for iterating over an array or object */ 275 | #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) 276 | 277 | /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ 278 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size); 279 | CJSON_PUBLIC(void) cJSON_free(void *object); 280 | 281 | #ifdef __cplusplus 282 | } 283 | #endif 284 | 285 | #endif 286 | -------------------------------------------------------------------------------- /src/j1939decode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "j1939decode.h" 7 | #include "cJSON.h" 8 | 9 | /* Log function pointer */ 10 | static log_fn_ptr log_fn = NULL; 11 | 12 | /* J1939 lookup table pointer */ 13 | static cJSON * j1939db_json = NULL; 14 | 15 | /* JSON object for all PGNs */ 16 | static cJSON * j1939db_pgns = NULL; 17 | /* JSON object for all SPNs */ 18 | static cJSON * j1939db_spns = NULL; 19 | /* JSON object for all source addresses */ 20 | static cJSON * j1939db_source_addresses = NULL; 21 | 22 | /* Static helper functions */ 23 | static void log_msg(const char * fmt, ...); 24 | static char * file_read(const char * filename, const char * mode); 25 | static bool in_array(uint32_t val, const uint32_t * array, size_t len); 26 | static cJSON * create_byte_array(const uint64_t * data); 27 | static const cJSON * get_pgn_data(uint32_t pgn); 28 | static const cJSON * get_spn_data(uint32_t spn); 29 | static cJSON * extract_spn_data(uint32_t spn, const uint64_t * data, uint32_t start_bit); 30 | static char * get_sa_name(uint8_t sa); 31 | static char * get_pgn_name(uint32_t pgn); 32 | 33 | /* Extract J1939 sub fields from CAN ID */ 34 | static inline uint8_t get_pri(uint32_t id) 35 | { 36 | /* 3-bit priority */ 37 | return (uint8_t) ((id >> (18U + 8U)) & ((1U << 3U) - 1)); 38 | } 39 | static inline uint32_t get_pgn(uint32_t id) 40 | { 41 | /* 18-bit parameter group number */ 42 | return (uint32_t) ((id >> 8U) & ((1U << 18U) - 1)); 43 | } 44 | static inline uint8_t get_sa(uint32_t id) 45 | { 46 | /* 8-bit source address */ 47 | return (uint8_t) ((id >> 0U) & ((1U << 8U) - 1)); 48 | } 49 | 50 | /**************************************************************************//** 51 | 52 | \brief Log formatted message to user defined handler, or stderr as default 53 | 54 | \return void 55 | 56 | ******************************************************************************/ 57 | void log_msg(const char * fmt, ...) 58 | { 59 | char buf[4096]; 60 | va_list args; 61 | va_start(args, fmt); 62 | vsnprintf(buf, sizeof(buf), fmt, args); 63 | va_end(args); 64 | 65 | if (log_fn) 66 | { 67 | (*log_fn)(buf); 68 | } 69 | else 70 | { 71 | /* Default print to stderr */ 72 | fprintf(stderr, "%s\n", buf); 73 | } 74 | } 75 | 76 | /**************************************************************************//** 77 | 78 | \brief Read file contents into allocated memory 79 | 80 | \return pointer to string containing file contents 81 | 82 | ******************************************************************************/ 83 | char * file_read(const char * filename, const char * mode) 84 | { 85 | FILE * fp = fopen(filename, mode); 86 | if (fp == NULL) 87 | { 88 | log_msg("Could not open file %s", filename); 89 | /* No need to close the file before returning since it was never opened */ 90 | return NULL; 91 | } 92 | 93 | /* Get total file size */ 94 | fseek(fp, 0, SEEK_END); 95 | long file_size = ftell(fp); 96 | rewind(fp); 97 | 98 | /* Allocate enough memory for entire file */ 99 | char * buf = malloc(file_size + 1); 100 | if (buf == NULL) 101 | { 102 | log_msg("Memory allocation failure"); 103 | fclose(fp); 104 | return NULL; 105 | } 106 | 107 | /* Read the entire file into a buffer */ 108 | long read_size = fread(buf, 1, file_size, fp); 109 | if (read_size != file_size) 110 | { 111 | log_msg("Read %ld of %ld total bytes in file %s", read_size, file_size, filename); 112 | fclose(fp); 113 | return NULL; 114 | } 115 | 116 | fclose(fp); 117 | 118 | // Null terminator 119 | buf[file_size] = '\0'; 120 | 121 | return buf; 122 | } 123 | 124 | /**************************************************************************//** 125 | 126 | \brief Set log function handler 127 | 128 | \return void 129 | 130 | ******************************************************************************/ 131 | void j1939decode_set_log_fn(log_fn_ptr fn) 132 | { 133 | if (fn) 134 | { 135 | log_fn = fn; 136 | } 137 | } 138 | 139 | /**************************************************************************//** 140 | 141 | \brief Print version string 142 | 143 | \return version string 144 | 145 | ******************************************************************************/ 146 | const char * j1939decode_version(void) 147 | { 148 | static char version[15]; 149 | sprintf(version, "%i.%i.%i", J1939DECODE_VERSION_MAJOR, J1939DECODE_VERSION_MINOR, J1939DECODE_VERSION_PATCH); 150 | 151 | return version; 152 | } 153 | 154 | /**************************************************************************//** 155 | 156 | \brief Initialize and allocate memory for J1939 lookup table 157 | 158 | \return void 159 | 160 | ******************************************************************************/ 161 | void j1939decode_init(void) 162 | { 163 | char * s; 164 | /* Memory-map all file contents */ 165 | s = file_read(J1939DECODE_DB, "r"); 166 | if (s != NULL) 167 | { 168 | j1939db_json = cJSON_Parse(s); 169 | free(s); 170 | if (j1939db_json == NULL) 171 | { 172 | log_msg("Unable to parse J1939db"); 173 | } 174 | } 175 | 176 | /* Pre-allocate large JSON objects */ 177 | j1939db_pgns = cJSON_GetObjectItemCaseSensitive(j1939db_json, "J1939PGNdb"); 178 | j1939db_spns = cJSON_GetObjectItemCaseSensitive(j1939db_json, "J1939SPNdb"); 179 | j1939db_source_addresses = cJSON_GetObjectItemCaseSensitive(j1939db_json, "J1939SATabledb"); 180 | } 181 | 182 | /**************************************************************************//** 183 | 184 | \brief Deinitialize and free memory for J1939 lookup table 185 | 186 | \return void 187 | 188 | ******************************************************************************/ 189 | void j1939decode_deinit(void) 190 | { 191 | /* cJSON_Delete() checks if pointer is NULL before freeing */ 192 | cJSON_Delete(j1939db_json); 193 | 194 | /* Explicitly set pointer to NULL */ 195 | j1939db_json = NULL; 196 | } 197 | 198 | /**************************************************************************//** 199 | 200 | \brief 201 | 202 | \param val value to search 203 | \param array array of values to search in 204 | \param len number of elements in array 205 | 206 | \return bool boolean indicating if value was found in array 207 | 208 | ******************************************************************************/ 209 | bool in_array(uint32_t val, const uint32_t * array, size_t len) 210 | { 211 | for (size_t i = 0; i < len; i++) 212 | { 213 | if (val == array[i]) 214 | { 215 | return true; 216 | } 217 | } 218 | return false; 219 | } 220 | 221 | /**************************************************************************//** 222 | 223 | \brief Split 64 bit number into JSON array object of bytes 224 | 225 | \param data pointer to data (8 bytes total) 226 | 227 | \return cJSON * pointer to the JSON array object 228 | 229 | ******************************************************************************/ 230 | cJSON * create_byte_array(const uint64_t * data) 231 | { 232 | cJSON * array = cJSON_CreateArray(); 233 | if (array == NULL) 234 | { 235 | goto cleanup; 236 | } 237 | 238 | for (uint32_t i = 0; i < sizeof(data); i++) 239 | { 240 | /* Cast to uint8 pointer and then index */ 241 | cJSON * num = cJSON_CreateNumber(((uint8_t *) data)[i]); 242 | if (num == NULL) 243 | { 244 | goto cleanup; 245 | } 246 | cJSON_AddItemToArray(array, num); 247 | } 248 | 249 | return array; 250 | 251 | cleanup: 252 | cJSON_Delete(array); 253 | return NULL; 254 | } 255 | 256 | /**************************************************************************//** 257 | 258 | \brief Get parameter group number data 259 | 260 | \param pgn parameter group number 261 | 262 | \return cJSON * pointer to the PGN data JSON object 263 | 264 | ******************************************************************************/ 265 | const cJSON * get_pgn_data(uint32_t pgn) 266 | { 267 | /* String large enough to fit a six-digit number (18 bits) */ 268 | char pgn_string[7]; 269 | snprintf(pgn_string, sizeof(pgn_string), "%d", pgn); 270 | 271 | const cJSON * pgn_data = cJSON_GetObjectItemCaseSensitive(j1939db_pgns, pgn_string); 272 | 273 | return pgn_data; 274 | } 275 | 276 | /**************************************************************************//** 277 | 278 | \brief Get suspect parameter number data 279 | 280 | \param spn suspect parameter number 281 | 282 | \return cJSON * pointer to the SPN data JSON object 283 | 284 | ******************************************************************************/ 285 | const cJSON * get_spn_data(uint32_t spn) 286 | { 287 | /* String large enough to fit a six-digit number */ 288 | char spn_string[7]; 289 | snprintf(spn_string, sizeof(spn_string), "%d", spn); 290 | 291 | const cJSON * spn_data = cJSON_GetObjectItemCaseSensitive(j1939db_spns, spn_string); 292 | 293 | return spn_data; 294 | } 295 | 296 | /**************************************************************************//** 297 | 298 | \brief Extract suspect parameter number data items and decode SPN value 299 | 300 | \param spn suspect parameter number 301 | \param data pointer to data (8 bytes total) 302 | \param start_bit starting bit of SPN in PGN (zero-order) 303 | 304 | \return cJSON * pointer to the SPN data JSON object 305 | 306 | ******************************************************************************/ 307 | cJSON * extract_spn_data(uint32_t spn, const uint64_t * data, uint32_t start_bit) 308 | { 309 | /* JSON object for specific SPN data */ 310 | const cJSON * spn_data = get_spn_data(spn); 311 | 312 | /* Mutable copy of SPN data object 313 | * We are keeping all existing fields and then adding more of our own */ 314 | /* TODO: Create our own SPN data JSON object rather than copying the existing one 315 | * By creating our own JSON object and adding all fields explicitly we have control over what the key names are */ 316 | cJSON * spn_data_copy = cJSON_Duplicate(spn_data, 1); 317 | 318 | /* TODO: Use PascalCase or snake_case for JSON key names? 319 | * Existing J1939 lookup table uses PascalCase but snake_case may be more appropriate */ 320 | 321 | if (cJSON_IsObject(spn_data)) 322 | { 323 | int length_in_bits = cJSON_GetObjectItemCaseSensitive(spn_data, "SPNLength")->valueint; 324 | int offset = cJSON_GetObjectItemCaseSensitive(spn_data, "Offset")->valueint; 325 | double resolution = cJSON_GetObjectItemCaseSensitive(spn_data, "Resolution")->valuedouble; 326 | double operational_high = cJSON_GetObjectItemCaseSensitive(spn_data, "OperationalHigh")->valuedouble; 327 | double operational_low = cJSON_GetObjectItemCaseSensitive(spn_data, "OperationalLow")->valuedouble; 328 | /* Not currently using name or units 329 | char * name = cJSON_GetObjectItemCaseSensitive(spn_data, "Name")->valuestring; 330 | char * units = cJSON_GetObjectItemCaseSensitive(spn_data, "Units")->valuestring; 331 | */ 332 | 333 | /* TODO: Support bit decodings for when the units are "Bits" */ 334 | /* TODO: Support decoding of ASCII values when resolution is "ASCII" */ 335 | 336 | /* Decode the data for this SPN */ 337 | uint64_t mask = (1U << (unsigned int) length_in_bits) - 1; 338 | uint64_t value_raw = ((*data) >> start_bit) & mask; 339 | double value = value_raw * resolution + offset; 340 | 341 | if (cJSON_AddNumberToObject(spn_data_copy, "StartBit", start_bit) == NULL) 342 | { 343 | goto cleanup; 344 | } 345 | 346 | if (cJSON_AddNumberToObject(spn_data_copy, "ValueRaw", value_raw) == NULL) 347 | { 348 | goto cleanup; 349 | } 350 | 351 | /* Decoded value is valid boolean defaulting to false */ 352 | cJSON_bool valid = false; 353 | 354 | /* Check that decoded value is within operational range */ 355 | if (value >= operational_low && value <= operational_high) 356 | { 357 | if (cJSON_AddNumberToObject(spn_data_copy, "ValueDecoded", value) == NULL) 358 | { 359 | goto cleanup; 360 | } 361 | 362 | valid = true; 363 | } 364 | else 365 | { 366 | /* Decoded value is invalid or not available if outside of operation range 367 | * Use the "Valid" boolean key when checking if decoded data is valid or not */ 368 | if (cJSON_AddStringToObject(spn_data_copy, "ValueDecoded", "Not available") == NULL) 369 | { 370 | goto cleanup; 371 | } 372 | } 373 | 374 | if (cJSON_AddBoolToObject(spn_data_copy, "Valid", valid) == NULL) 375 | { 376 | goto cleanup; 377 | } 378 | } 379 | else 380 | { 381 | log_msg("No SPN data found in database for SPN %d", spn); 382 | goto cleanup; 383 | } 384 | 385 | return spn_data_copy; 386 | 387 | cleanup: 388 | cJSON_Delete(spn_data_copy); 389 | /* Do not return the freed cJSON pointer to avoid use-after-free flaw 390 | * cJSON_Delete() does not set the freed pointer to NULL */ 391 | return NULL; 392 | } 393 | 394 | /**************************************************************************//** 395 | 396 | \brief Get source address name 397 | 398 | \param sa source address number 399 | 400 | \return char * pointer to the source address name string 401 | 402 | ******************************************************************************/ 403 | char * get_sa_name(uint8_t sa) 404 | { 405 | char * sa_name; 406 | 407 | /* Preferred Addresses are in the range of 0 to 127 and 248 to 255 */ 408 | if (sa <= 127 || sa >= 248) 409 | { 410 | /* Source addresses 92 through to 127 have not yet been assigned */ 411 | if (sa >= 92 && sa <= 127) 412 | { 413 | sa_name = "Reserved"; 414 | } 415 | else 416 | { 417 | /* String large enough to fit a three-digit number (8 bits) */ 418 | char sa_string[4]; 419 | snprintf(sa_string, sizeof(sa_string), "%d", sa); 420 | 421 | sa_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(j1939db_source_addresses, sa_string)); 422 | if (sa_name == NULL) 423 | { 424 | sa_name = "Unknown"; 425 | log_msg("No source address name found in database for source address %d", sa); 426 | } 427 | } 428 | } 429 | /* Industry Group specific addresses are in the range of 128 to 247 */ 430 | else if (sa >= 128 && sa <= 247) 431 | { 432 | sa_name = "Industry Group specific"; 433 | } 434 | else 435 | { 436 | sa_name = "Unknown"; 437 | log_msg("Unknown source address %d outside of expected range", sa); 438 | } 439 | 440 | return sa_name; 441 | } 442 | 443 | /**************************************************************************//** 444 | 445 | \brief Get parameter group number name 446 | 447 | \param pgn parameter group number 448 | 449 | \return char * pointer to the PGN name string 450 | 451 | ******************************************************************************/ 452 | char * get_pgn_name(uint32_t pgn) 453 | { 454 | char * pgn_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(get_pgn_data(pgn), "Name")); 455 | if (pgn_name == NULL) 456 | { 457 | pgn_name = "Unknown"; 458 | log_msg("No PGN name found in database for PGN %d", pgn); 459 | } 460 | 461 | return pgn_name; 462 | } 463 | 464 | /**************************************************************************//** 465 | 466 | \brief Build JSON string for j1939 decoded data 467 | 468 | \param id CAN identifier 469 | \param dlc data length code 470 | \param data pointer to data (8 bytes total) 471 | \param pretty pretty print returned JSON string 472 | 473 | \return char * pointer to the JSON string 474 | 475 | ******************************************************************************/ 476 | char * j1939decode_to_json(uint32_t id, uint8_t dlc, const uint64_t * data, bool pretty) 477 | { 478 | /* Fail and return NULL if database is not loaded 479 | * Remember to call j1939decode_init() first! */ 480 | if (j1939db_json == NULL) 481 | { 482 | log_msg("J1939 database not loaded"); 483 | return NULL; 484 | } 485 | 486 | if (dlc > 8) 487 | { 488 | log_msg("DLC cannot be greater than 8 bytes"); 489 | return NULL; 490 | } 491 | 492 | /* JSON string to be returned */ 493 | char * json_string = NULL; 494 | 495 | /* JSON object containing decoded J1939 data */ 496 | cJSON * json_object = cJSON_CreateObject(); 497 | if (json_object == NULL) 498 | { 499 | goto end; 500 | } 501 | 502 | if (cJSON_AddNumberToObject(json_object, "ID", id) == NULL) 503 | { 504 | goto end; 505 | } 506 | 507 | if (cJSON_AddNumberToObject(json_object, "Priority", get_pri(id)) == NULL) 508 | { 509 | goto end; 510 | } 511 | 512 | if (cJSON_AddNumberToObject(json_object, "PGN", get_pgn(id)) == NULL) 513 | { 514 | goto end; 515 | } 516 | 517 | if (cJSON_AddNumberToObject(json_object, "SA", get_sa(id)) == NULL) 518 | { 519 | goto end; 520 | } 521 | 522 | if (cJSON_AddStringToObject(json_object, "SAName", get_sa_name(get_sa(id))) == NULL) 523 | { 524 | goto end; 525 | } 526 | 527 | if (cJSON_AddNumberToObject(json_object, "DLC", dlc) == NULL) 528 | { 529 | goto end; 530 | } 531 | 532 | /* Add raw data bytes to JSON object */ 533 | cJSON_AddItemToObject(json_object, "DataRaw", create_byte_array(data)); 534 | 535 | /* JSON object for specific PGN data */ 536 | const cJSON * pgn_data = get_pgn_data(get_pgn(id)); 537 | 538 | /* Decoded flag default to false until set otherwise */ 539 | cJSON_bool decoded_flag = false; 540 | 541 | if (cJSON_IsObject(pgn_data)) 542 | { 543 | /* PGN number found in lookup table */ 544 | 545 | if (cJSON_AddStringToObject(json_object, "PGNName", get_pgn_name(get_pgn(id))) == NULL) 546 | { 547 | goto end; 548 | } 549 | 550 | /* JSON object containing list of decoded SPN data */ 551 | cJSON * spn_object = cJSON_CreateObject(); 552 | if (spn_object == NULL) 553 | { 554 | goto end; 555 | } 556 | 557 | /* JSON object for SPN list in PGN */ 558 | const cJSON * spn_list_array = cJSON_GetObjectItemCaseSensitive(pgn_data, "SPNs"); 559 | if (cJSON_IsArray(spn_list_array)) 560 | { 561 | uint32_t num_spns = cJSON_GetArraySize(spn_list_array); 562 | if (num_spns > 0) 563 | { 564 | /* One or more SPNs exist for PGN */ 565 | 566 | for (uint32_t i = 0; i < num_spns; i++) 567 | { 568 | /* Getting SPN number from SPN list in PGN since the SPN number is used as a key only and not included in the SPN data object itself */ 569 | uint32_t spn_number = (uint32_t) cJSON_GetArrayItem(spn_list_array, i)->valueint; 570 | 571 | /* Array of all possible proprietary SPNs */ 572 | const uint32_t proprietary_spns[] = {2550, 2551, 3328}; 573 | 574 | /* Check for proprietary SPNs */ 575 | if (in_array(spn_number, proprietary_spns, sizeof(proprietary_spns) / sizeof(proprietary_spns[0]))) 576 | { 577 | /* TODO: Disabling print to silently ignore proprietary SPNs */ 578 | /* log_msg("Skipping decode for proprietary SPN %d", spn_number); */ 579 | } 580 | else 581 | { 582 | /* Need to pass SPN starting bit position since it is found in the PGN data object */ 583 | uint32_t start_bit; 584 | const cJSON * start_bit_json = cJSON_GetArrayItem(cJSON_GetObjectItemCaseSensitive(pgn_data, "SPNStartBits"), i); 585 | if (cJSON_IsNumber(start_bit_json)) 586 | { 587 | if (start_bit_json->valueint < 0) 588 | { 589 | log_msg("Start bit cannot be negative for SPN %d, skipping decode", spn_number); 590 | continue; 591 | } 592 | 593 | /* Now cast to unsigned */ 594 | start_bit = (uint32_t) start_bit_json->valueint; 595 | } 596 | else 597 | { 598 | log_msg("No start bit found in database for SPN %d, skipping decode", spn_number); 599 | continue; 600 | } 601 | 602 | /* Add SPN data object to SPN list object using SPN number as a key */ 603 | char spn_string[7]; 604 | snprintf(spn_string, sizeof(spn_string), "%d", spn_number); 605 | 606 | cJSON * spn_data = extract_spn_data(spn_number, data, start_bit); 607 | if (spn_data != NULL) 608 | { 609 | /* At least one SPN found in database and actually decoded */ 610 | 611 | /* TODO: What criteria should be used to determine if the message should be flagged as "decoded" or not? 612 | * 1. If PGN data found in database? 613 | * 2. If at least one SPN found in database for PGN? 614 | * 3. If at least one SPN, with start bits, found in database for PGN? 615 | * 4. If at least one SPN actually decoded? (i.e. extract_spn_data() did not return NULL) 616 | * Using #4 criteria for now */ 617 | 618 | decoded_flag = true; 619 | } 620 | cJSON_AddItemToObject(spn_object, spn_string, spn_data); 621 | } 622 | } 623 | } 624 | else 625 | { 626 | log_msg("Empty SPN list found in database for PGN %d", get_pgn(id)); 627 | } 628 | } 629 | else 630 | { 631 | log_msg("No SPNs found in database for PGN %d", get_pgn(id)); 632 | } 633 | 634 | /* Add SPN list object to the main JSON object */ 635 | cJSON_AddItemToObject(json_object, "SPNs", spn_object); 636 | } 637 | else 638 | { 639 | /* TODO: This print may happen too often when trying to decode non-J1939 data */ 640 | /* log_msg("PGN %d not found in database", get_pgn(id)); */ 641 | } 642 | 643 | if (cJSON_AddBoolToObject(json_object, "Decoded", decoded_flag) == NULL) 644 | { 645 | goto end; 646 | } 647 | 648 | /* Print the JSON string 649 | * Memory will be allocated so remember to free it when you are done with it! */ 650 | json_string = pretty ? cJSON_Print(json_object) : cJSON_PrintUnformatted(json_object); 651 | if (json_string == NULL) 652 | { 653 | log_msg("Failed to print JSON string"); 654 | } 655 | 656 | end: 657 | cJSON_Delete(json_object); 658 | return json_string; 659 | } 660 | -------------------------------------------------------------------------------- /src/j1939decode.h: -------------------------------------------------------------------------------- 1 | #ifndef J1939DECODE_H 2 | #define J1939DECODE_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | /* Project version */ 12 | #define J1939DECODE_VERSION_MAJOR 3 13 | #define J1939DECODE_VERSION_MINOR 1 14 | #define J1939DECODE_VERSION_PATCH 1 15 | 16 | /* J1939 digital annex JSON filename */ 17 | #define J1939DECODE_DB "J1939db.json" 18 | 19 | /* Log function pointer type */ 20 | typedef void (*log_fn_ptr)(const char *); 21 | 22 | /* Set log function handler */ 23 | void j1939decode_set_log_fn(log_fn_ptr fn); 24 | 25 | /* Print version string */ 26 | const char * j1939decode_version(void); 27 | 28 | /* Initialize and allocate memory for J1939 lookup table */ 29 | void j1939decode_init(void); 30 | 31 | /* Deinitialize and free memory for J1939 lookup table */ 32 | void j1939decode_deinit(void); 33 | 34 | /* Build JSON string for j1939 decoded data 35 | * Memory will be allocated so remember to free the string when you are done with it! */ 36 | /* Example JSON output format: 37 | { 38 | "ID": 419348235, 39 | "Priority": 6, 40 | "PGN": 65215, 41 | "SA": 11, 42 | "SAName": "Brakes - System Controller", 43 | "DLC": 8, 44 | "DataRaw": [170, 15, 125, 125, 125, 125, 255, 255], 45 | "PGNName": "Wheel Speed Information", 46 | "SPNs": { 47 | "904": { 48 | "DataRange": "0 to 250.996 km/h", 49 | "Name": "Front Axle Speed", 50 | "Offset": 0, 51 | "OperationalHigh": 250.996, 52 | "OperationalLow": 0, 53 | "OperationalRange": "", 54 | "Resolution": 0.00390625, 55 | "SPNLength": 16, 56 | "Units": "km/h", 57 | "StartBit": 0, 58 | "ValueRaw": 4010, 59 | "ValueDecoded": 15.6640625, 60 | "Valid": true 61 | }, 62 | ... 63 | }, 64 | "Decoded": true 65 | } 66 | */ 67 | char * j1939decode_to_json(uint32_t id, uint8_t dlc, const uint64_t * data, bool pretty); 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | 73 | #endif //J1939DECODE_H 74 | -------------------------------------------------------------------------------- /test/support/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackm/j1939decode/8e37d61f54e540d58e3f883b08ac59cb112d572b/test/support/.gitkeep -------------------------------------------------------------------------------- /test/test_j1939decode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "unity.h" 6 | 7 | #include "j1939decode.h" 8 | #include "cJSON.h" 9 | 10 | 11 | static uint8_t pri; 12 | static uint32_t pgn; 13 | static uint8_t sa; 14 | static uint8_t dlc; 15 | static uint8_t data[8]; 16 | 17 | /* Compile CAN ID from J1939 sub fields */ 18 | static inline uint32_t get_id(uint8_t pri, uint32_t pgn, uint8_t sa) 19 | { 20 | return (uint32_t) ( 21 | ((pri & ((1U << 3U) - 1)) << (18U + 8U)) + 22 | ((pgn & ((1U << 18U) - 1)) << 8U) + 23 | ((sa & ((1U << 8U) - 1)) << 0U) 24 | ); 25 | } 26 | 27 | 28 | void setUp(void) 29 | { 30 | j1939decode_init(); 31 | 32 | /* Default values */ 33 | pri = 0; 34 | pgn = 0; 35 | sa = 0; 36 | dlc = 8; 37 | for (size_t i = 0; i < sizeof(data); i++) 38 | { 39 | data[i] = 0xFF; 40 | } 41 | } 42 | 43 | void tearDown(void) 44 | { 45 | j1939decode_deinit(); 46 | } 47 | 48 | void test_j1939decode_version_number(void) 49 | { 50 | TEST_ASSERT_EQUAL_STRING("3.1.1", j1939decode_version()); 51 | } 52 | 53 | void test_j1939decode_return_not_null(void) 54 | { 55 | /* If message can be converted to JSON, j1939decode_to_json() will return non-null pointer */ 56 | TEST_ASSERT_NOT_NULL(j1939decode_to_json(get_id(pri, pgn, sa), dlc, (uint64_t *) data, false)); 57 | } 58 | 59 | void test_j1939decode_return_parsable_json(void) 60 | { 61 | char * json_string = j1939decode_to_json(get_id(pri, pgn, sa), dlc, (uint64_t *) data, false); 62 | cJSON * json = cJSON_Parse(json_string); 63 | 64 | /* If JSON string can be parsed, cJSON_Parse() will return non-null pointer */ 65 | TEST_ASSERT_NOT_NULL(json); 66 | 67 | cJSON_Delete(json); 68 | free(json_string); 69 | } 70 | 71 | void test_j1939decode_message_decoded_true(void) 72 | { 73 | /* Set to any real PGN that exists in J1939 database */ 74 | pgn = 0; 75 | 76 | char * json_string = j1939decode_to_json(get_id(pri, pgn, sa), dlc, (uint64_t *) data, false); 77 | cJSON * json = cJSON_Parse(json_string); 78 | 79 | char * item_string = cJSON_Print(cJSON_GetObjectItemCaseSensitive(json, "Decoded")); 80 | 81 | /* "Decoded" key should be true when given a real PGN */ 82 | TEST_ASSERT_EQUAL_STRING("true", item_string); 83 | 84 | free(item_string); 85 | cJSON_Delete(json); 86 | free(json_string); 87 | } 88 | 89 | void test_j1939decode_message_decoded_false(void) 90 | { 91 | /* PGN 1 does not exist in J1939 database */ 92 | pgn = 1; 93 | 94 | char * json_string = j1939decode_to_json(get_id(pri, pgn, sa), dlc, (uint64_t *) data, false); 95 | cJSON * json = cJSON_Parse(json_string); 96 | 97 | char * item_string = cJSON_Print(cJSON_GetObjectItemCaseSensitive(json, "Decoded")); 98 | 99 | /* "Decoded" key should be false when given non-existent PGN */ 100 | TEST_ASSERT_EQUAL_STRING("false", item_string); 101 | 102 | free(item_string); 103 | cJSON_Delete(json); 104 | free(json_string); 105 | } 106 | 107 | void test_j1939decode_message_data_raw(void) 108 | { 109 | /* Set arbitrary data values */ 110 | data[0] = 11; 111 | data[1] = 22; 112 | data[2] = 33; 113 | data[3] = 44; 114 | data[4] = 55; 115 | data[5] = 66; 116 | data[6] = 77; 117 | data[7] = 88; 118 | 119 | char * json_string = j1939decode_to_json(get_id(pri, pgn, sa), dlc, (uint64_t *) data, false); 120 | cJSON * json = cJSON_Parse(json_string); 121 | 122 | char * item_string = cJSON_Print(cJSON_GetObjectItemCaseSensitive(json, "DataRaw")); 123 | 124 | /* "DataRaw" key should be the same as the raw message data bytes */ 125 | TEST_ASSERT_EQUAL_STRING("[11, 22, 33, 44, 55, 66, 77, 88]", item_string); 126 | 127 | free(item_string); 128 | cJSON_Delete(json); 129 | free(json_string); 130 | } 131 | 132 | void test_j1939decode_message_proprietary_pgn(void) 133 | { 134 | /* PGN 65280 is a proprietary PGN for manufacturer defined usage */ 135 | pgn = 65280; 136 | 137 | char * json_string = j1939decode_to_json(get_id(pri, pgn, sa), dlc, (uint64_t *) data, false); 138 | cJSON * json = cJSON_Parse(json_string); 139 | 140 | char * item_string = cJSON_Print(cJSON_GetObjectItemCaseSensitive(json, "Decoded")); 141 | 142 | /* "Decoded" key should be false when given a proprietary PGN */ 143 | TEST_ASSERT_EQUAL_STRING("false", item_string); 144 | 145 | free(item_string); 146 | cJSON_Delete(json); 147 | free(json_string); 148 | } 149 | 150 | void test_j1939decode_message_sa_reserved(void) 151 | { 152 | char * json_string; 153 | cJSON * json; 154 | 155 | for(int i = 92; i <= 127; i++) 156 | { 157 | sa = i; 158 | 159 | json_string = j1939decode_to_json(get_id(pri, pgn, sa), dlc, (uint64_t *) data, false); 160 | json = cJSON_Parse(json_string); 161 | 162 | /* "SAName" key should be set to "Reserved" if SA is between 92 through to 127 inclusive */ 163 | TEST_ASSERT_EQUAL_STRING("Reserved", cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(json, "SAName"))); 164 | } 165 | 166 | cJSON_Delete(json); 167 | free(json_string); 168 | } 169 | 170 | void test_j1939decode_message_sa_industry_group_specific(void) 171 | { 172 | char * json_string; 173 | cJSON * json; 174 | 175 | for(int i = 128; i <= 247; i++) 176 | { 177 | sa = i; 178 | 179 | json_string = j1939decode_to_json(get_id(pri, pgn, sa), dlc, (uint64_t *) data, false); 180 | json = cJSON_Parse(json_string); 181 | 182 | /* "SAName" key should be set to "Industry Group specific" if SA is between 128 through to 247 inclusive */ 183 | TEST_ASSERT_EQUAL_STRING("Industry Group specific", cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(json, "SAName"))); 184 | } 185 | 186 | cJSON_Delete(json); 187 | free(json_string); 188 | } 189 | --------------------------------------------------------------------------------