From 0ec79d6853b7fcc77c83ad5128b8170dda044032 Mon Sep 17 00:00:00 2001 From: Leandro Pereira Date: Wed, 18 Apr 2018 18:14:22 -0700 Subject: [PATCH] lib: json: Efficiently pack field name, offset, alignment, type This trades a little bit over 40 bytes (on x86) of text for a lot of savings in rodata. This is accomplished by using bitfields to pack the field name length, offset, alignment, and the type tag into a single 32-bit unsigned integer instead of scattering this information into four different integers. Signed-off-by: Leandro Pereira --- include/json.h | 56 ++++++++++++++++++++++++++++++++----------------- lib/json/json.c | 4 +--- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/include/json.h b/include/json.h index ebff5835c87..f9cbcaf1d39 100644 --- a/include/json.h +++ b/include/json.h @@ -27,6 +27,11 @@ #include enum json_tokens { + /* Before changing this enum, ensure that its maximum + * value is still within 7 bits. See comment next to the + * declaration of `type` in struct json_obj_descr. + */ + JSON_TOK_NONE = '_', JSON_TOK_OBJECT_START = '{', JSON_TOK_OBJECT_END = '}', @@ -45,15 +50,28 @@ enum json_tokens { struct json_obj_descr { const char *field_name; - size_t field_name_len; - size_t offset; - size_t alignment; - /* Valid values here: JSON_TOK_STRING, JSON_TOK_NUMBER, - * JSON_TOK_TRUE, JSON_TOK_FALSE, JSON_TOK_OBJECT_START, - * JSON_TOK_LIST_START. (All others ignored.) + /* Alignment can never be 0 or more than 4. The macros to create a + * struct json_obj_descr will subtract 1 from the result of + * __alignof__() calls in order to keep this value in the 0-3 range + * and thus use only 2 bits. 1 is added back when rounding it up to + * calculate the struct size while parsing an array or object. */ - enum json_tokens type; + u32_t alignment : 2; + + /* 127 characters is more than enough for a field name. */ + u32_t field_name_len : 7; + + /* Valid values here (enum json_tokens): JSON_TOK_STRING, + * JSON_TOK_NUMBER, JSON_TOK_TRUE, JSON_TOK_FALSE, + * JSON_TOK_OBJECT_START, JSON_TOK_LIST_START. (All others + * ignored.) Maximum value is '}' (125), so this has to be 7 bits + * long. + */ + u32_t type : 7; + + /* 65535 bytes is more than enough for many JSON payloads. */ + u32_t offset : 16; union { struct { @@ -110,7 +128,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .field_name = (#field_name_), \ .field_name_len = sizeof(#field_name_) - 1, \ .offset = offsetof(struct_, field_name_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ .type = type_, \ } @@ -144,7 +162,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .field_name = (#field_name_), \ .field_name_len = (sizeof(#field_name_) - 1), \ .offset = offsetof(struct_, field_name_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ .type = JSON_TOK_OBJECT_START, \ .object = { \ .sub_descr = sub_descr_, \ @@ -183,13 +201,13 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .field_name = (#field_name_), \ .field_name_len = sizeof(#field_name_) - 1, \ .offset = offsetof(struct_, field_name_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ .type = JSON_TOK_LIST_START, \ .array = { \ .element_descr = &(struct json_obj_descr) { \ .type = elem_type_, \ .offset = offsetof(struct_, len_field_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ }, \ .n_elements = (max_len_), \ }, \ @@ -240,7 +258,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .field_name = (#field_name_), \ .field_name_len = sizeof(#field_name_) - 1, \ .offset = offsetof(struct_, field_name_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ .type = JSON_TOK_LIST_START, \ .array = { \ .element_descr = &(struct json_obj_descr) { \ @@ -250,7 +268,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .sub_descr_len = elem_descr_len_, \ }, \ .offset = offsetof(struct_, len_field_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ }, \ .n_elements = (max_len_), \ }, \ @@ -279,7 +297,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .field_name = (json_field_name_), \ .field_name_len = sizeof(json_field_name_) - 1, \ .offset = offsetof(struct_, struct_field_name_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ .type = type_, \ } @@ -305,7 +323,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .field_name = (json_field_name_), \ .field_name_len = (sizeof(json_field_name_) - 1), \ .offset = offsetof(struct_, struct_field_name_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ .type = JSON_TOK_OBJECT_START, \ .object = { \ .sub_descr = sub_descr_, \ @@ -341,13 +359,13 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .field_name = (json_field_name_), \ .field_name_len = sizeof(json_field_name_) - 1, \ .offset = offsetof(struct_, struct_field_name_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ .type = JSON_TOK_LIST_START, \ .array = { \ .element_descr = &(struct json_obj_descr) { \ .type = elem_type_, \ .offset = offsetof(struct_, len_field_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ }, \ .n_elements = (max_len_), \ }, \ @@ -407,7 +425,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .field_name = json_field_name_, \ .field_name_len = sizeof(json_field_name_) - 1, \ .offset = offsetof(struct_, struct_field_name_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ .type = JSON_TOK_LIST_START, \ .element_descr = &(struct json_obj_descr) { \ .type = JSON_TOK_OBJECT_START, \ @@ -416,7 +434,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, .sub_descr_len = elem_descr_len_, \ }, \ .offset = offsetof(struct_, len_field_), \ - .alignment = __alignof__(struct_), \ + .alignment = __alignof__(struct_) - 1, \ }, \ .n_elements = (max_len_), \ } diff --git a/lib/json/json.c b/lib/json/json.c index 2d9f01be002..98820b7d28d 100644 --- a/lib/json/json.c +++ b/lib/json/json.c @@ -476,8 +476,6 @@ static int decode_value(struct json_obj *obj, static ptrdiff_t get_elem_size(const struct json_obj_descr *descr) { - assert(descr->alignment); - switch (descr->type) { case JSON_TOK_NUMBER: return sizeof(s32_t); @@ -495,7 +493,7 @@ static ptrdiff_t get_elem_size(const struct json_obj_descr *descr) for (i = 0; i < descr->object.sub_descr_len; i++) { ptrdiff_t s = get_elem_size(&descr->object.sub_descr[i]); - total += ROUND_UP(s, descr->alignment); + total += ROUND_UP(s, descr->alignment + 1); } return total;