From 5151c6c2ee575cf238c31bf55db33d64719257a0 Mon Sep 17 00:00:00 2001 From: Madhur Dixit Date: Tue, 24 Mar 2020 07:01:10 +0530 Subject: [PATCH 01/27] Initial Commit --- .../JSONValueExtractor/JSONValueExtractor.ino | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 examples/JSONValueExtractor/JSONValueExtractor.ino diff --git a/examples/JSONValueExtractor/JSONValueExtractor.ino b/examples/JSONValueExtractor/JSONValueExtractor.ino new file mode 100644 index 0000000..c14c5ba --- /dev/null +++ b/examples/JSONValueExtractor/JSONValueExtractor.ino @@ -0,0 +1,69 @@ +/* + JSON Value Extractor + + This sketch demonstrates how to use some features + of the Official Arduino JSON library to traverse through all the + key value pair in the object and the nested objects. + Can be very helpful when searching for a specific data in a key + which is nested at multiple levels + The sketch actually use recursion to traverse all the keys in + a given JSON. + + Example originally added on 24-03-2020 + by Madhur Dixit https://github.com/Chester-King + + This example code is in the public domain. +*/ + +#include + +void setup() { + + Serial.begin(9600); + while (!Serial); + valueExtractor(); + +} + +void loop() { +} + +void valueExtractor() { + + Serial.println("object"); + Serial.println("======"); + JSONVar myObject; + + // Making a JSON Object + myObject["foo"] = "bar"; + myObject["blah"]["abc"] = 42; + myObject["blah"]["efg"] = "pod"; + myObject["blah"]["cde"]["pan1"] = "King"; + myObject["blah"]["cde"]["pan2"] = 3.14; + myObject["jok"]["hij"] = "bar"; + + Serial.println(myObject); + Serial.println(); + Serial.println("Extracted Values"); + Serial.println("======"); + + objRec(myObject); + +} + +void objRec(JSONVar myObject) { + Serial.println("{"); + for (int x = 0; x < myObject.keys().length(); x++) { + if ((JSON.typeof(myObject[myObject.keys()[x]])).equals("object")) { + Serial.print(myObject.keys()[x]); + Serial.println(" : "); + objRec(myObject[myObject.keys()[x]]); + } + else { + Serial.print(myObject.keys()[x]); + Serial.print(" : "); + Serial.println(myObject[myObject.keys()[x]]); + } + } + Serial.println("}"); +} From 964b662b4865b547dcf1684798304f8e9c399580 Mon Sep 17 00:00:00 2001 From: Luigi Gubello Date: Fri, 6 Nov 2020 19:07:14 +0100 Subject: [PATCH 02/27] Update cJSON to v 1.7.14 --- src/cjson/cJSON.c | 416 ++++++++++++++++++++++++++++++---------------- src/cjson/cJSON.h | 50 +++--- 2 files changed, 302 insertions(+), 164 deletions(-) diff --git a/src/cjson/cJSON.c b/src/cjson/cJSON.c index a7edb10..52c5de5 100644 --- a/src/cjson/cJSON.c +++ b/src/cjson/cJSON.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef ENABLE_LOCALES #include @@ -58,9 +59,32 @@ #include "cJSON.h" /* define our own boolean type */ +#ifdef true +#undef true +#endif #define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif #define false ((cJSON_bool)0) +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0/0.0 +#endif +#endif + typedef struct { const unsigned char *json; size_t position; @@ -72,16 +96,28 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) return (const char*) (global_error.json + global_error.position); } -CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { - if (!cJSON_IsString(item)) { +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { return NULL; } return item->valuestring; } +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ -#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 10) +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 14) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif @@ -125,7 +161,7 @@ typedef struct internal_hooks } internal_hooks; #if defined(_MSC_VER) -/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ static void * CJSON_CDECL internal_malloc(size_t size) { return malloc(size); @@ -144,6 +180,9 @@ static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) #define internal_realloc realloc #endif +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) @@ -358,6 +397,33 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) return object->valuedouble = number; } +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + typedef struct { unsigned char *buffer; @@ -470,6 +536,13 @@ static void update_offset(printbuffer * const buffer) buffer->offset += strlen((const char*)buffer_pointer); } +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + /* Render the number nicely from the given item into a string. */ static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { @@ -477,9 +550,9 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out double d = item->valuedouble; int length = 0; size_t i = 0; - unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ unsigned char decimal_point = get_decimal_point(); - double test; + double test = 0.0; if (output_buffer == NULL) { @@ -487,44 +560,12 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out } /* This checks for NaN and Infinity */ - if ((d * 0) != 0) + if (isnan(d) || isinf(d)) { length = sprintf((char*)number_buffer, "null"); } else { -#ifdef __AVR__ - (void)test; - - // zero the buffer - memset(number_buffer, 0x00, sizeof(number_buffer)); - - // add the integer part - ltoa((long)d, (char*)number_buffer, 10); - length = strlen((char*)number_buffer); - - // calculate the number of decimal points (up to 9) - long decimal = (long)((d - (long)d) * 1000000000L); - - if (decimal < 0) { - // negative decimal, make it positive - decimal *= -1; - } - - // trim the trailing zeros - while (decimal && (decimal % 10) == 0) { - decimal /= 10; - } - - if (decimal) { - // add the decimal part - number_buffer[length] = decimal_point; - - ltoa(decimal, (char*)&number_buffer[length + 1], 10); - - length = strlen((char*)number_buffer); - } -#else /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ length = sprintf((char*)number_buffer, "%1.15g", d); @@ -534,10 +575,9 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out /* If not, print with 17 decimal places of precision */ length = sprintf((char*)number_buffer, "%1.17g", d); } -#endif } - /* sprintf failed or buffer overrun occured */ + /* sprintf failed or buffer overrun occurred */ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { return false; @@ -1000,6 +1040,11 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) return NULL; } + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { buffer->offset++; @@ -1029,8 +1074,23 @@ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) return buffer; } -/* Parse an object - create a new root, and populate. */ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) { parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; cJSON *item = NULL; @@ -1039,13 +1099,13 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return global_error.json = NULL; global_error.position = 0; - if (value == NULL) + if (value == NULL || 0 == buffer_length) { goto fail; } buffer.content = (const unsigned char*)value; - buffer.length = strlen((const char*)value) + sizeof(""); + buffer.length = buffer_length; buffer.offset = 0; buffer.hooks = global_hooks; @@ -1115,7 +1175,12 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) return cJSON_ParseWithOpts(value, 0, 0); } -#define cjson_min(a, b) ((a < b) ? a : b) +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) { @@ -1222,20 +1287,20 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON return (char*)p.buffer; } -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - if ((len < 0) || (buf == NULL)) + if ((length < 0) || (buffer == NULL)) { return false; } - p.buffer = (unsigned char*)buf; - p.length = (size_t)len; + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; p.offset = 0; p.noalloc = true; - p.format = fmt; + p.format = format; p.hooks = global_hooks; return print_value(item, &p); @@ -1448,6 +1513,10 @@ static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buf success: input_buffer->depth--; + if (head != NULL) { + head->prev = current_item; + } + item->type = cJSON_Array; item->child = head; @@ -1588,7 +1657,7 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu buffer_skip_whitespace(input_buffer); if (!parse_string(current_item, input_buffer)) { - goto fail; /* faile to parse name */ + goto fail; /* failed to parse name */ } buffer_skip_whitespace(input_buffer); @@ -1620,6 +1689,10 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu success: input_buffer->depth--; + if (head != NULL) { + head->prev = current_item; + } + item->type = cJSON_Object; item->child = head; @@ -1882,35 +1955,39 @@ static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { cJSON *child = NULL; - if ((item == NULL) || (array == NULL)) + if ((item == NULL) || (array == NULL) || (array == item)) { return false; } child = array->child; - + /* + * To find the last item in array quickly, we use prev in array + */ if (child == NULL) { /* list is empty, start new one */ array->child = item; + item->prev = item; + item->next = NULL; } else { /* append to the end */ - while (child->next) + if (child->prev) { - child = child->next; + suffix_object(child->prev, item); + array->child->prev = item; } - suffix_object(child, item); } return true; } /* Add item to array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) { - add_item_to_array(array, item); + return add_item_to_array(array, item); } #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) @@ -1934,7 +2011,7 @@ static cJSON_bool add_item_to_object(cJSON * const object, const char * const st char *new_key = NULL; int new_type = cJSON_Invalid; - if ((object == NULL) || (string == NULL) || (item == NULL)) + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { return false; } @@ -1966,35 +2043,35 @@ static cJSON_bool add_item_to_object(cJSON * const object, const char * const st return add_item_to_array(object, item); } -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) { - add_item_to_object(object, string, item, &global_hooks, false); + return add_item_to_object(object, string, item, &global_hooks, false); } /* Add an item to an object with constant string as key */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { - add_item_to_object(object, string, item, &global_hooks, true); + return add_item_to_object(object, string, item, &global_hooks, true); } -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) { if (array == NULL) { - return; + return false; } - add_item_to_array(array, create_reference(item, &global_hooks)); + return add_item_to_array(array, create_reference(item, &global_hooks)); } -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) { if ((object == NULL) || (string == NULL)) { - return; + return false; } - add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); } CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) @@ -2112,7 +2189,7 @@ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const it return NULL; } - if (item->prev != NULL) + if (item != parent->child) { /* not the first element */ item->prev->next = item->next; @@ -2128,6 +2205,12 @@ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const it /* first element */ parent->child = item->next; } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + /* make sure the detached item doesn't point anywhere anymore */ item->prev = NULL; item->next = NULL; @@ -2175,20 +2258,19 @@ CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const } /* Replace array/object items with new ones. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { cJSON *after_inserted = NULL; if (which < 0) { - return; + return false; } after_inserted = get_array_item(array, (size_t)which); if (after_inserted == NULL) { - add_item_to_array(array, newitem); - return; + return add_item_to_array(array, newitem); } newitem->next = after_inserted; @@ -2202,6 +2284,7 @@ CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newit { newitem->prev->next = newitem; } + return true; } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) @@ -2223,14 +2306,28 @@ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON { replacement->next->prev = replacement; } - if (replacement->prev != NULL) - { - replacement->prev->next = replacement; - } if (parent->child == item) { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } parent->child = replacement; } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } item->next = NULL; item->prev = NULL; @@ -2239,14 +2336,14 @@ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON return true; } -CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { if (which < 0) { - return; + return false; } - cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); } static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) @@ -2264,19 +2361,17 @@ static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSO replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); replacement->type &= ~cJSON_StringIsConst; - cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); - - return true; + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); } -CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { - replace_item_in_object(object, string, newitem, false); + return replace_item_in_object(object, string, newitem, false); } -CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { - replace_item_in_object(object, string, newitem, true); + return replace_item_in_object(object, string, newitem, true); } /* Create basic types: */ @@ -2313,12 +2408,12 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { - item->type = b ? cJSON_True : cJSON_False; + item->type = boolean ? cJSON_True : cJSON_False; } return item; @@ -2471,6 +2566,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) } p = n; } + a->child->prev = n; return a; } @@ -2507,6 +2603,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) } p = n; } + a->child->prev = n; return a; } @@ -2543,11 +2640,12 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) } p = n; } + a->child->prev = n; return a; } -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) { size_t i = 0; cJSON *n = NULL; @@ -2579,6 +2677,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) } p = n; } + a->child->prev = n; return a; } @@ -2651,6 +2750,10 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) } child = child->next; } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } return newitem; @@ -2663,69 +2766,96 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) return NULL; } -CJSON_PUBLIC(void) cJSON_Minify(char *json) +static void skip_oneline_comment(char **input) { - unsigned char *into = (unsigned char*)json; + *input += static_strlen("//"); - if (json == NULL) + for (; (*input)[0] != '\0'; ++(*input)) { - return; + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); - while (*json) + for (; (*input)[0] != '\0'; ++(*input)) { - if (*json == ' ') - { - json++; - } - else if (*json == '\t') + if (((*input)[0] == '*') && ((*input)[1] == '/')) { - /* Whitespace characters. */ - json++; + *input += static_strlen("*/"); + return; } - else if (*json == '\r') - { - json++; - } - else if (*json=='\n') - { - json++; - } - else if ((*json == '/') && (json[1] == '/')) - { - /* double-slash comments, to end of line. */ - while (*json && (*json != '\n')) - { - json++; - } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); } - else if ((*json == '/') && (json[1] == '*')) + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) { - /* multiline comments. */ - while (*json && !((*json == '*') && (json[1] == '/'))) - { + case ' ': + case '\t': + case '\r': + case '\n': json++; - } - json += 2; - } - else if (*json == '\"') - { - /* string literals, which are \" sensitive. */ - *into++ = (unsigned char)*json++; - while (*json && (*json != '\"')) - { - if (*json == '\\') + break; + + case '/': + if (json[1] == '/') { - *into++ = (unsigned char)*json++; + skip_oneline_comment(&json); } - *into++ = (unsigned char)*json++; - } - *into++ = (unsigned char)*json++; - } - else - { - /* All other characters. */ - *into++ = (unsigned char)*json++; + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; } } @@ -2872,7 +3002,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons return true; case cJSON_Number: - if (a->valuedouble == b->valuedouble) + if (compare_double(a->valuedouble, b->valuedouble)) { return true; } @@ -2966,4 +3096,4 @@ CJSON_PUBLIC(void *) cJSON_malloc(size_t size) CJSON_PUBLIC(void) cJSON_free(void *object) { global_hooks.deallocate(object); -} +} \ No newline at end of file diff --git a/src/cjson/cJSON.h b/src/cjson/cJSON.h index 3279f6b..4ebdd90 100644 --- a/src/cjson/cJSON.h +++ b/src/cjson/cJSON.h @@ -81,7 +81,7 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ /* project version */ #define CJSON_VERSION_MAJOR 1 #define CJSON_VERSION_MINOR 7 -#define CJSON_VERSION_PATCH 10 +#define CJSON_VERSION_PATCH 14 #include @@ -146,9 +146,11 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); /* Render a cJSON entity to text for transfer/storage. */ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); @@ -160,7 +162,7 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); @@ -173,8 +175,9 @@ CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *st /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); -/* Check if the item is a string and return its valuestring */ -CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); /* These functions check the type of an item */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); @@ -203,29 +206,30 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); /* Create a string where valuestring references a string so * it will not be freed by cJSON_Delete */ CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); -/* Create an object/arrray that only references it's elements so +/* Create an object/array that only references it's elements so * they will not be freed by cJSON_Delete */ CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); -/* These utilities create an Array of count items. */ +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); /* Append item to the specified array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before * writing to `item->string` */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); -/* Remove/Detatch items from Arrays/Objects. */ +/* Remove/Detach items from Arrays/Objects. */ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); @@ -235,22 +239,24 @@ CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); /* Update array items. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); -CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); -CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); -CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will -need to be released. With recurse!=0, it will duplicate any children connected to the item. -The item->next and ->prev pointers are always zero on return from Duplicate. */ + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); - +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable adress area. */ CJSON_PUBLIC(void) cJSON_Minify(char *json); /* Helper functions for creating and adding items to an object at the same time. @@ -270,6 +276,8 @@ CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * c /* helper for the cJSON_SetNumberValue macro */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); /* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) @@ -282,4 +290,4 @@ CJSON_PUBLIC(void) cJSON_free(void *object); } #endif -#endif +#endif \ No newline at end of file From 2ca94a48ef3dc0aef5f985619a9cab5fd97e12fe Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Tue, 12 Mar 2019 16:28:43 -0400 Subject: [PATCH 03/27] Use two ltoa calls with trimming instead of sprintf on AVR --- src/cjson/cJSON.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/cjson/cJSON.c b/src/cjson/cJSON.c index 52c5de5..0e459ab 100644 --- a/src/cjson/cJSON.c +++ b/src/cjson/cJSON.c @@ -566,6 +566,38 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out } else { +#ifdef __AVR__ + (void)test; + + // zero the buffer + memset(number_buffer, 0x00, sizeof(number_buffer)); + + // add the integer part + ltoa((long)d, (char*)number_buffer, 10); + length = strlen((char*)number_buffer); + + // calculate the number of decimal points (up to 9) + long decimal = (long)((d - (long)d) * 1000000000L); + + if (decimal < 0) { + // negative decimal, make it positive + decimal *= -1; + } + + // trim the trailing zeros + while (decimal && (decimal % 10) == 0) { + decimal /= 10; + } + + if (decimal) { + // add the decimal part + number_buffer[length] = decimal_point; + + ltoa(decimal, (char*)&number_buffer[length + 1], 10); + + length = strlen((char*)number_buffer); + } +#else /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ length = sprintf((char*)number_buffer, "%1.15g", d); @@ -575,6 +607,7 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out /* If not, print with 17 decimal places of precision */ length = sprintf((char*)number_buffer, "%1.17g", d); } +#endif } /* sprintf failed or buffer overrun occurred */ From f4d6b976ec76aecb8dee23e2e9e7f3e5d365c079 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 4 May 2021 03:46:04 -0700 Subject: [PATCH 04/27] Configure Dependabot to check for outdated actions used in workflows Dependabot will periodically check the versions of all actions used in the repository's workflows. If any are found to be outdated, it will submit a pull request to update them. NOTE: Dependabot's PRs will sometimes try to pin to the patch version of the action (e.g., updating `uses: foo/bar@v1` to `uses: foo/bar@v2.3.4`). When the action author has provided a major version ref, use that instead (e.g., `uses: foo/bar@v2`). Dependabot will automatically close its PR once the workflow has been updated. More information: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..03600dd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +# See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file +version: 2 + +updates: + # Configure check for outdated GitHub Actions actions in workflows. + # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot + - package-ecosystem: github-actions + directory: / # Check the repository's workflows under /.github/workflows/ + schedule: + interval: daily From 1377727cdac380167dab82ca4e3a5f7c0e275016 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 4 May 2021 03:46:38 -0700 Subject: [PATCH 05/27] Add CI workflow to check for commonly misspelled words On every push, pull request, and periodically, use the codespell-project/actions-codespell action to check for commonly misspelled words. In the event of a false positive, the problematic word should be added, in all lowercase, to the ignore-words-list field of ./.codespellrc. Regardless of the case of the word in the false positive, it must be in all lowercase in the ignore list. The ignore list is comma-separated with no spaces. --- .codespellrc | 7 +++++++ .github/workflows/spell-check.yml | 22 ++++++++++++++++++++++ README.adoc | 7 ++++++- 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 .codespellrc create mode 100644 .github/workflows/spell-check.yml diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..4db8948 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,7 @@ +# See: https://github.com/codespell-project/codespell#using-a-config-file +[codespell] +# In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: +ignore-words-list = writen +check-filenames = +check-hidden = +skip = ./.git,./src/cjson diff --git a/.github/workflows/spell-check.yml b/.github/workflows/spell-check.yml new file mode 100644 index 0000000..01bee87 --- /dev/null +++ b/.github/workflows/spell-check.yml @@ -0,0 +1,22 @@ +name: Spell Check + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + pull_request: + schedule: + # Run every Tuesday at 8 AM UTC to catch new misspelling detections resulting from dictionary updates. + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + spellcheck: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Spell check + uses: codespell-project/actions-codespell@master diff --git a/README.adoc b/README.adoc index d77961a..1f9f22a 100644 --- a/README.adoc +++ b/README.adoc @@ -1,4 +1,9 @@ -= JSON Library for Arduino = +:repository-owner: arduino-libraries +:repository-name: Arduino_JSON + += {repository-name} Library for Arduino = + +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml/badge.svg["Spell Check status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml"] Process JSON in your Arduino sketches. From 7e7a05dde24c0fd2ffb90b923c374e94593874a2 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 4 May 2021 03:55:01 -0700 Subject: [PATCH 06/27] Correct typos in comments and documentation --- examples/JSONArray/JSONArray.ino | 6 +++--- examples/JSONKitchenSink/JSONKitchenSink.ino | 2 +- examples/JSONObject/JSONObject.ino | 8 ++++---- keywords.txt | 2 +- src/Arduino_JSON.h | 2 +- src/JSON.cpp | 2 +- src/JSON.h | 2 +- src/JSONVar.cpp | 2 +- src/JSONVar.h | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/JSONArray/JSONArray.ino b/examples/JSONArray/JSONArray.ino index e6a6c94..3fb80a8 100644 --- a/examples/JSONArray/JSONArray.ino +++ b/examples/JSONArray/JSONArray.ino @@ -2,7 +2,7 @@ JSON Array This sketch demonstrates how to use various features - of the Official Arduino JSON library, in particular for JSON arrays. + of the Official Arduino_JSON library, in particular for JSON arrays. This example code is in the public domain. */ @@ -29,7 +29,7 @@ void demoParse() { JSONVar myArray = JSON.parse(input); - // JSON.typeof(jsonVar) can be used to get the type of the var + // JSON.typeof(jsonVar) can be used to get the type of the variable if (JSON.typeof(myArray) == "undefined") { Serial.println("Parsing input failed!"); return; @@ -77,7 +77,7 @@ void demoCreation() { Serial.print("myArray.length() = "); Serial.println(myArray.length()); - // JSON.stringify(myVar) can be used to convert the json var to a String + // JSON.stringify(myVar) can be used to convert the JSONVar to a String String jsonString = JSON.stringify(myArray); Serial.print("JSON.stringify(myArray) = "); diff --git a/examples/JSONKitchenSink/JSONKitchenSink.ino b/examples/JSONKitchenSink/JSONKitchenSink.ino index 164d0e2..f224505 100644 --- a/examples/JSONKitchenSink/JSONKitchenSink.ino +++ b/examples/JSONKitchenSink/JSONKitchenSink.ino @@ -2,7 +2,7 @@ JSON Kitchen Sink This sketch demonstrates how to use various features - of the Official Arduino JSON library. + of the Official Arduino_JSON library. This example code is in the public domain. */ diff --git a/examples/JSONObject/JSONObject.ino b/examples/JSONObject/JSONObject.ino index cf4bbfd..786185a 100644 --- a/examples/JSONObject/JSONObject.ino +++ b/examples/JSONObject/JSONObject.ino @@ -2,7 +2,7 @@ JSON Object This sketch demonstrates how to use various features - of the Official Arduino JSON library, in particular for JSON objects. + of the Official Arduino_JSON library, in particular for JSON objects. This example code is in the public domain. */ @@ -29,7 +29,7 @@ void demoParse() { JSONVar myObject = JSON.parse(input); - // JSON.typeof(jsonVar) can be used to get the type of the var + // JSON.typeof(jsonVar) can be used to get the type of the variable if (JSON.typeof(myObject) == "undefined") { Serial.println("Parsing input failed!"); return; @@ -63,7 +63,7 @@ void demoParse() { Serial.println((const char*) myObject["foo"]); } - // JSON vars can be printed using print or println + // JSONVars can be printed using print or println Serial.print("myObject = "); Serial.println(myObject); @@ -83,7 +83,7 @@ void demoCreation() { Serial.print("myObject.keys() = "); Serial.println(myObject.keys()); - // JSON.stringify(myVar) can be used to convert the json var to a String + // JSON.stringify(myVar) can be used to convert the JSONVar to a String String jsonString = JSON.stringify(myObject); Serial.print("JSON.stringify(myObject) = "); diff --git a/keywords.txt b/keywords.txt index a5db85c..42d80cd 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,5 +1,5 @@ ####################################### -# Syntax Coloring Map For JSON +# Syntax Coloring Map For Arduino_JSON ####################################### # Class ####################################### diff --git a/src/Arduino_JSON.h b/src/Arduino_JSON.h index 80cb2a1..3b75f03 100644 --- a/src/Arduino_JSON.h +++ b/src/Arduino_JSON.h @@ -1,5 +1,5 @@ /* - This file is part of the Arduino JSON library. + This file is part of the Arduino_JSON library. Copyright (c) 2019 Arduino SA. All rights reserved. This library is free software; you can redistribute it and/or diff --git a/src/JSON.cpp b/src/JSON.cpp index b620c9e..9d0d9ef 100644 --- a/src/JSON.cpp +++ b/src/JSON.cpp @@ -1,5 +1,5 @@ /* - This file is part of the Arduino JSON library. + This file is part of the Arduino_JSON library. Copyright (c) 2019 Arduino SA. All rights reserved. This library is free software; you can redistribute it and/or diff --git a/src/JSON.h b/src/JSON.h index 664d0d0..ab0b851 100644 --- a/src/JSON.h +++ b/src/JSON.h @@ -1,5 +1,5 @@ /* - This file is part of the Arduino JSON library. + This file is part of the Arduino_JSON library. Copyright (c) 2019 Arduino SA. All rights reserved. This library is free software; you can redistribute it and/or diff --git a/src/JSONVar.cpp b/src/JSONVar.cpp index 9df3c7c..247fcc3 100644 --- a/src/JSONVar.cpp +++ b/src/JSONVar.cpp @@ -1,5 +1,5 @@ /* - This file is part of the Arduino JSON library. + This file is part of the Arduino_JSON library. Copyright (c) 2019 Arduino SA. All rights reserved. This library is free software; you can redistribute it and/or diff --git a/src/JSONVar.h b/src/JSONVar.h index b6863bc..58a7213 100644 --- a/src/JSONVar.h +++ b/src/JSONVar.h @@ -1,5 +1,5 @@ /* - This file is part of the Arduino JSON library. + This file is part of the Arduino_JSON library. Copyright (c) 2019 Arduino SA. All rights reserved. This library is free software; you can redistribute it and/or From 1db9c0376b179bba47e9ebc662e8f3c0203f5dab Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 4 May 2021 03:55:18 -0700 Subject: [PATCH 07/27] Add CI workflow to do Arduino project-specific linting On every push, pull request, and periodically, run Arduino Lint to check for common problems not related to the project code. --- .github/workflows/check-arduino.yml | 28 ++++++++++++++++++++++++++++ README.adoc | 1 + 2 files changed, 29 insertions(+) create mode 100644 .github/workflows/check-arduino.yml diff --git a/.github/workflows/check-arduino.yml b/.github/workflows/check-arduino.yml new file mode 100644 index 0000000..5df60fc --- /dev/null +++ b/.github/workflows/check-arduino.yml @@ -0,0 +1,28 @@ +name: Check Arduino + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + pull_request: + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Arduino Lint + uses: arduino/arduino-lint-action@v1 + with: + compliance: strict + library-manager: update + # Always use this setting for official repositories. Remove for 3rd party projects. + official: true + project-type: library diff --git a/README.adoc b/README.adoc index 1f9f22a..c1a9a80 100644 --- a/README.adoc +++ b/README.adoc @@ -3,6 +3,7 @@ = {repository-name} Library for Arduino = +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml/badge.svg["Check Arduino status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml"] image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml/badge.svg["Spell Check status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml"] Process JSON in your Arduino sketches. From 43f62b89df82dc08b2f07b8321a6d4ea0f37d336 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 4 May 2021 03:55:33 -0700 Subject: [PATCH 08/27] Add license file This defines the license in a standardized place, which allows GitHub's automated license detection system to detect the license type: https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/licensing-a-repository#detecting-a-license This is the exact license text provided by choosealicense.com This text should not be modified in any way. Doing so may interfere with GitHub's license detection or even the licence's function as a legal instrument. --- LICENSE.txt | 504 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 504 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..8000a6f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! From 87a6333689c60b748f751c46dd90224e5a700b0a Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 4 May 2021 03:56:01 -0700 Subject: [PATCH 09/27] Add "smoke test" examples compilation CI workflow On every push or pull request that affects library source or example files, and periodically, compile all example sketches for the specified boards. --- .github/workflows/compile-examples.yml | 95 ++++++++++++++++++++++++++ README.adoc | 1 + 2 files changed, 96 insertions(+) create mode 100644 .github/workflows/compile-examples.yml diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml new file mode 100644 index 0000000..3fb24c4 --- /dev/null +++ b/.github/workflows/compile-examples.yml @@ -0,0 +1,95 @@ +name: Compile Examples + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/compile-examples.yml" + - "library.properties" + - "examples/**" + - "src/**" + pull_request: + paths: + - ".github/workflows/compile-examples.yml" + - "library.properties" + - "examples/**" + - "src/**" + schedule: + # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). + - cron: "0 8 * * TUE" + workflow_dispatch: + repository_dispatch: + +jobs: + build: + name: ${{ matrix.board.fqbn }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + matrix: + board: + - fqbn: arduino:avr:nano + platforms: | + - name: arduino:avr + - fqbn: arduino:avr:mega + platforms: | + - name: arduino:avr + - fqbn: arduino:avr:leonardo + platforms: | + - name: arduino:avr + - fqbn: arduino:avr:yun + platforms: | + - name: arduino:avr + - fqbn: arduino:megaavr:uno2018 + platforms: | + - name: arduino:megaavr + - fqbn: arduino:megaavr:nona4809 + platforms: | + - name: arduino:megaavr + - fqbn: arduino:sam:arduino_due_x_dbg + platforms: | + - name: arduino:sam + - fqbn: arduino:samd:arduino_zero_edbg + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:mkrzero + platforms: | + - name: arduino:samd + - fqbn: arduino:samd:nano_33_iot + platforms: | + - name: arduino:samd + - fqbn: arduino:mbed_portenta:envie_m4 + platforms: | + - name: arduino:mbed_portenta + - fqbn: arduino:mbed_portenta:envie_m7 + platforms: | + - name: arduino:mbed_portenta + - fqbn: arduino:mbed_edge:edge_control + platforms: | + - name: arduino:mbed_edge + - fqbn: arduino:mbed_nano:nano33ble + platforms: | + - name: arduino:mbed_nano + - fqbn: arduino:mbed_nano:nanorp2040connect + platforms: | + - name: arduino:mbed_nano + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Compile examples + uses: arduino/compile-sketches@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fqbn: ${{ matrix.board.fqbn }} + platforms: ${{ matrix.board.platforms }} + libraries: | + # Install the library from the local path. + - source-path: ./ + # Additional library dependencies can be listed here. + # See: https://github.com/arduino/compile-sketches#libraries + sketch-paths: | + - examples diff --git a/README.adoc b/README.adoc index c1a9a80..04f99b6 100644 --- a/README.adoc +++ b/README.adoc @@ -4,6 +4,7 @@ = {repository-name} Library for Arduino = image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml/badge.svg["Check Arduino status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-arduino.yml"] +image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml/badge.svg["Compile Examples status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/compile-examples.yml"] image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml/badge.svg["Spell Check status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/spell-check.yml"] Process JSON in your Arduino sketches. From 09984ca08b838d82db712f85352be7cafd939047 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 4 May 2021 03:56:16 -0700 Subject: [PATCH 10/27] Report changes in memory usage that would result from merging a PR On creation or commit to a pull request, a report of the resulting change in memory usage of the examples will be commented to the PR thread. --- .github/workflows/compile-examples.yml | 12 ++++++++++++ .github/workflows/report-size-deltas.yml | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 .github/workflows/report-size-deltas.yml diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 3fb24c4..113aaa9 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -25,6 +25,9 @@ jobs: name: ${{ matrix.board.fqbn }} runs-on: ubuntu-latest + env: + SKETCHES_REPORTS_PATH: sketches-reports + strategy: fail-fast: false @@ -93,3 +96,12 @@ jobs: # See: https://github.com/arduino/compile-sketches#libraries sketch-paths: | - examples + enable-deltas-report: true + sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} + + - name: Save sketches report as workflow artifact + uses: actions/upload-artifact@v2 + with: + if-no-files-found: error + path: ${{ env.SKETCHES_REPORTS_PATH }} + name: ${{ env.SKETCHES_REPORTS_PATH }} diff --git a/.github/workflows/report-size-deltas.yml b/.github/workflows/report-size-deltas.yml new file mode 100644 index 0000000..652be5d --- /dev/null +++ b/.github/workflows/report-size-deltas.yml @@ -0,0 +1,24 @@ +name: Report Size Deltas + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/report-size-deltas.yml" + schedule: + # Run at the minimum interval allowed by GitHub Actions. + # Note: GitHub Actions periodically has outages which result in workflow failures. + # In this event, the workflows will start passing again once the service recovers. + - cron: "*/5 * * * *" + workflow_dispatch: + repository_dispatch: + +jobs: + report: + runs-on: ubuntu-latest + steps: + - name: Comment size deltas reports to PRs + uses: arduino/report-size-deltas@v1 + with: + # The name of the workflow artifact created by the sketch compilation workflow + sketches-reports-source: sketches-reports From fcbe4634e00d16f2e2437511f10b98785801e8dc Mon Sep 17 00:00:00 2001 From: Martin Mueller Date: Tue, 25 May 2021 07:47:51 -0400 Subject: [PATCH 11/27] Added support for char, unsigned char, short, unsigned short and unsigned int. --- src/JSONVar.cpp | 104 +++++++++++++++++++++++++++++++++++++++++++++--- src/JSONVar.h | 21 +++++++++- 2 files changed, 118 insertions(+), 7 deletions(-) diff --git a/src/JSONVar.cpp b/src/JSONVar.cpp index 247fcc3..4f5ce68 100644 --- a/src/JSONVar.cpp +++ b/src/JSONVar.cpp @@ -33,12 +33,42 @@ JSONVar::JSONVar(bool b) : *this = b; } +JSONVar::JSONVar (char i) : + JSONVar () +{ + *this = i; +} + +JSONVar::JSONVar (unsigned char i) : + JSONVar () +{ + *this = i; +} + +JSONVar::JSONVar (short i) : + JSONVar () +{ + *this = i; +} + +JSONVar::JSONVar (unsigned short i) : + JSONVar () +{ + *this = i; +} + JSONVar::JSONVar(int i) : JSONVar() { *this = i; } +JSONVar::JSONVar (unsigned int i) : + JSONVar () +{ + *this = i; +} + JSONVar::JSONVar(long l) : JSONVar() { @@ -132,14 +162,44 @@ JSONVar::operator bool() const return cJSON_IsBool(_json) && cJSON_IsTrue(_json); } -JSONVar::operator int() const +JSONVar::operator char () const +{ + return cJSON_IsNumber (_json) ? _json->valueint : 0; +} + +JSONVar::operator unsigned char () const +{ + return cJSON_IsNumber (_json) ? _json->valueint : 0; +} + +JSONVar::operator short () const +{ + return cJSON_IsNumber (_json) ? _json->valueint : 0; +} + +JSONVar::operator unsigned short () const +{ + return cJSON_IsNumber (_json) ? _json->valueint : 0; +} + +JSONVar::operator int () const { - return cJSON_IsNumber(_json) ? _json->valueint : 0; + return cJSON_IsNumber (_json) ? _json->valueint : 0; } -JSONVar::operator long() const +JSONVar::operator unsigned int () const { - return cJSON_IsNumber(_json) ? _json->valueint : 0; + return cJSON_IsNumber (_json) ? _json->valueint : 0; +} + +JSONVar::operator long () const +{ + return cJSON_IsNumber (_json) ? _json->valueint : 0; +} + +JSONVar::operator unsigned long () const +{ + return cJSON_IsNumber (_json) ? _json->valueint : 0; } JSONVar::operator double() const @@ -156,6 +216,15 @@ JSONVar::operator const char*() const return NULL; } +JSONVar::operator const String () const +{ + if (cJSON_IsString (_json)) { + return String(_json->valuestring); + } + + return String(); +} + void JSONVar::operator=(const JSONVar& v) { if (&v == &undefined) { @@ -196,9 +265,34 @@ void JSONVar::operator=(bool b) replaceJson(b ? cJSON_CreateTrue() : cJSON_CreateFalse()); } +void JSONVar::operator=(char i) +{ + replaceJson (cJSON_CreateNumber (i)); +} + +void JSONVar::operator=(unsigned char i) +{ + replaceJson (cJSON_CreateNumber (i)); +} + +void JSONVar::operator=(short i) +{ + replaceJson (cJSON_CreateNumber (i)); +} + +void JSONVar::operator=(unsigned short i) +{ + replaceJson (cJSON_CreateNumber (i)); +} + void JSONVar::operator=(int i) { - replaceJson(cJSON_CreateNumber(i)); + replaceJson (cJSON_CreateNumber (i)); +} + +void JSONVar::operator=(unsigned int i) +{ + replaceJson (cJSON_CreateNumber (i)); } void JSONVar::operator=(long l) diff --git a/src/JSONVar.h b/src/JSONVar.h index 58a7213..c240f7d 100644 --- a/src/JSONVar.h +++ b/src/JSONVar.h @@ -31,7 +31,12 @@ class JSONVar : public Printable { public: JSONVar(); JSONVar(bool b); + JSONVar(char i); + JSONVar(unsigned char i); + JSONVar(short i); + JSONVar(unsigned short i); JSONVar(int i); + JSONVar(unsigned int i); JSONVar(long l); JSONVar(unsigned long ul); JSONVar(double d); @@ -47,17 +52,29 @@ class JSONVar : public Printable { virtual size_t printTo(Print& p) const; operator bool() const; + operator char() const; + operator unsigned char() const; + operator short() const; + operator unsigned short() const; operator int() const; - operator long() const; + operator unsigned int() const; + operator long () const; + operator unsigned long () const; operator double() const; - operator const char*() const; + operator const char* () const; + operator const String () const; void operator=(const JSONVar& v); #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) JSONVar& operator=(JSONVar&& v); #endif void operator=(bool b); + void operator=(char i); + void operator=(unsigned char i); + void operator=(short i); + void operator=(unsigned short i); void operator=(int i); + void operator=(unsigned int i); void operator=(long l); void operator=(unsigned long ul); void operator=(double d); From 7905d144b3f2f3fda0f99d514bf98445971f5b69 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 10 Jan 2022 01:08:53 -0800 Subject: [PATCH 12/27] Add GitHub Actions workflow to synchronize with shared repository labels (#30) On every push that changes relevant files, and periodically, configure the repository's issue and pull request labels according to the universal, shared, and local label configuration files. --- .github/dependabot.yml | 2 + .github/workflows/sync-labels.yml | 138 ++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 .github/workflows/sync-labels.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 03600dd..fa738ec 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,3 +8,5 @@ updates: directory: / # Check the repository's workflows under /.github/workflows/ schedule: interval: daily + labels: + - "topic: infrastructure" diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml new file mode 100644 index 0000000..3ee6feb --- /dev/null +++ b/.github/workflows/sync-labels.yml @@ -0,0 +1,138 @@ +# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md +name: Sync Labels + +# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/sync-labels.ya?ml" + - ".github/label-configuration-files/*.ya?ml" + pull_request: + paths: + - ".github/workflows/sync-labels.ya?ml" + - ".github/label-configuration-files/*.ya?ml" + schedule: + # Run daily at 8 AM UTC to sync with changes to shared label configurations. + - cron: "0 8 * * *" + workflow_dispatch: + repository_dispatch: + +env: + CONFIGURATIONS_FOLDER: .github/label-configuration-files + CONFIGURATIONS_ARTIFACT: label-configuration-files + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Download JSON schema for labels configuration file + id: download-schema + uses: carlosperate/download-file-action@v1 + with: + file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json + location: ${{ runner.temp }}/label-configuration-schema + + - name: Install JSON schema validator + run: | + sudo npm install \ + --global \ + ajv-cli \ + ajv-formats + + - name: Validate local labels configuration + run: | + # See: https://github.com/ajv-validator/ajv-cli#readme + ajv validate \ + --all-errors \ + -c ajv-formats \ + -s "${{ steps.download-schema.outputs.file-path }}" \ + -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}" + + download: + needs: check + runs-on: ubuntu-latest + + strategy: + matrix: + filename: + # Filenames of the shared configurations to apply to the repository in addition to the local configuration. + # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels + - universal.yml + + steps: + - name: Download + uses: carlosperate/download-file-action@v1 + with: + file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} + + - name: Pass configuration files to next job via workflow artifact + uses: actions/upload-artifact@v2 + with: + path: | + *.yaml + *.yml + if-no-files-found: error + name: ${{ env.CONFIGURATIONS_ARTIFACT }} + + sync: + needs: download + runs-on: ubuntu-latest + + steps: + - name: Set environment variables + run: | + # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable + echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV" + + - name: Determine whether to dry run + id: dry-run + if: > + github.event_name == 'pull_request' || + ( + ( + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' + ) && + github.ref != format('refs/heads/{0}', github.event.repository.default_branch) + ) + run: | + # Use of this flag in the github-label-sync command will cause it to only check the validity of the + # configuration. + echo "::set-output name=flag::--dry-run" + + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Download configuration files artifact + uses: actions/download-artifact@v2 + with: + name: ${{ env.CONFIGURATIONS_ARTIFACT }} + path: ${{ env.CONFIGURATIONS_FOLDER }} + + - name: Remove unneeded artifact + uses: geekyeggo/delete-artifact@v1 + with: + name: ${{ env.CONFIGURATIONS_ARTIFACT }} + + - name: Merge label configuration files + run: | + # Merge all configuration files + shopt -s extglob + cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}" + + - name: Install github-label-sync + run: sudo npm install --global github-label-sync + + - name: Sync labels + env: + GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # See: https://github.com/Financial-Times/github-label-sync + github-label-sync \ + --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \ + ${{ steps.dry-run.outputs.flag }} \ + ${{ github.repository }} From eda25200fef375d93bdc15228deff2a7ece60eeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 21:15:21 +0000 Subject: [PATCH 13/27] Bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/check-arduino.yml | 2 +- .github/workflows/compile-examples.yml | 2 +- .github/workflows/spell-check.yml | 2 +- .github/workflows/sync-labels.yml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check-arduino.yml b/.github/workflows/check-arduino.yml index 5df60fc..6730396 100644 --- a/.github/workflows/check-arduino.yml +++ b/.github/workflows/check-arduino.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Arduino Lint uses: arduino/arduino-lint-action@v1 diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 113aaa9..8647bdd 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -81,7 +81,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Compile examples uses: arduino/compile-sketches@v1 diff --git a/.github/workflows/spell-check.yml b/.github/workflows/spell-check.yml index 01bee87..3f6b03f 100644 --- a/.github/workflows/spell-check.yml +++ b/.github/workflows/spell-check.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Spell check uses: codespell-project/actions-codespell@master diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index 3ee6feb..4ea5755 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Download JSON schema for labels configuration file id: download-schema @@ -105,7 +105,7 @@ jobs: echo "::set-output name=flag::--dry-run" - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Download configuration files artifact uses: actions/download-artifact@v2 From 2058d1fdc6b2ee0c955e17e7c4fbfa7b9ed2d709 Mon Sep 17 00:00:00 2001 From: Joe Andolina Date: Wed, 2 Mar 2022 10:55:36 -0800 Subject: [PATCH 14/27] Adding JSON helpers --- src/JSONVar.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ src/JSONVar.h | 6 ++++++ 2 files changed, 60 insertions(+) diff --git a/src/JSONVar.cpp b/src/JSONVar.cpp index 247fcc3..e933ff8 100644 --- a/src/JSONVar.cpp +++ b/src/JSONVar.cpp @@ -412,4 +412,58 @@ void JSONVar::replaceJson(struct cJSON* json) } } +//--------------------------------------------------------------------- + +bool JSONVar::hasPropertyEqualTo(const String& key, String& value) const{ + return hasPropertyEqualTo(key.c_str(), value.c_str()); +} + +//--------------------------------------------------------------------- + +bool JSONVar::hasPropertyEqualTo(const char* key, const char* value) const { + if(!hasProperty(sourceProperty)){ + return false; + } + + if(strcmp(targetValue, source[sourceProperty]) == 0){ + return true; + } + + return false; +} + +//--------------------------------------------------------------------- + +JSONVar JSONVar::getPropertyWithValue(const String& key, String& value, String child = "") const { + return getPropertyWithValue(key..c_str(), value.c_str(), child.c_str()); +} + +//--------------------------------------------------------------------- + +JSONVar JSONVar::getPropertyWithValue(const char* key, const char* value, const char* child = '') const { + if(this.hasOwnPropertyEqualTo(key, value)){ + if(source.hasOwnProperty(childName)){ + return source[childName]; + } + else { + return source; + } + } + + if(JSON.typeof_(source) == "array"){ + for (int i = 0; i < source.length(); ++i) { + if(_hasMatch(source[i], sourceProperty, targetValue)){ + if(source[i].hasOwnProperty(childName)){ + return source[i][childName]; + } + else { + return source[i]; + } + } + } + } + + return null; +} + JSONVar undefined; diff --git a/src/JSONVar.h b/src/JSONVar.h index 58a7213..e6b16b4 100644 --- a/src/JSONVar.h +++ b/src/JSONVar.h @@ -77,6 +77,12 @@ class JSONVar : public Printable { JSONVar keys() const; bool hasOwnProperty(const char* key) const; bool hasOwnProperty(const String& key) const; + + bool haPropertyEqualTo(const String& key, String& value) const; + bool hasPropertyEqualTo(const char* key, const char* value) const; + + JSONVar getPropertyWithValue(const String& key, String& value, String child = "") const; + JSONVar getPropertyWithValue(const char* key, const char* value, const char* child = '') const; static JSONVar parse(const char* s); static JSONVar parse(const String& s); From 1dac1328e6beee7aa85828a6c890301ac0d91290 Mon Sep 17 00:00:00 2001 From: Joe Andolina Date: Wed, 2 Mar 2022 11:06:08 -0800 Subject: [PATCH 15/27] More Work --- src/JSONVar.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/JSONVar.cpp b/src/JSONVar.cpp index e933ff8..e9c61ba 100644 --- a/src/JSONVar.cpp +++ b/src/JSONVar.cpp @@ -415,17 +415,17 @@ void JSONVar::replaceJson(struct cJSON* json) //--------------------------------------------------------------------- bool JSONVar::hasPropertyEqualTo(const String& key, String& value) const{ - return hasPropertyEqualTo(key.c_str(), value.c_str()); + return this.hasPropertyEqualTo(key.c_str(), value.c_str()); } //--------------------------------------------------------------------- bool JSONVar::hasPropertyEqualTo(const char* key, const char* value) const { - if(!hasProperty(sourceProperty)){ + if(!this.hasProperty(key)){ return false; } - if(strcmp(targetValue, source[sourceProperty]) == 0){ + if(strcmp(value, this[key]) == 0){ return true; } @@ -435,29 +435,29 @@ bool JSONVar::hasPropertyEqualTo(const char* key, const char* value) const { //--------------------------------------------------------------------- JSONVar JSONVar::getPropertyWithValue(const String& key, String& value, String child = "") const { - return getPropertyWithValue(key..c_str(), value.c_str(), child.c_str()); + return this.getPropertyWithValue(key..c_str(), value.c_str(), child.c_str()); } //--------------------------------------------------------------------- JSONVar JSONVar::getPropertyWithValue(const char* key, const char* value, const char* child = '') const { if(this.hasOwnPropertyEqualTo(key, value)){ - if(source.hasOwnProperty(childName)){ - return source[childName]; + if(this.hasOwnProperty(child)){ + return this[child]; } else { - return source; + return this; } } - if(JSON.typeof_(source) == "array"){ - for (int i = 0; i < source.length(); ++i) { - if(_hasMatch(source[i], sourceProperty, targetValue)){ - if(source[i].hasOwnProperty(childName)){ - return source[i][childName]; + if(JSON.typeof_(this) == "array"){ + for (int i = 0; i < this.length(); ++i) { + if(this.hasPropertyEqualTo(key, value)){ + if(this[i].hasOwnProperty(child)){ + return this[i][child]; } else { - return source[i]; + return this[i]; } } } From 1da99788176f26ed6db2a97e88a8038597b7da2b Mon Sep 17 00:00:00 2001 From: Joe Andolina Date: Thu, 3 Mar 2022 10:44:23 -0800 Subject: [PATCH 16/27] Added filtering and property comparison --- src/JSONVar.cpp | 92 ++++++++++++++++++++++++++++++------------------- src/JSONVar.h | 16 +++++---- 2 files changed, 66 insertions(+), 42 deletions(-) diff --git a/src/JSONVar.cpp b/src/JSONVar.cpp index e9c61ba..39a6fa5 100644 --- a/src/JSONVar.cpp +++ b/src/JSONVar.cpp @@ -334,7 +334,6 @@ bool JSONVar::hasOwnProperty(const char* key) const } cJSON* json = cJSON_GetObjectItemCaseSensitive(_json, key); - return (json != NULL); } @@ -414,56 +413,77 @@ void JSONVar::replaceJson(struct cJSON* json) //--------------------------------------------------------------------- -bool JSONVar::hasPropertyEqualTo(const String& key, String& value) const{ - return this.hasPropertyEqualTo(key.c_str(), value.c_str()); -} - -//--------------------------------------------------------------------- +bool JSONVar::hasPropertyEqual(const char* key, const char* value) const { + // Serial.printf("JSONVar::hasPropertyEqual - %s == %s\n", key, value); -bool JSONVar::hasPropertyEqualTo(const char* key, const char* value) const { - if(!this.hasProperty(key)){ + if (!cJSON_IsObject(_json)) { return false; } - if(strcmp(value, this[key]) == 0){ - return true; - } - - return false; -} + cJSON* json = cJSON_GetObjectItemCaseSensitive(_json, key); + // Serial.printf("JSONVar::hasPropertyEqual - Found %s\n", json->valuestring); + return json != NULL && strcmp(value, json->valuestring) == 0; +} //--------------------------------------------------------------------- -JSONVar JSONVar::getPropertyWithValue(const String& key, String& value, String child = "") const { - return this.getPropertyWithValue(key..c_str(), value.c_str(), child.c_str()); -} +bool JSONVar::hasPropertyEqual(const char* key, const JSONVar& value) const { + return this->hasPropertyEqual(key, (const char*)value); +} //--------------------------------------------------------------------- -JSONVar JSONVar::getPropertyWithValue(const char* key, const char* value, const char* child = '') const { - if(this.hasOwnPropertyEqualTo(key, value)){ - if(this.hasOwnProperty(child)){ - return this[child]; - } - else { - return this; +bool JSONVar::hasPropertyEqual(const String& key, const String& value) const { + return this->hasPropertyEqual(key.c_str(), value.c_str()); +} + +//--------------------------------------------------------------------- + +bool JSONVar::hasPropertyEqual(const String& key, const JSONVar& value) const { + return this->hasPropertyEqual(key.c_str(), (const char*)value); +} + +//--------------------------------------------------------------------- + +JSONVar JSONVar::filter(const char* key, const char* value) const { + cJSON* item; + cJSON* test; + cJSON* json = cJSON_CreateArray(); + + if(JSONVar::typeof_((*this)) != "array"){ + test = cJSON_GetObjectItemCaseSensitive(_json, key); + if(test != NULL && strcmp(value, test->valuestring) == 0){ + cJSON_AddItemToArray(json, _json); } + return JSONVar(json, _json); } - - if(JSON.typeof_(this) == "array"){ - for (int i = 0; i < this.length(); ++i) { - if(this.hasPropertyEqualTo(key, value)){ - if(this[i].hasOwnProperty(child)){ - return this[i][child]; - } - else { - return this[i]; - } - } + + for (int i = 0; i < cJSON_GetArraySize(_json); ++i) { + item = cJSON_GetArrayItem(_json, i); + if (item == NULL) { + continue; + } + + test = cJSON_GetObjectItemCaseSensitive(item, key); + + if(test != NULL && strcmp(value, test->valuestring) == 0){ + cJSON_AddItemToArray(json, item); } } - return null; + return JSONVar(json, _json); +} + +JSONVar JSONVar::filter(const char* key, const JSONVar& value) const { + return this->filter(key, (const char*)value); +} + +JSONVar JSONVar::filter(const String& key, const String& value) const { + return this->filter(key.c_str(), value.c_str()); +} + +JSONVar JSONVar::filter(const String& key, const JSONVar& value) const { + return this->filter(key.c_str(), (const char*)value); } JSONVar undefined; diff --git a/src/JSONVar.h b/src/JSONVar.h index e6b16b4..5e14588 100644 --- a/src/JSONVar.h +++ b/src/JSONVar.h @@ -71,18 +71,22 @@ class JSONVar : public Printable { JSONVar operator[](const char* key); JSONVar operator[](const String& key); JSONVar operator[](int index); - JSONVar operator[](const JSONVar& key); + JSONVar operator[](const JSONVar& key); int length() const; JSONVar keys() const; bool hasOwnProperty(const char* key) const; bool hasOwnProperty(const String& key) const; - bool haPropertyEqualTo(const String& key, String& value) const; - bool hasPropertyEqualTo(const char* key, const char* value) const; - - JSONVar getPropertyWithValue(const String& key, String& value, String child = "") const; - JSONVar getPropertyWithValue(const char* key, const char* value, const char* child = '') const; + bool hasPropertyEqual(const char* key, const char* value) const; + bool hasPropertyEqual(const char* key, const JSONVar& value) const; + bool hasPropertyEqual(const String& key, const String& value) const; + bool hasPropertyEqual(const String& key, const JSONVar& value) const; + + JSONVar filter(const char* key, const char* value) const; + JSONVar filter(const char* key, const JSONVar& value) const; + JSONVar filter(const String& key, const String& value) const; + JSONVar filter(const String& key, const JSONVar& value) const; static JSONVar parse(const char* s); static JSONVar parse(const String& s); From 7ab7c5af463d2c0e7131f79f11fc21c0a8b84695 Mon Sep 17 00:00:00 2001 From: Joe Andolina Date: Fri, 4 Mar 2022 09:44:19 -0800 Subject: [PATCH 17/27] Added cJSON_Duplicate to found items. --- src/JSONVar.cpp | 79 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 7 deletions(-) diff --git a/src/JSONVar.cpp b/src/JSONVar.cpp index 39a6fa5..e799b36 100644 --- a/src/JSONVar.cpp +++ b/src/JSONVar.cpp @@ -445,32 +445,97 @@ bool JSONVar::hasPropertyEqual(const String& key, const JSONVar& value) const //--------------------------------------------------------------------- +// JSONVar JSONVar::filter(const char* key, const char* value) const { +// JSONVar item; +// cJSON* test; +// cJSON* json = cJSON_CreateArray(); + +// Serial.printf("JSONVar::filter - %s == %s\n", key, value); + +// // if(!cJSON_IsArray(_json)){ +// // // target = cJSON_CreateArray(); +// // // cJSON_AddItemToArray(target, _json); +// // return (*this); +// // } + +// // Serial.printf("JSON SIZE %d", cJSON_GetArraySize(_json)); + +// Serial.printf("This an array %d\n", (*this).length()); + +// for (int i = 0; i < (*this).length(); i++) { +// Serial.println("GettingItem"); +// item = this[(int)i]; +// Serial.println(item); +// Serial.println(item[(const char*)key]); +// Serial.println("GotItem"); + +// // Serial.println("Loop " + String(i)); +// // Serial.println(this->operator[](i));//cJSON_GetArrayItem(_json, i); + +// // if (item == NULL) { +// // Serial.println("Loop Null"); +// // continue; +// // } + +// if(item.hasPropertyEqual(key, value)){ +// Serial.println("Got Match"); +// return item; +// } +// else { +// Serial.println("NO Match"); +// } +// } + + // if(cJSON_GetArraySize(json) == 0){ + // Serial.println("Returning Null"); + // return NULL; + // } + // else if(cJSON_GetArraySize(json) == 1){ + // Serial.println("Returning Single"); + // return JSONVar(cJSON_GetArrayItem(json, 0), (*this)._json); + // } + + // // Serial.println("Returning Array"); +// return JSONVar(); +// } + JSONVar JSONVar::filter(const char* key, const char* value) const { cJSON* item; cJSON* test; cJSON* json = cJSON_CreateArray(); - if(JSONVar::typeof_((*this)) != "array"){ - test = cJSON_GetObjectItemCaseSensitive(_json, key); + Serial.printf("JSONVar::filter - %s == %s\n", key, value); + + if(!cJSON_IsArray(_json)){ + test = cJSON_GetObjectItem(_json, key); + if(test != NULL && strcmp(value, test->valuestring) == 0){ - cJSON_AddItemToArray(json, _json); + return JSONVar(cJSON_Duplicate(item,true), _json); } - return JSONVar(json, _json); } - for (int i = 0; i < cJSON_GetArraySize(_json); ++i) { + for (int i = 0; i < cJSON_GetArraySize(_json); i++) { item = cJSON_GetArrayItem(_json, i); + if (item == NULL) { continue; } - test = cJSON_GetObjectItemCaseSensitive(item, key); + test = cJSON_GetObjectItem(item, key); if(test != NULL && strcmp(value, test->valuestring) == 0){ - cJSON_AddItemToArray(json, item); + cJSON_AddItemToArray(json, cJSON_Duplicate(item,true)); } } + if(cJSON_GetArraySize(json) == 0){ + return JSONVar(NULL, NULL); + } + + if(cJSON_GetArraySize(json) == 1){ + return JSONVar(cJSON_GetArrayItem(json, 0), _json); + } + return JSONVar(json, _json); } From 607acab3f2195a9fb97b4d0986a6c06582e3c73c Mon Sep 17 00:00:00 2001 From: Joe Andolina Date: Fri, 4 Mar 2022 10:06:24 -0800 Subject: [PATCH 18/27] Updated the object filter implementation --- src/JSONVar.cpp | 63 ++----------------------------------------------- 1 file changed, 2 insertions(+), 61 deletions(-) diff --git a/src/JSONVar.cpp b/src/JSONVar.cpp index e799b36..60dc0d8 100644 --- a/src/JSONVar.cpp +++ b/src/JSONVar.cpp @@ -414,14 +414,11 @@ void JSONVar::replaceJson(struct cJSON* json) //--------------------------------------------------------------------- bool JSONVar::hasPropertyEqual(const char* key, const char* value) const { - // Serial.printf("JSONVar::hasPropertyEqual - %s == %s\n", key, value); - if (!cJSON_IsObject(_json)) { return false; } cJSON* json = cJSON_GetObjectItemCaseSensitive(_json, key); - // Serial.printf("JSONVar::hasPropertyEqual - Found %s\n", json->valuestring); return json != NULL && strcmp(value, json->valuestring) == 0; } @@ -445,72 +442,16 @@ bool JSONVar::hasPropertyEqual(const String& key, const JSONVar& value) const //--------------------------------------------------------------------- -// JSONVar JSONVar::filter(const char* key, const char* value) const { -// JSONVar item; -// cJSON* test; -// cJSON* json = cJSON_CreateArray(); - -// Serial.printf("JSONVar::filter - %s == %s\n", key, value); - -// // if(!cJSON_IsArray(_json)){ -// // // target = cJSON_CreateArray(); -// // // cJSON_AddItemToArray(target, _json); -// // return (*this); -// // } - -// // Serial.printf("JSON SIZE %d", cJSON_GetArraySize(_json)); - -// Serial.printf("This an array %d\n", (*this).length()); - -// for (int i = 0; i < (*this).length(); i++) { -// Serial.println("GettingItem"); -// item = this[(int)i]; -// Serial.println(item); -// Serial.println(item[(const char*)key]); -// Serial.println("GotItem"); - -// // Serial.println("Loop " + String(i)); -// // Serial.println(this->operator[](i));//cJSON_GetArrayItem(_json, i); - -// // if (item == NULL) { -// // Serial.println("Loop Null"); -// // continue; -// // } - -// if(item.hasPropertyEqual(key, value)){ -// Serial.println("Got Match"); -// return item; -// } -// else { -// Serial.println("NO Match"); -// } -// } - - // if(cJSON_GetArraySize(json) == 0){ - // Serial.println("Returning Null"); - // return NULL; - // } - // else if(cJSON_GetArraySize(json) == 1){ - // Serial.println("Returning Single"); - // return JSONVar(cJSON_GetArrayItem(json, 0), (*this)._json); - // } - - // // Serial.println("Returning Array"); -// return JSONVar(); -// } - JSONVar JSONVar::filter(const char* key, const char* value) const { cJSON* item; cJSON* test; cJSON* json = cJSON_CreateArray(); - Serial.printf("JSONVar::filter - %s == %s\n", key, value); - - if(!cJSON_IsArray(_json)){ + if(cJSON_IsObject(_json)){ test = cJSON_GetObjectItem(_json, key); if(test != NULL && strcmp(value, test->valuestring) == 0){ - return JSONVar(cJSON_Duplicate(item,true), _json); + return (*this); } } From ad0494ffc6d2eea323c058e6beaf1267c3b3f18a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 21:09:30 +0000 Subject: [PATCH 19/27] Bump actions/download-artifact from 2 to 3 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 3. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/sync-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index 4ea5755..e84e803 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -108,7 +108,7 @@ jobs: uses: actions/checkout@v3 - name: Download configuration files artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: ${{ env.CONFIGURATIONS_ARTIFACT }} path: ${{ env.CONFIGURATIONS_FOLDER }} From 9b679fcdfc6c1ab6907169e29d6afd840979fdc8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 21:09:34 +0000 Subject: [PATCH 20/27] Bump actions/upload-artifact from 2 to 3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/compile-examples.yml | 2 +- .github/workflows/sync-labels.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 8647bdd..1c4e188 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -100,7 +100,7 @@ jobs: sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} - name: Save sketches report as workflow artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: if-no-files-found: error path: ${{ env.SKETCHES_REPORTS_PATH }} diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index 4ea5755..1d969d5 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -70,7 +70,7 @@ jobs: file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} - name: Pass configuration files to next job via workflow artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: path: | *.yaml From 6bf729d4742cb9d8e4a884682fb59f098ebaa211 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Oct 2022 21:10:37 +0000 Subject: [PATCH 21/27] Bump geekyeggo/delete-artifact from 1 to 2 Bumps [geekyeggo/delete-artifact](https://github.com/geekyeggo/delete-artifact) from 1 to 2. - [Release notes](https://github.com/geekyeggo/delete-artifact/releases) - [Commits](https://github.com/geekyeggo/delete-artifact/compare/v1...v2) --- updated-dependencies: - dependency-name: geekyeggo/delete-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/sync-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index 986bda6..10abaea 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -114,7 +114,7 @@ jobs: path: ${{ env.CONFIGURATIONS_FOLDER }} - name: Remove unneeded artifact - uses: geekyeggo/delete-artifact@v1 + uses: geekyeggo/delete-artifact@v2 with: name: ${{ env.CONFIGURATIONS_ARTIFACT }} From f17c8f738f757478acdf21d7ca5057c5bd791b95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Oct 2022 21:15:10 +0000 Subject: [PATCH 22/27] Bump carlosperate/download-file-action from 1 to 2 Bumps [carlosperate/download-file-action](https://github.com/carlosperate/download-file-action) from 1 to 2. - [Release notes](https://github.com/carlosperate/download-file-action/releases) - [Commits](https://github.com/carlosperate/download-file-action/compare/v1...v2) --- updated-dependencies: - dependency-name: carlosperate/download-file-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/sync-labels.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml index 10abaea..94938f3 100644 --- a/.github/workflows/sync-labels.yml +++ b/.github/workflows/sync-labels.yml @@ -31,7 +31,7 @@ jobs: - name: Download JSON schema for labels configuration file id: download-schema - uses: carlosperate/download-file-action@v1 + uses: carlosperate/download-file-action@v2 with: file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json location: ${{ runner.temp }}/label-configuration-schema @@ -65,7 +65,7 @@ jobs: steps: - name: Download - uses: carlosperate/download-file-action@v1 + uses: carlosperate/download-file-action@v2 with: file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} From 07033185ad8c16e6c0b275cd2efec90e04ddcd15 Mon Sep 17 00:00:00 2001 From: Dirk-Willem van Gulik Date: Thu, 27 Oct 2022 16:31:24 +0200 Subject: [PATCH 23/27] Add unsigned long support; addresses issues #39 --- examples/JSONObject/JSONObject.ino | 9 ++++++++- src/JSONVar.cpp | 5 +++++ src/JSONVar.h | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/examples/JSONObject/JSONObject.ino b/examples/JSONObject/JSONObject.ino index 786185a..9291588 100644 --- a/examples/JSONObject/JSONObject.ino +++ b/examples/JSONObject/JSONObject.ino @@ -78,7 +78,14 @@ void demoCreation() { myObject["hello"] = "world"; myObject["true"] = true; - myObject["x"] = 42; + + myObject["x1"] = (int) 42; + myObject["x2"] = (long) 42; + myObject["x3"] = (unsigned long) 42; + + int x1 = myObject["x1"]; + long x2 = myObject["x2"]; + unsigned long x3 = myObject["x3"]; Serial.print("myObject.keys() = "); Serial.println(myObject.keys()); diff --git a/src/JSONVar.cpp b/src/JSONVar.cpp index 247fcc3..8adc085 100644 --- a/src/JSONVar.cpp +++ b/src/JSONVar.cpp @@ -142,6 +142,11 @@ JSONVar::operator long() const return cJSON_IsNumber(_json) ? _json->valueint : 0; } +JSONVar::operator unsigned long() const +{ + return cJSON_IsNumber(_json) ? _json->valueint : 0; +} + JSONVar::operator double() const { return cJSON_IsNumber(_json) ? _json->valuedouble : NAN; diff --git a/src/JSONVar.h b/src/JSONVar.h index 58a7213..0bf71f0 100644 --- a/src/JSONVar.h +++ b/src/JSONVar.h @@ -49,6 +49,7 @@ class JSONVar : public Printable { operator bool() const; operator int() const; operator long() const; + operator unsigned long() const; operator double() const; operator const char*() const; From 0bb22497cf13c27ba29ca8075e0be64c1c524bf9 Mon Sep 17 00:00:00 2001 From: Dirk-Willem van Gulik Date: Thu, 27 Oct 2022 16:33:39 +0200 Subject: [PATCH 24/27] Better test case added --- examples/JSONObject/JSONObject.ino | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/JSONObject/JSONObject.ino b/examples/JSONObject/JSONObject.ino index 9291588..6298727 100644 --- a/examples/JSONObject/JSONObject.ino +++ b/examples/JSONObject/JSONObject.ino @@ -84,8 +84,13 @@ void demoCreation() { myObject["x3"] = (unsigned long) 42; int x1 = myObject["x1"]; + assert(x1 == 42); + long x2 = myObject["x2"]; + assert(x2 == 42); + unsigned long x3 = myObject["x3"]; + assert(x3 == 42); Serial.print("myObject.keys() = "); Serial.println(myObject.keys()); @@ -127,4 +132,4 @@ void demoCreation() { Serial.print("myObject = "); Serial.println(myObject); -} +} \ No newline at end of file From e8f2289a75a0b90ce31cfde658ff457ca15082c6 Mon Sep 17 00:00:00 2001 From: Dirk-Willem van Gulik Date: Thu, 27 Oct 2022 16:38:01 +0200 Subject: [PATCH 25/27] Nano platform needs assert.h explicitly. Added. --- examples/JSONObject/JSONObject.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/JSONObject/JSONObject.ino b/examples/JSONObject/JSONObject.ino index 6298727..157ab8d 100644 --- a/examples/JSONObject/JSONObject.ino +++ b/examples/JSONObject/JSONObject.ino @@ -8,6 +8,7 @@ */ #include +#include const char input[] = "{\"result\":true,\"count\":42,\"foo\":\"bar\"}"; @@ -132,4 +133,4 @@ void demoCreation() { Serial.print("myObject = "); Serial.println(myObject); -} \ No newline at end of file +} From ff955e9a182dc412092b21eed90d43d34dece2b0 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Thu, 10 Nov 2022 09:47:24 +0100 Subject: [PATCH 26/27] Fix merge-introduced conflict. --- src/JSONVar.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/JSONVar.cpp b/src/JSONVar.cpp index b180310..4f5ce68 100644 --- a/src/JSONVar.cpp +++ b/src/JSONVar.cpp @@ -202,11 +202,6 @@ JSONVar::operator unsigned long () const return cJSON_IsNumber (_json) ? _json->valueint : 0; } -JSONVar::operator unsigned long() const -{ - return cJSON_IsNumber(_json) ? _json->valueint : 0; -} - JSONVar::operator double() const { return cJSON_IsNumber(_json) ? _json->valuedouble : NAN; From f01229c3b6aff97414b2400ffbaaea9d614ff1e6 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Thu, 10 Nov 2022 10:01:04 +0100 Subject: [PATCH 27/27] Release v0.2.0 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 27d6a81..2e47fee 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Arduino_JSON -version=0.1.0 +version=0.2.0 author=Arduino maintainer=Arduino sentence=[BETA] Process JSON in your Arduino sketches.