}
+int mrp_msg_get(mrp_msg_t *msg, ...)
+{
+#define HANDLE_TYPE(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ valp = va_arg(ap, typeof(valp)); \
+ valp->_member = f->_member; \
+ break
+
+ mrp_msg_field_t *f;
+ mrp_msg_value_t *valp;
+ mrp_list_hook_t *start, *p;
+ uint16_t tag, type;
+ int found;
+ va_list ap;
+
+ va_start(ap, msg);
+
+ /*
+ * Okay... this might look a bit weird at first sight. This is
+ * mostly because we don't use the standard list iterating macros
+ * in the inner loop. There is a good reason for that: we want to
+ * minimise the number of times we scan the message which is just
+ * a linked list of fields. We do this by arranging the nested
+ * loops below in such a way that if the order of fields to fetch
+ * in the argument list matches the order of fields in the message
+ * we end up running the outer and inner loops in a 'phase lock'.
+ * So if the caller fetches the fields in the correct order we end
+ * up scanning the message at most once but only up to the last
+ * field to fetch.
+ */
+
+ start = msg->fields.next;
+
+ while ((tag = va_arg(ap, unsigned int)) != MRP_MSG_FIELD_INVALID) {
+ type = va_arg(ap, unsigned int);
+ found = FALSE;
+
+ for (p = start; p != start->prev; p = p->next) {
+ if (p == &msg->fields)
+ continue;
+
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (f->tag != tag)
+ continue;
+
+ if (f->type != type)
+ goto out;
+
+ switch (type) {
+ HANDLE_TYPE(STRING, str);
+ HANDLE_TYPE(BOOL , bln);
+ HANDLE_TYPE(UINT8 , u8 );
+ HANDLE_TYPE(SINT8 , s8 );
+ HANDLE_TYPE(UINT16, u16);
+ HANDLE_TYPE(SINT16, s16);
+ HANDLE_TYPE(UINT32, u32);
+ HANDLE_TYPE(SINT32, s32);
+ HANDLE_TYPE(UINT64, u64);
+ HANDLE_TYPE(SINT64, s64);
+ HANDLE_TYPE(DOUBLE, dbl);
+ /* XXX TODO: add handling for array types */
+ default:
+ goto out;
+ }
+
+ start = p->next;
+ found = TRUE;
+ break;
+ }
+
+ if (!found)
+ break;
+ }
+
+ out:
+ va_end(ap);
+
+ return found;
+
+#undef HANDLE_TYPE
+}
+
+
static const char *field_type_name(uint16_t type)
{
#define BASIC(t, n) [MRP_MSG_FIELD_##t] = n
}
+static void test_basic(void)
+{
+ mrp_msg_t *msg;
+ char *str1, *str2;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ double dbl1, dbl2;
+ int i;
+
+ struct field_t {
+ uint16_t tag;
+ uint16_t type;
+ void *ptr;
+ } f[] = {
+ { 0x1, MRP_MSG_FIELD_STRING, &str1 },
+ { 0x2, MRP_MSG_FIELD_STRING, &str2 },
+ { 0x3, MRP_MSG_FIELD_UINT16, &u16 },
+ { 0x4, MRP_MSG_FIELD_SINT16, &s16 },
+ { 0x5, MRP_MSG_FIELD_UINT32, &u32 },
+ { 0x6, MRP_MSG_FIELD_SINT32, &s32 },
+ { 0x7, MRP_MSG_FIELD_DOUBLE, &dbl1 },
+ { 0x8, MRP_MSG_FIELD_DOUBLE, &dbl2 }
+ };
+
+ msg = mrp_msg_create(MRP_MSG_TAG_STRING(0x1, "string 0x1"),
+ MRP_MSG_TAG_STRING(0x2, "string 0x2"),
+ MRP_MSG_TAG_UINT16(0x3, 3),
+ MRP_MSG_TAG_SINT16(0x4, -4),
+ MRP_MSG_TAG_UINT32(0x5, 5),
+ MRP_MSG_TAG_SINT32(0x6, -6),
+ MRP_MSG_TAG_DOUBLE(0x7, 3.14),
+ MRP_MSG_TAG_DOUBLE(0x8, -9.81),
+ MRP_MSG_END);
+
+ if (msg == NULL) {
+ mrp_log_error("Failed to create message.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Message created OK.");
+
+
+ if (!mrp_msg_get(msg,
+ 0x1, MRP_MSG_FIELD_STRING, &str1,
+ 0x2, MRP_MSG_FIELD_STRING, &str2,
+ 0x3, MRP_MSG_FIELD_UINT16, &u16,
+ 0x4, MRP_MSG_FIELD_SINT16, &s16,
+ 0x5, MRP_MSG_FIELD_UINT32, &u32,
+ 0x6, MRP_MSG_FIELD_SINT32, &s32,
+ 0x7, MRP_MSG_FIELD_DOUBLE, &dbl1,
+ 0x8, MRP_MSG_FIELD_DOUBLE, &dbl2,
+ MRP_MSG_END)) {
+ mrp_log_error("Failed to get message fields.");
+ exit(1);
+ }
+ else {
+ mrp_log_info("Got message fields:");
+ mrp_log_info(" str1='%s', str2='%s'", str1, str2);
+ mrp_log_info(" u16=%u, s16=%d", u16, s16);
+ mrp_log_info(" u32=%u, s32=%d", u32, s32);
+ mrp_log_info(" dbl1=%f, dbl2=%f", dbl1, dbl2);
+ }
+
+ if (!mrp_msg_get(msg,
+ 0x8, MRP_MSG_FIELD_DOUBLE, &dbl2,
+ 0x7, MRP_MSG_FIELD_DOUBLE, &dbl1,
+ 0x6, MRP_MSG_FIELD_SINT32, &s32,
+ 0x5, MRP_MSG_FIELD_UINT32, &u32,
+ 0x4, MRP_MSG_FIELD_SINT16, &s16,
+ 0x3, MRP_MSG_FIELD_UINT16, &u16,
+ 0x2, MRP_MSG_FIELD_STRING, &str2,
+ 0x1, MRP_MSG_FIELD_STRING, &str1,
+ MRP_MSG_END)) {
+ mrp_log_error("Failed to get message fields.");
+ exit(1);
+ }
+ else {
+ mrp_log_info("Got message fields:");
+ mrp_log_info(" str1='%s', str2='%s'", str1, str2);
+ mrp_log_info(" u16=%u, s16=%d", u16, s16);
+ mrp_log_info(" u32=%u, s32=%d", u32, s32);
+ mrp_log_info(" dbl1=%f, dbl2=%f", dbl1, dbl2);
+ }
+
+
+#define TAG(idx) f[(idx) & 0x7].tag
+#define TYPE(idx) f[(idx) & 0x7].type
+#define PTR(idx) f[(idx) & 0x7].ptr
+#define FIELD(idx) TAG((idx)), TYPE((idx)), PTR((idx))
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(f); i++) {
+ if (!mrp_msg_get(msg,
+ FIELD(i+0), FIELD(i+1), FIELD(i+2), FIELD(i+3),
+ FIELD(i+4), FIELD(i+5), FIELD(i+6), FIELD(i+7),
+ MRP_MSG_END)) {
+ mrp_log_error("Failed to get message fields for offset %d.", i);
+ exit(1);
+ }
+ else {
+ mrp_log_info("Got message fields for offset %d:", i);
+ mrp_log_info(" str1='%s', str2='%s'", str1, str2);
+ mrp_log_info(" u16=%u, s16=%d", u16, s16);
+ mrp_log_info(" u32=%u, s32=%d", u32, s32);
+ mrp_log_info(" dbl1=%f, dbl2=%f", dbl1, dbl2);
+ }
+ }
+
+ if (mrp_msg_get(msg,
+ 0x9, MRP_MSG_FIELD_STRING, &str1, MRP_MSG_END)) {
+ mrp_log_error("Hmm... non-existent field found.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Ok, non-existent field not found...");
+}
+
+
int main(int argc, char *argv[])
{
mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_DEBUG));
mrp_log_set_target(MRP_LOG_TO_STDOUT);
+ test_basic();
+
test_default_encode_decode(argc, argv);
test_custom_encode_decode();