diff --git a/docs/01.overview.md b/docs/01.overview.md index f19e13d..34eae5c 100644 --- a/docs/01.overview.md +++ b/docs/01.overview.md @@ -16,20 +16,18 @@ Features & TODOs * [x] No use of malloc() (json.h only) * [x] No extra library dependencies. (Only C standard libraries used.) * [x] Only need to include a single file (emJSON.h or json.h) +* [X] No printf(), scanf(). * [x] JSON encoding * [x] JSON decoding * [x] Support for 8 bit CPU (such as AVR) -* [ ] No sprintf(), sscanf(). -* [ ] Merge functions -* [ ] Thread-safe -* [ ] Data alignment * [x] Support String type * [x] Support Integer type * [x] Support Number type (floating point) -* [ ] Support object type +* [x] Support object type * [ ] Support Boolean literals (true, false) * [ ] Support Null literal (null) * [ ] Support array type +* [ ] Merge functions Simple Examples --------------- diff --git a/emJSON/json.c b/emJSON/json.c index 818356b..4238f29 100644 --- a/emJSON/json.c +++ b/emJSON/json.c @@ -6,8 +6,16 @@ #define PERTURB_SHIFT 5 // Private functions + +struct result_ +{ + int status; + size_t idx; +}; + static int get_idx_(json_t *obj, char *key); -static int insert_(json_t *obj, char *key, void *value, size_t size, json_type_t type); +static struct result_ insert_(json_t *obj, char *key, void *value, size_t size, json_type_t type); +static void table_move_ptr_ (void *dest, void *source, json_t *obj); /******************************************************************************* * Core Hash function @@ -68,6 +76,7 @@ json_t json_init(void *buffer, size_t buf_size, size_t table_size) }; struct header_ *header = header_ptr_(&new_obj); *header = (struct header_){ + .parent = NULL, .buf_size = buf_size, .buf_idx = sizeof(struct header_) + table_size * sizeof(struct entry_), .table_size = table_size, @@ -122,18 +131,18 @@ int json_clear(json_t *obj) * Insertion functions ******************************************************************************/ -int json_insert(json_t *obj, char *key, char *value, json_type_t type) +int json_insert(json_t *obj, char *key, void *value, json_type_t type) { switch (type) { case JSON_INT: return json_insert_int(obj, key, *(int *)value); - break; case JSON_FLOAT: return json_insert_float(obj, key, *(float *)value); - break; case JSON_STRING: return json_insert_str(obj, key, value); + case JSON_OBJECT: + return json_insert_obj(obj, key, value); default: return JSON_ERROR; } @@ -141,12 +150,12 @@ int json_insert(json_t *obj, char *key, char *value, json_type_t type) int json_insert_int(json_t *obj, char *key, int32_t value) { - return insert_(obj, key, &value, sizeof(int), JSON_INT); + return insert_(obj, key, &value, sizeof(int), JSON_INT).status; } int json_insert_float(json_t *obj, char *key, float value) { - return insert_(obj, key, &value, sizeof(float), JSON_FLOAT); + return insert_(obj, key, &value, sizeof(float), JSON_FLOAT).status; } int json_insert_str(json_t *obj, char *key, char *value) @@ -157,7 +166,25 @@ int json_insert_str(json_t *obj, char *key, char *value) memset(str_tmp, 0, size); strcpy(str_tmp, value); - return insert_(obj, key, str_tmp, size, JSON_STRING); + return insert_(obj, key, str_tmp, size, JSON_STRING).status; +} + + +int json_insert_obj(json_t *obj, char *key, json_t *input) +{ + // insert + struct result_ ret = insert_(obj, key, input->buf, buf_size_(input), JSON_OBJECT); + if (ret.status != JSON_OK) + { + return ret.status; + } + // move pointers + table_move_ptr_ (table_ptr_(obj)[ret.idx].value_ptr, input->buf, input); + + // change input object and set parent + input->buf = table_ptr_(obj)[ret.idx].value_ptr; + parent_ptr_(input) = obj->buf; + return ret.status; } /******************************************************************************* @@ -167,10 +194,9 @@ int json_insert_str(json_t *obj, char *key, char *value) void *json_get(json_t *obj, char *key, json_type_t type) { int idx = get_idx_(obj, key); - JSON_DEBUG_PRINTF("json_get(): found index %d.\n", idx); if (idx >= 0) { - json_type_t target_type = table_ptr_(obj)[idx].value_type; + json_type_t target_type = table_ptr_(obj)[idx].value_type; return (target_type == type)? table_ptr_(obj)[idx].value_ptr : NULL; } return NULL; @@ -204,6 +230,14 @@ char *json_get_str(json_t *obj, char *key) return (char *)json_get(obj, key, JSON_STRING); } +json_t json_get_obj(json_t *obj, char *key) +{ + json_t ret = { + .buf = json_get(obj, key, JSON_OBJECT) + }; + return ret; +} + /******************************************************************************* * Setter functions ******************************************************************************/ @@ -223,7 +257,7 @@ int json_set(json_t *obj, char *key, void *value) int json_set_str(json_t *obj, char *key, char *value) { // make temporary string. Length is multiples of 8. - int size = (((strlen(value) + 1) >> 3) + 1) << 3; + size_t size = (((strlen(value) + 1) >> 3) + 1) << 3; char str_tmp[size]; memset(str_tmp, 0, size); strcpy(str_tmp, value); @@ -259,27 +293,9 @@ int json_set_float(json_t *obj, char *key, float value) int json_replace_buffer(json_t *obj, void *new_buf, size_t size) { void *old_buf = obj->buf; - // Because in some system the offset is beyond signed int - char is_plus = ((new_buf - old_buf) > 0)? 1: 0; - size_t offset = (is_plus)?(new_buf - old_buf):(old_buf - new_buf); - for (size_t i = 0; i < table_size_(obj); i++) - { - struct entry_ *entry = (table_ptr_(obj) + i); - if (NULL == entry->key) - { - continue; - } - if (is_plus) - { - entry->key += offset; - entry->value_ptr += offset; - } - else - { - entry->key -= offset; - entry->value_ptr -= offset; - } - } + + table_move_ptr_ (new_buf, old_buf, obj); + // clear buffer first then copy memset(new_buf, 0, size); memcpy(new_buf, old_buf, buf_idx_(obj)); @@ -323,30 +339,12 @@ int json_double_table(json_t *obj) json_t json_copy(void *dest_buf, json_t *obj) { - // Because in some system the offset is beyond signed int - char is_plus = ((dest_buf - obj->buf) > 0)? 1: 0; - size_t offset = (is_plus)?(dest_buf - obj->buf):(obj->buf - dest_buf); memcpy(dest_buf, obj->buf, buf_size_(obj)); json_t new_obj = { .buf = dest_buf }; - for (size_t i = 0; i < table_size_(obj); i++) - { - if (NULL == table_ptr_(obj)[i].key) - { - continue; - } - if (is_plus) - { - table_ptr_(&new_obj)[i].key += offset; - table_ptr_(&new_obj)[i].value_ptr += offset; - } - else - { - table_ptr_(&new_obj)[i].key -= offset; - table_ptr_(&new_obj)[i].value_ptr -= offset; - } - } + + table_move_ptr_ (dest_buf, obj->buf, &new_obj); return new_obj; } @@ -374,18 +372,27 @@ size_t json_buffer_size(json_t *obj) * Debug functions ******************************************************************************/ +#ifndef DEBUG + // empty +#else void json_debug_print_obj(json_t *obj) { + if (NULL == obj->buf) + { + JSON_DEBUG_PRINTF("Invalid object!! buffer is NULL!\n"); + return; + } JSON_DEBUG_PRINTF("==========================\n"); JSON_DEBUG_PRINTF("\t emJSON object block\n"); JSON_DEBUG_PRINTF("==========================\n"); + JSON_DEBUG_PRINTF("Parent: %p\n", parent_ptr_(obj)); JSON_DEBUG_PRINTF("Buffer size: 0x%x\n", (unsigned int)buf_size_(obj)); JSON_DEBUG_PRINTF("Current buffer index: 0x%x\n", (unsigned int)buf_idx_(obj)); JSON_DEBUG_PRINTF("Size of table : %lu\n", table_size_(obj)); JSON_DEBUG_PRINTF("Entry count : %lu\n", entry_count_(obj)); // Print pointers JSON_DEBUG_PRINTF("Header Pointer : %p\n", header_ptr_(obj)); - JSON_DEBUG_PRINTF("Size of table in bytes: 0x%x\n", (unsigned int)sizeof(struct header_)); + JSON_DEBUG_PRINTF("Size of header in bytes: 0x%x\n", (unsigned int)sizeof(struct header_)); JSON_DEBUG_PRINTF("Table Pointer : %p\n", table_ptr_(obj)); JSON_DEBUG_PRINTF("Size of each entry in the table: 0x%x\n", (unsigned int)sizeof(struct entry_)); JSON_DEBUG_PRINTF("Size of table in bytes: 0x%x\n", (unsigned int)table_byte_size_(obj)); @@ -408,7 +415,7 @@ void json_debug_print_obj(json_t *obj) JSON_DEBUG_PRINTF("Key length: 0x%x\n", (unsigned int)strlen(entry->key) + 1); JSON_DEBUG_PRINTF("Hash : %u\n", entry->hash); JSON_DEBUG_PRINTF("Value pointer : %p\n", entry->value_ptr); - JSON_DEBUG_PRINTF("Value length: 0x%x\n", entry->value_size); + JSON_DEBUG_PRINTF("Value size in bytes: 0x%x\n", (unsigned int)entry->value_size); // print type switch(entry->value_type) { @@ -422,9 +429,18 @@ void json_debug_print_obj(json_t *obj) break; case JSON_STRING: JSON_DEBUG_PRINTF("Entry type : String\n"); - JSON_DEBUG_PRINTF("Charaters count : 0x%x\n", (unsigned int)strlen((char *)entry->value_ptr)); + JSON_DEBUG_PRINTF("Charters count : 0x%x\n", (unsigned int)strlen((char *)entry->value_ptr)); JSON_DEBUG_PRINTF("Value : %s\n", (char *)entry->value_ptr); break; + case JSON_OBJECT: + JSON_DEBUG_PRINTF("Entry type : Object\n"); + JSON_DEBUG_PRINTF("======= Child Object Printing =======\n"); + { + json_t tmp = {.buf = entry->value_ptr}; + json_debug_print_obj(&tmp); + } + JSON_DEBUG_PRINTF("======= Child Object Printing End =======\n"); + break; default: JSON_DEBUG_PRINTF("UNKOWN TYPE!!!\n"); } @@ -432,7 +448,7 @@ void json_debug_print_obj(json_t *obj) char dump_str[17]; memset(dump_str, 0, 17); uint8_t *dump = (uint8_t *)entry->value_ptr; - for (int j = 0; j < entry->value_size; j++) + for (size_t j = 0; j < entry->value_size; j++) { sprintf(&dump_str[(2*j)%16], "%02x", (uint8_t)dump[j]); if( ((j%8 == 7)) || (j == (entry->value_size -1)) ) @@ -447,6 +463,7 @@ void json_debug_print_obj(json_t *obj) JSON_DEBUG_PRINTF("\t End printing\n"); JSON_DEBUG_PRINTF("==========================\n"); return; +#endif } /******************************************************************************* @@ -477,6 +494,7 @@ static int get_idx_(json_t *obj, char *key) { if (0 == checked[idx]) { + checked[idx] += 1; count++; if (count >= table_size_(obj)) { // When the table is full and visited all entries @@ -501,11 +519,16 @@ static int get_idx_(json_t *obj, char *key) } -static int insert_(json_t *obj, char *key, void *value, size_t size, json_type_t type) +static struct result_ insert_(json_t *obj, char *key, void *value, size_t size, json_type_t type) { + struct result_ ret = { + .status = JSON_ERROR, + .idx = 0 + }; if (entry_count_(obj) >= table_size_(obj)) { - return JSON_TABLE_FULL; + ret.status = JSON_TABLE_FULL; + return ret; } // buffer size check @@ -514,7 +537,8 @@ static int insert_(json_t *obj, char *key, void *value, size_t size, json_type_t size_t buf_required = key_size + value_size; if (buf_idx_(obj) + buf_required > buf_size_(obj)) { - return JSON_BUFFER_FULL; + ret.status = JSON_BUFFER_FULL; + return ret; } // construct entry object first, with hash. @@ -529,7 +553,8 @@ static int insert_(json_t *obj, char *key, void *value, size_t size, json_type_t { // collision, open addressing if (new_entry.hash == table_ptr_(obj)[new_idx].hash) { // collision, and the hash are the same (same key) - return JSON_KEY_EXISTS; + ret.status = JSON_KEY_EXISTS; + return ret; } new_idx = (new_idx << 2) + new_idx + 1 + perturb; perturb >>= PERTURB_SHIFT; @@ -554,5 +579,33 @@ static int insert_(json_t *obj, char *key, void *value, size_t size, json_type_t table_ptr_(obj)[new_idx] = new_entry; entry_count_(obj) += 1; - return JSON_OK; + + ret.status = JSON_OK; + ret.idx = new_idx; + return ret; +} + +static void table_move_ptr_ (void *dest, void *source, json_t *obj) +{ + // Because in some system the offset is beyond signed int + char is_plus = ((dest - source) > 0)? 1: 0; + size_t offset = (is_plus)?(dest - source):(source - dest); + for (size_t i = 0; i < table_size_(obj); i++) + { + struct entry_ *entry = (table_ptr_(obj) + i); + if (NULL == entry->key) + { + continue; + } + if (is_plus) + { + entry->key += offset; + entry->value_ptr += offset; + } + else + { + entry->key -= offset; + entry->value_ptr -= offset; + } + } } diff --git a/emJSON/json.h b/emJSON/json.h index a813829..8c7f458 100644 --- a/emJSON/json.h +++ b/emJSON/json.h @@ -14,14 +14,14 @@ #define JSON_TYPE_MISMATCH -7 typedef uint8_t json_type_t; - #define JSON_INT 1 - #define JSON_FLOAT 2 - #define JSON_STRING 3 - #define JSON_UNKNOWN 4 - //object, - //array, - //boolean, - //null_value + #define JSON_INT 1 + #define JSON_FLOAT 2 + #define JSON_STRING 3 + #define JSON_OBJECT 4 + //#define JSON_ARRAY 5 + //#define JSON_NULL 6 + //#define JSON_BOOL 7 + #define JSON_UNKNOWN 8 typedef struct { @@ -41,16 +41,18 @@ int json_delete(json_t *obj, char *key); int json_clear(json_t *obj); // Insertion functions -int json_insert(json_t *obj, char *key, char *value, json_type_t type); +int json_insert(json_t *obj, char *key, void *value, json_type_t type); int json_insert_str(json_t *obj, char *key, char *value); int json_insert_int(json_t *obj, char *key, int32_t value); int json_insert_float(json_t *obj, char *key, float value); +int json_insert_obj(json_t *obj, char *key, json_t *input); // Getter functions void *json_get(json_t *obj, char *key, json_type_t type); char *json_get_str(json_t *obj, char *key); int json_get_int(json_t *obj, char *key); float json_get_float(json_t *obj, char *key); +json_t json_get_obj(json_t *obj, char *key); // Setter functions int json_set(json_t *obj, char *key, void *value); @@ -73,6 +75,24 @@ size_t json_table_size(json_t *obj); size_t json_count(json_t *obj); size_t json_buffer_size(json_t *obj); + +// Debugging Support +#ifdef DEBUG + #include + void json_debug_print_obj(json_t *obj); + #define JSON_DEBUG_PRINT_OBJ(obj) json_debug_print_obj(obj) + + #if defined(ARDUINO_PLATFORM) || defined(ARDUINO) + #define JSON_DEBUG_PRINTF(msg, ...) printf("EMJSON-DEBUG: " msg, ##__VA_ARGS__) + #else + #define JSON_DEBUG_PRINTF(msg, ...) printf("EMJSON-DEBUG: " msg, ##__VA_ARGS__) + #endif +#else + #define JSON_DEBUG_PRINT_OBJ(obj) + #define JSON_DEBUG_PRINTF(msg, ...) +#endif + + #ifdef __cplusplus } #endif diff --git a/emJSON/json_internal.h b/emJSON/json_internal.h index 2702414..127a511 100644 --- a/emJSON/json_internal.h +++ b/emJSON/json_internal.h @@ -13,12 +13,13 @@ struct entry_ int32_t hash; void *value_ptr; char *key; - uint8_t value_size; + size_t value_size; json_type_t value_type; }; struct header_ { + void *parent; size_t buf_size; size_t buf_idx; size_t table_size; @@ -28,6 +29,7 @@ struct header_ // pointer macros #define header_ptr_(obj) ((struct header_ *)((obj)->buf)) +#define parent_ptr_(obj) (header_ptr_(obj)->parent) #define table_ptr_(obj) ((struct entry_ *)((obj)->buf + sizeof(struct header_))) #define content_ptr_(obj) ((obj)->buf + sizeof(struct header_) + table_byte_size_(obj)) @@ -51,19 +53,6 @@ static inline size_t content_byte_size_(json_t *obj) (sizeof(struct header_) + header_ptr_(obj)->table_size * sizeof(struct entry_)); } -// Debugging settings - -#ifdef DEBUG - #include - #define JSON_DEBUG_PRINTF(msg, ...) printf("EMJSON-DEBUG: " msg, ##__VA_ARGS__) - #define JSON_DEBUG_PRINT_OBJ(obj) json_debug_print_obj(obj) -#else - #define JSON_DEBUG_PRINTF(msg, ...) - #define JSON_DEBUG_PRINT_OBJ(obj) -#endif - -void json_debug_print_obj(json_t *obj); - #endif /* JSON_INTERNAL_H_ */ diff --git a/emJSON/json_string.c b/emJSON/json_string.c index df021b9..2e64002 100644 --- a/emJSON/json_string.c +++ b/emJSON/json_string.c @@ -25,8 +25,11 @@ static inline int is_digit_(char input); */ struct atox_ret_ { - int value_int; - float value_float; + union + { + float f; + int i; + } value; uint8_t str_len; }; @@ -325,7 +328,6 @@ static struct parser_result_ check_number_(char *input) int json_strcpy(char *dest, json_t *obj) { - char str_buf[30]; // FIXME: Take more string length int idx = 0; memset(dest + idx, '{', 1); idx += 1; @@ -348,7 +350,7 @@ int json_strcpy(char *dest, json_t *obj) switch (entry.value_type) { case JSON_INT: - itoa_(*(int *)entry.value_ptr, str_buf, 10); + str_len = itoa_(*(int *)entry.value_ptr, dest + idx, 10); break; case JSON_FLOAT: #ifdef __CC_ARM @@ -357,30 +359,37 @@ int json_strcpy(char *dest, json_t *obj) { float value; memcpy(&value, entry.value_ptr, sizeof(float)); - ftoa_(value, str_buf); + ftoa_(value, dest + idx); break; } #else // __GNUC__ - ftoa_(*(float *)entry.value_ptr, str_buf); + str_len = ftoa_(*(float *)entry.value_ptr, dest + idx); break; #endif case JSON_STRING: memset(dest + idx, '\"', 1); idx += 1; - strcpy(str_buf, (char *)entry.value_ptr); + strcpy(dest + idx, (char *)entry.value_ptr); + str_len = strlen(dest + idx); break; + case JSON_OBJECT: + { + json_t tmp = { + .buf = entry.value_ptr + }; + str_len = json_strcpy(dest + idx, &tmp); + } + break; default: return JSON_ERROR; } - str_len = strlen(str_buf); - strcpy(dest + idx, str_buf); strcpy(dest + idx + str_len, (JSON_STRING == entry.value_type) ? "\"," : ","); idx += str_len + ((JSON_STRING == entry.value_type)? 2 : 1); } // it's the end // -1 is to remove the last ','. strcpy(dest + idx -1, "}"); - return JSON_OK; + return idx; } int json_strlen(json_t *obj) @@ -424,6 +433,14 @@ int json_strlen(json_t *obj) idx += 1; // '\"' str_len = strlen((char *)entry.value_ptr); break; + case JSON_OBJECT: + { + json_t tmp = { + .buf = entry.value_ptr + }; + str_len = json_strlen(&tmp); + } + break; default: return JSON_ERROR; } @@ -451,10 +468,10 @@ static int insert_(json_t *obj, struct parser_result_ *key, struct parser_result input_ptr = value->i; break; case JSON_INT: - input = atoi_(value->i).value_int; + input = atoi_(value->i).value.i; break; case JSON_FLOAT: - input_f = atof_(value->i).value_float; + input_f = atof_(value->i).value.f; input_ptr = &input_f; break; default: @@ -516,7 +533,7 @@ struct atox_ret_ atoi_(const char *str) ret_int = ret_int * 10 + (*str - '0'); } ret = (struct atox_ret_){ - .value_int = ret_int * sign, + .value.i = ret_int * sign, .str_len = len }; return ret; @@ -545,7 +562,7 @@ struct atox_ret_ atof_(const char *str) len += 1; } tmp = atoi_(str); - int_part = tmp.value_int; + int_part = tmp.value.i; str += tmp.str_len; len += tmp.str_len; @@ -555,7 +572,7 @@ struct atox_ret_ atof_(const char *str) { str += 1; tmp = atoi_(str); - frac_part = (float) tmp.value_int; + frac_part = (float) tmp.value.i; frac_len = tmp.str_len; str += tmp.str_len; @@ -568,14 +585,14 @@ struct atox_ret_ atof_(const char *str) { str += 1; tmp = atoi_(str); - expo_part = tmp.value_int; + expo_part = tmp.value.i; str += tmp.str_len; len += tmp.str_len + 1; } // put them together - ret.value_float = (float) int_part; + ret.value.f = (float) int_part; if (frac_len > 0) { // add fraction @@ -585,15 +602,15 @@ struct atox_ret_ atof_(const char *str) frac_divisor *= 10; } frac_part /= frac_divisor; - if (ret.value_float < 0) + if (ret.value.f < 0) { - ret.value_float *= -1; - ret.value_float += frac_part; - ret.value_float *= -1; + ret.value.f *= -1; + ret.value.f += frac_part; + ret.value.f *= -1; } else { - ret.value_float += frac_part; + ret.value.f += frac_part; } } @@ -613,17 +630,17 @@ struct atox_ret_ atof_(const char *str) } if (expo_signed) { - ret.value_float *= expo_multiplier; + ret.value.f *= expo_multiplier; } else { - ret.value_float /= expo_multiplier; + ret.value.f /= expo_multiplier; } } if (!is_plus) { - ret.value_float *= -1; + ret.value.f *= -1; } // done ret.str_len = len; diff --git a/examples/full_example.c b/examples/full_example.c index 1ceb361..13a4e06 100644 --- a/examples/full_example.c +++ b/examples/full_example.c @@ -108,7 +108,34 @@ int main() printf("%d\n", (int)strlen(str)); printf("%s\n", str); free(str); - // Finished + + // Child object test + char str_input2[] = "{\"sensor1\":0.045600,\"message\":\"JSON Is Cool\",\"sensor2\":142}"; + char buffer2[512]; + json_t test2 = json_init(buffer2, 512, 4); // 4 is table size, this MUST be power of 2. + json_parse(&test2, str_input2); + + char str_input3[] = "{\"message\":\"JSON Child Ojbect\",\"sensor3\":0.562}"; + char buffer3[256]; + json_t test3 = json_init(buffer3, 256, 4); // 4 is table size, this MUST be power of 2. + json_parse(&test3, str_input3); + + json_insert_obj(&test2, "Child", &test3); + + json_debug_print_obj(&test2); + fflush(stdout); + json_t test4 = json_get_obj(&test2, "Child"); + + json_debug_print_obj(&test4); + // Serialize + printf("==JSON Serialize Test==\n"); + str = emJSON_string(&test2); + printf("%d\n", (int)json_strlen(&test2)); + printf("%d\n", (int)strlen(str)); + printf("%s\n", str); + free(str); + + // Finished emJSON_free(&test); printf("%s\n", "Done, You're Good!\n"); return 0;