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>
This commit is contained in:
Leandro Pereira 2018-04-18 18:14:22 -07:00 committed by Anas Nashif
parent 1799cfdb2f
commit 0ec79d6853
2 changed files with 38 additions and 22 deletions

View file

@ -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 @@ 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_), \
}

View file

@ -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;