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 <leandro.pereira@intel.com>
diff --git a/include/json.h b/include/json.h
index ebff583..f9cbcaf 100644
--- a/include/json.h
+++ b/include/json.h
@@ -27,6 +27,11 @@
#include <sys/types.h>
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 @@
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 @@
.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 @@
.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 @@
.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 @@
.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 @@
.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 @@
.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 @@
.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 @@
.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 @@
.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 @@
.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 2d9f01b..98820b7 100644
--- a/lib/json/json.c
+++ b/lib/json/json.c
@@ -476,8 +476,6 @@
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 @@
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;