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;