The Layout Utility

--MUTS provides a utility that allows you to take a collection of bit fields (specifying size and alignment in bits), and lay them out efficiently in some buffer. This is useful for building message headers, but also for dynamically adding fields to structures, fields that are not known in advance. In particular, this is useful for event structures.

SOURCES

include/muts/layout.h
src/muts/layout.c

INCLUDE

#include "muts.h"

INTERFACE

void bit_copy(
	uint8_t *src, int src_offset,
	uint8_t *dst, int dst_offset,
	int nbits
);
Do a bit copy operation from the given source to the given destination. We assume here that the source and destination buffers do not overlap. We loop through the destination bytes, basically one by one, and treat the first and last byte in a special way. For the middle part, we keep dst_offset at zero.
typedef struct lo_field {
	char *name;			/* ASCII name of field */
	unsigned int size;		/* in bits */
	unsigned int alignment;		/* in bits */
	char *format;			/* format for printf() */
	unsigned int offset;		/* in bits */
	unsigned int byte_offset;	/* in bytes */
	unsigned int bit_offset;	/* from byte_offset */
	...
} lo_field_t, *lo_field_id;
This is the field structure that is used and exported by this module. The user assigns to a field a name, a size, and an alignment, each in bits. This module will, given a set of fields, fit them snugly into a byte array. As a result, it fits in the offset field. byte_offset is offset / 8, and bit_offset is offset % 8, and are used for efficiency.
uint32_t lo_get_val(
	lo_descr_id lod,
	void *buf,
	lo_field_id lof
);
Get the value of a field of at most 32 bits.
error_t lo_create(
	struct mem_chan *mc,
	lo_descr_id *lodp
);
Create an layout descriptor.
void lo_add_field(
	lo_descr_id lod,
	lo_field_id lof
);
Add a field to an layout descriptor. The field already contains the name, size, and alignment of the field. At this point no offsets are assigned to the field. This is done by lo_layout().
void lo_new_field(
	lo_descr_id lod,
	lo_field_id lof,
	char *name,
	unsigned int size,
	unsigned int alignment,
	char *format
);
Initialize and add a field.
error_t lo_layout(
	lo_descr_id lod,
	int offset,
	int alignment
);
Lay out the different fields in the layout descriptor. The strategy we tried here is to sort all the fields according to size, and fit the largest ones first, and the smaller ones in the holes that are created. The reason we were doing this is intuitive, but we ran a test sorting it in different ways (and not sorting it), and this gave the best result. However, since the improvement over not sorting was only 2%, and the sorting the wrong way was only 13% worse than the best way, we are not bothering any more with the sorting. This makes the package more faster and more portable.

The offset is added to every offset calculated (allowing for a buffer to start off not at the beginning of a structure), and the size of the whole layout is rounded up to the given alignment. Both are given in bits.

Note that this routine only needs to be called by users who don't want to do their own lay out of the fields. The rest of this package works perfectly well without calling lo_layout().

error_t lo_get_field(
	lo_descr_id lod,
	char *name,
	lo_field_id result
);
Get a field descriptor. This routine is not particularly efficient, and should be called sparingly. Also, the returned field descriptor is a copy of the original, rather than a pointer to the original.
error_t lo_print(
	lo_descr_id lod,
	void *buf,
	unsigned int options,
	char *indent
);
Pretty print a buffer. The options can include LO_SIZE, LO_ALIGNMENT, and LO_OFFSET to specify what details should be printed. If you're interested in all options, you can use LO_ALL instead of or-ing everything together.
int lo_size(lo_descr_id lod)
Return the size of the layout descriptor.
error_t lo_free(lo_descr_id lod);
Release a layout descriptor.
error_t lo_init(void);
Initialize this module.
TYPE *LO_ADDR(
	lo_descr_id lod,
	void *buf,
	TYPE type,
	lo_field_id lof
);
This macro returns a pointer of the given type containing the address of the byte aligned field specified by lof in the given buf.
TYPE LO_VAR(
	lo_descr_id lod,
	void *buf,
	TYPE type,
	lo_field_id lof
);
This macro represents the variable the byte aligned field of the given type specified by lof in the given buf.
int LO_GET_BIT(
	lo_descr_id lod,
	void *buf,
	lo_field_id lof
);
This macro returns the value of the bit field lof in buf.
void LO_SET_BIT(
	lo_descr_id lod,
	void *buf,
	lo_field_id lof
);
This macro sets the bit field lof in buf to 1.
void LO_CLR_BIT(
	lo_descr_id lod,
	void *buf,
	lo_field_id lof
);
This macro clears the bit field lof in buf.
void LO_BIT_SET_VALUE(
	lo_descr_id lod,
	void *buf,
	lo_field_id lof,
	int value
);
This macro sets the bit field lof in buf to 1 if value is non-zero, and to 0 if value is zero.
void LO_GET_FIELD(
	lo_descr_id lod,
	lo_field_id lof,
	void *in,
	void *out
);
Copy the given field in the given in buffer to the given byte-aligned out buffer.
void LO_SET_FIELD(
	lo_descr_id lod,
	lo_field_id lof,
	void *out,
	void *in
);
Copy the given byte-aligned in buffer into the field in the given out buffer.
void LO_COPY_FIELD(
	lo_descr_id lod1,
	void *buf1,
	lo_field_id lof1,
	lo_descr_id lod2,
	void *buf2,
	lo_field_id lof2
);
Copy field lof1 in buf1 to field lof2 in buf2.
uint32_t LO_GET_VAL(
	lo_descr_id lod,
	void *buf,
	lo_field_id lof
);
Get the value of a field of at most 32 bits.
void LO_SET_VAL(
	lo_descr_id lod,
	void *buf,
	lo_field_id lof,
	uint32_t val
);
Set the value of a field of at most 32 bits.

EXAMPLE

lo_field_t field1 = { "a", 0, 0 };
lo_field_t field2 = { "b", 0, 0 };
lo_field_t field3 = { "c", 0, 0 };

int main(void){
	lo_descr_id lod;
	static uint8_t buf[8];
	int i, val1, val2, val3, width;

	for (width = 1; width < 22; width++) {
		lo_create(0, &lod);

		field1.size = width;
		field1.alignment = width;
		lo_add_field(lod, &field1);
		field2.size = width;
		field2.alignment = width;
		lo_add_field(lod, &field2);
		field3.size = width;
		field3.alignment = width;
		lo_add_field(lod, &field3);

		lo_layout(lod, 0, 32);

		for (i = 0; i < 100; i++) {
			val1 = random() % (1 << width);
			val2 = random() % (1 << width);
			val3 = random() % (1 << width);
			LO_SET_VAL(lod, buf, &field1, val1);
			LO_SET_VAL(lod, buf, &field2, val2);
			LO_SET_VAL(lod, buf, &field3, val3);
			if (LO_GET_VAL(lod, buf, &field1) != val1)
				sys_panic("val1");
			if (LO_GET_VAL(lod, buf, &field2) != val2)
				sys_panic("val2");
			if (LO_GET_VAL(lod, buf, &field3) != val3)
				sys_panic("val3");
		}
	}

	return 0;
}

FUTURE WORK

At this point we're not convinced yet what to do when two fields of the same name are added. Four possibilities: 1) return an error. 2) overwrite. 3) add both separately. 4) ignore if they are the same. For now I just add the field to the list.