common/msg: added mrp_msg_get for fetching multiple message fields.
authorKrisztian Litkey <kli@iki.fi>
Wed, 12 Sep 2012 19:54:32 +0000 (22:54 +0300)
committerKrisztian Litkey <krisztian.litkey@intel.com>
Fri, 26 Oct 2012 16:03:50 +0000 (19:03 +0300)
You can use mrp_msg_get to fetch multiple fields from a message with
a single function call. Moreover, the implementation will do its best
to avoid scanning the message more than once. IOW, mrp_msg_get can be
used to fetch multiple fields from a message efficiently if the fields
to fetch are specified in the same order (with potential fields missing
in between) as they are present in the actual message.

src/common/msg.c
src/common/msg.h
src/common/tests/msg-test.c

index 2cf2785..2a4eadc 100644 (file)
@@ -382,6 +382,90 @@ mrp_msg_field_t *mrp_msg_find(mrp_msg_t *msg, uint16_t tag)
 }
 
 
+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
index bf23d22..d4fe420 100644 (file)
@@ -203,6 +203,9 @@ int mrp_msg_prepend(mrp_msg_t *msg, uint16_t tag, ...);
 /** Find a field in a message. */
 mrp_msg_field_t *mrp_msg_find(mrp_msg_t *msg, uint16_t tag);
 
+/** Get the given fields (with matching tags and types) from the message. */
+int mrp_msg_get(mrp_msg_t *msg, ...) MRP_NULLTERM;
+
 /** Dump a message. */
 int mrp_msg_dump(mrp_msg_t *msg, FILE *fp);
 
index ff0c164..44c5cd2 100644 (file)
@@ -470,11 +470,132 @@ void test_custom_encode_decode(void)
 }
 
 
+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();