summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--librabbitmq/amqp.h131
-rw-r--r--librabbitmq/amqp_private.h6
-rw-r--r--librabbitmq/amqp_table.c326
-rw-r--r--tests/test_tables.c247
4 files changed, 615 insertions, 95 deletions
diff --git a/librabbitmq/amqp.h b/librabbitmq/amqp.h
index b25d84c..643fc39 100644
--- a/librabbitmq/amqp.h
+++ b/librabbitmq/amqp.h
@@ -31,24 +31,135 @@ typedef struct amqp_table_t_ {
#define AMQP_EMPTY_TABLE ((amqp_table_t) { .num_entries = 0, .entries = NULL })
-typedef struct amqp_table_entry_t_ {
- amqp_bytes_t key;
+typedef struct amqp_array_t_ {
+ int num_entries;
+ struct amqp_field_value_t_ *entries;
+} amqp_array_t;
+
+#define AMQP_EMPTY_ARRAY ((amqp_array_t) { .num_entries = 0, .entries = NULL })
+
+/*
+ 0-9 0-9-1 Qpid/Rabbit Type Remarks
+---------------------------------------------------------------------------
+ t t Boolean
+ b b Signed 8-bit
+ B Unsigned 8-bit
+ U s Signed 16-bit (A1)
+ u Unsigned 16-bit
+ I I I Signed 32-bit
+ i Unsigned 32-bit
+ L l Signed 64-bit (B)
+ l Unsigned 64-bit
+ f f 32-bit float
+ d d 64-bit float
+ D D D Decimal
+ s Short string (A2)
+ S S S Long string
+ A Nested Array
+ T T T Timestamp (u64)
+ F F F Nested Table
+ V V V Void
+ x Byte array
+
+Remarks:
+
+ A1, A2: Notice how the types **CONFLICT** here. In Qpid and Rabbit,
+ 's' means a signed 16-bit integer; in 0-9-1, it means a
+ short string.
+
+ B: Notice how the signednesses **CONFLICT** here. In Qpid and Rabbit,
+ 'l' means a signed 64-bit integer; in 0-9-1, it means an unsigned
+ 64-bit integer.
+
+I'm going with the Qpid/Rabbit types, where there's a conflict, and
+the 0-9-1 types otherwise. 0-8 is a subset of 0-9, which is a subset
+of the other two, so this will work for both 0-8 and 0-9-1 branches of
+the code.
+*/
+
+typedef struct amqp_field_value_t_ {
char kind;
union {
- amqp_bytes_t bytes;
+ amqp_boolean_t boolean;
+ int8_t i8;
+ uint8_t u8;
+ int16_t i16;
+ uint16_t u16;
int32_t i32;
- amqp_decimal_t decimal;
+ uint32_t u32;
+ int64_t i64;
uint64_t u64;
+ float f32;
+ double f64;
+ amqp_decimal_t decimal;
+ amqp_bytes_t bytes;
amqp_table_t table;
+ amqp_array_t array;
} value;
+} amqp_field_value_t;
+
+typedef struct amqp_table_entry_t_ {
+ amqp_bytes_t key;
+ amqp_field_value_t value;
} amqp_table_entry_t;
-#define _AMQP_TE_INIT(ke,ki,v) { .key = (ke), .kind = (ki), .value = { v } }
-#define AMQP_TABLE_ENTRY_S(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'S', .bytes = (v))
-#define AMQP_TABLE_ENTRY_I(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'I', .i32 = (v))
-#define AMQP_TABLE_ENTRY_D(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'D', .decimal = (v))
-#define AMQP_TABLE_ENTRY_T(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'T', .u64 = (v))
-#define AMQP_TABLE_ENTRY_F(k,v) _AMQP_TE_INIT(amqp_cstring_bytes(k), 'F', .table = (v))
+typedef enum {
+ AMQP_FIELD_KIND_BOOLEAN = 't',
+ AMQP_FIELD_KIND_I8 = 'b',
+ AMQP_FIELD_KIND_U8 = 'B',
+ AMQP_FIELD_KIND_I16 = 's',
+ AMQP_FIELD_KIND_U16 = 'u',
+ AMQP_FIELD_KIND_I32 = 'I',
+ AMQP_FIELD_KIND_U32 = 'i',
+ AMQP_FIELD_KIND_I64 = 'l',
+ AMQP_FIELD_KIND_F32 = 'f',
+ AMQP_FIELD_KIND_F64 = 'd',
+ AMQP_FIELD_KIND_DECIMAL = 'D',
+ AMQP_FIELD_KIND_UTF8 = 'S',
+ AMQP_FIELD_KIND_ARRAY = 'A',
+ AMQP_FIELD_KIND_TIMESTAMP = 'T',
+ AMQP_FIELD_KIND_TABLE = 'F',
+ AMQP_FIELD_KIND_VOID = 'V',
+ AMQP_FIELD_KIND_BYTES = 'x',
+} amqp_field_value_kind_t;
+
+#define _AMQP_TEINIT(ke,ki,v) {.key = (ke), .value = {.kind = AMQP_FIELD_KIND_##ki, .value = {v}}}
+#define AMQP_TABLE_ENTRY_BOOLEAN(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), BOOLEAN, .boolean = (v))
+#define AMQP_TABLE_ENTRY_I8(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), I8, .i8 = (v))
+#define AMQP_TABLE_ENTRY_U8(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), U8, .u8 = (v))
+#define AMQP_TABLE_ENTRY_I16(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), I16, .i16 = (v))
+#define AMQP_TABLE_ENTRY_U16(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), U16, .u16 = (v))
+#define AMQP_TABLE_ENTRY_I32(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), I32, .i32 = (v))
+#define AMQP_TABLE_ENTRY_U32(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), U32, .u32 = (v))
+#define AMQP_TABLE_ENTRY_I64(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), I64, .i64 = (v))
+#define AMQP_TABLE_ENTRY_F32(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), F32, .f32 = (v))
+#define AMQP_TABLE_ENTRY_F64(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), F64, .f64 = (v))
+#define AMQP_TABLE_ENTRY_DECIMAL(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), DECIMAL, .decimal = (v))
+#define AMQP_TABLE_ENTRY_UTF8(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), UTF8, .bytes = (v))
+#define AMQP_TABLE_ENTRY_ARRAY(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), ARRAY, .array = (v))
+#define AMQP_TABLE_ENTRY_TIMESTAMP(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), TIMESTAMP, .u64 = (v))
+#define AMQP_TABLE_ENTRY_TABLE(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), TABLE, .table = (v))
+#define AMQP_TABLE_ENTRY_VOID(k) _AMQP_TEINIT(amqp_cstring_bytes(k), VOID, .u8 = 0)
+#define AMQP_TABLE_ENTRY_BYTES(k,v) _AMQP_TEINIT(amqp_cstring_bytes(k), BYTES, .bytes = (v))
+
+#define _AMQP_FVINIT(ki,v) {.kind = AMQP_FIELD_KIND_##ki, .value = {v}}
+#define AMQP_FIELD_VALUE_BOOLEAN(v) _AMQP_FVINIT(BOOLEAN, .boolean = (v))
+#define AMQP_FIELD_VALUE_I8(v) _AMQP_FVINIT(I8, .i8 = (v))
+#define AMQP_FIELD_VALUE_U8(v) _AMQP_FVINIT(U8, .u8 = (v))
+#define AMQP_FIELD_VALUE_I16(v) _AMQP_FVINIT(I16, .i16 = (v))
+#define AMQP_FIELD_VALUE_U16(v) _AMQP_FVINIT(U16, .u16 = (v))
+#define AMQP_FIELD_VALUE_I32(v) _AMQP_FVINIT(I32, .i32 = (v))
+#define AMQP_FIELD_VALUE_U32(v) _AMQP_FVINIT(U32, .u32 = (v))
+#define AMQP_FIELD_VALUE_I64(v) _AMQP_FVINIT(I64, .i64 = (v))
+#define AMQP_FIELD_VALUE_F32(v) _AMQP_FVINIT(F32, .f32 = (v))
+#define AMQP_FIELD_VALUE_F64(v) _AMQP_FVINIT(F64, .f64 = (v))
+#define AMQP_FIELD_VALUE_DECIMAL(v) _AMQP_FVINIT(DECIMAL, .decimal = (v))
+#define AMQP_FIELD_VALUE_UTF8(v) _AMQP_FVINIT(UTF8, .bytes = (v))
+#define AMQP_FIELD_VALUE_ARRAY(v) _AMQP_FVINIT(ARRAY, .array = (v))
+#define AMQP_FIELD_VALUE_TIMESTAMP(v) _AMQP_FVINIT(TIMESTAMP, .u64 = (v))
+#define AMQP_FIELD_VALUE_TABLE(v) _AMQP_FVINIT(TABLE, .table = (v))
+#define AMQP_FIELD_VALUE_VOID(k) _AMQP_FVINIT(VOID, .u8 = 0)
+#define AMQP_FIELD_VALUE_BYTES(v) _AMQP_FVINIT(BYTES, .bytes = (v))
typedef struct amqp_pool_blocklist_t_ {
int num_blocks;
diff --git a/librabbitmq/amqp_private.h b/librabbitmq/amqp_private.h
index 98a7a65..03a46fe 100644
--- a/librabbitmq/amqp_private.h
+++ b/librabbitmq/amqp_private.h
@@ -115,13 +115,15 @@ extern int amqp_encode_table(amqp_bytes_t encoded,
} \
})
-#define AMQP_CHECK_RESULT(expr) \
+#define AMQP_CHECK_RESULT_CLEANUP(expr, stmts) \
({ \
int _result = (expr); \
- if (_result < 0) return _result; \
+ if (_result < 0) { stmts; return _result; } \
_result; \
})
+#define AMQP_CHECK_RESULT(expr) AMQP_CHECK_RESULT_CLEANUP(expr, )
+
#define AMQP_CHECK_EOF_RESULT(expr) \
({ \
int _result = (expr); \
diff --git a/librabbitmq/amqp_table.c b/librabbitmq/amqp_table.c
index 16d85ff..5ffadc4 100644
--- a/librabbitmq/amqp_table.c
+++ b/librabbitmq/amqp_table.c
@@ -9,8 +9,67 @@
#include <assert.h>
+#define INITIAL_ARRAY_SIZE 16
#define INITIAL_TABLE_SIZE 16
+static int amqp_decode_field_value(amqp_bytes_t encoded,
+ amqp_pool_t *pool,
+ amqp_field_value_t *entry,
+ int *offsetptr); /* forward */
+
+static int amqp_encode_field_value(amqp_bytes_t encoded,
+ amqp_field_value_t *entry,
+ int *offsetptr); /* forward */
+
+/*---------------------------------------------------------------------------*/
+
+static int amqp_decode_array(amqp_bytes_t encoded,
+ amqp_pool_t *pool,
+ amqp_array_t *output,
+ int *offsetptr)
+{
+ int offset = *offsetptr;
+ uint32_t arraysize = D_32(encoded, offset);
+ int num_entries = 0;
+ amqp_field_value_t *entries = malloc(INITIAL_ARRAY_SIZE * sizeof(amqp_field_value_t));
+ int allocated_entries = INITIAL_ARRAY_SIZE;
+ int limit;
+
+ if (entries == NULL) {
+ return -ENOMEM;
+ }
+
+ offset += 4;
+ limit = offset + arraysize;
+
+ while (offset < limit) {
+ if (num_entries >= allocated_entries) {
+ void *newentries;
+ allocated_entries = allocated_entries * 2;
+ newentries = realloc(entries, allocated_entries * sizeof(amqp_field_value_t));
+ if (newentries == NULL) {
+ free(entries);
+ return -ENOMEM;
+ }
+ entries = newentries;
+ }
+
+ AMQP_CHECK_RESULT_CLEANUP(amqp_decode_field_value(encoded,
+ pool,
+ &entries[num_entries],
+ &offset),
+ free(entries));
+ num_entries++;
+ }
+
+ output->num_entries = num_entries;
+ output->entries = amqp_pool_alloc(pool, num_entries * sizeof(amqp_field_value_t));
+ memcpy(output->entries, entries, num_entries * sizeof(amqp_field_value_t));
+
+ *offsetptr = offset;
+ return 0;
+}
+
int amqp_decode_table(amqp_bytes_t encoded,
amqp_pool_t *pool,
amqp_table_t *output,
@@ -53,37 +112,11 @@ int amqp_decode_table(amqp_bytes_t encoded,
entry->key.bytes = D_BYTES(encoded, offset, keylen);
offset += keylen;
- entry->kind = D_8(encoded, offset);
- offset++;
-
- switch (entry->kind) {
- case 'S':
- entry->value.bytes.len = D_32(encoded, offset);
- offset += 4;
- entry->value.bytes.bytes = D_BYTES(encoded, offset, entry->value.bytes.len);
- offset += entry->value.bytes.len;
- break;
- case 'I':
- entry->value.i32 = (int32_t) D_32(encoded, offset);
- offset += 4;
- break;
- case 'D':
- entry->value.decimal.decimals = D_8(encoded, offset);
- offset++;
- entry->value.decimal.value = D_32(encoded, offset);
- offset += 4;
- break;
- case 'T':
- entry->value.u64 = D_64(encoded, offset);
- offset += 8;
- break;
- case 'F':
- AMQP_CHECK_RESULT(amqp_decode_table(encoded, pool, &(entry->value.table), &offset));
- break;
- default:
- return -EINVAL;
- }
-
+ AMQP_CHECK_RESULT_CLEANUP(amqp_decode_field_value(encoded,
+ pool,
+ &entry->value,
+ &offset),
+ free(entries));
num_entries++;
}
@@ -95,6 +128,116 @@ int amqp_decode_table(amqp_bytes_t encoded,
return 0;
}
+static int amqp_decode_field_value(amqp_bytes_t encoded,
+ amqp_pool_t *pool,
+ amqp_field_value_t *entry,
+ int *offsetptr)
+{
+ int offset = *offsetptr;
+
+ entry->kind = D_8(encoded, offset);
+ offset++;
+
+ switch (entry->kind) {
+ case AMQP_FIELD_KIND_BOOLEAN:
+ entry->value.boolean = D_8(encoded, offset) ? 1 : 0;
+ offset++;
+ break;
+ case AMQP_FIELD_KIND_I8:
+ entry->value.i8 = (int8_t) D_8(encoded, offset);
+ offset++;
+ break;
+ case AMQP_FIELD_KIND_U8:
+ entry->value.u8 = D_8(encoded, offset);
+ offset++;
+ break;
+ case AMQP_FIELD_KIND_I16:
+ entry->value.i16 = (int16_t) D_16(encoded, offset);
+ offset += 2;
+ break;
+ case AMQP_FIELD_KIND_U16:
+ entry->value.u16 = D_16(encoded, offset);
+ offset += 2;
+ break;
+ case AMQP_FIELD_KIND_I32:
+ entry->value.i32 = (int32_t) D_32(encoded, offset);
+ offset += 4;
+ break;
+ case AMQP_FIELD_KIND_U32:
+ entry->value.u32 = D_32(encoded, offset);
+ offset += 4;
+ break;
+ case AMQP_FIELD_KIND_I64:
+ entry->value.i64 = (int64_t) D_64(encoded, offset);
+ offset += 8;
+ break;
+ case AMQP_FIELD_KIND_F32:
+ entry->value.u32 = D_32(encoded, offset);
+ /* and by punning, f32 magically gets the right value...! */
+ offset += 4;
+ break;
+ case AMQP_FIELD_KIND_F64:
+ entry->value.u64 = D_64(encoded, offset);
+ /* and by punning, f64 magically gets the right value...! */
+ offset += 8;
+ break;
+ case AMQP_FIELD_KIND_DECIMAL:
+ entry->value.decimal.decimals = D_8(encoded, offset);
+ offset++;
+ entry->value.decimal.value = D_32(encoded, offset);
+ offset += 4;
+ break;
+ case AMQP_FIELD_KIND_UTF8:
+ /* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
+ same implementation, but different interpretations. */
+ /* fall through */
+ case AMQP_FIELD_KIND_BYTES:
+ entry->value.bytes.len = D_32(encoded, offset);
+ offset += 4;
+ entry->value.bytes.bytes = D_BYTES(encoded, offset, entry->value.bytes.len);
+ offset += entry->value.bytes.len;
+ break;
+ case AMQP_FIELD_KIND_ARRAY:
+ AMQP_CHECK_RESULT(amqp_decode_array(encoded, pool, &(entry->value.array), &offset));
+ break;
+ case AMQP_FIELD_KIND_TIMESTAMP:
+ entry->value.u64 = D_64(encoded, offset);
+ offset += 8;
+ break;
+ case AMQP_FIELD_KIND_TABLE:
+ AMQP_CHECK_RESULT(amqp_decode_table(encoded, pool, &(entry->value.table), &offset));
+ break;
+ case AMQP_FIELD_KIND_VOID:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *offsetptr = offset;
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int amqp_encode_array(amqp_bytes_t encoded,
+ amqp_array_t *input,
+ int *offsetptr)
+{
+ int offset = *offsetptr;
+ int arraysize_offset = offset;
+ int i;
+
+ offset += 4; /* skip space for the size of the array to be filled in later */
+
+ for (i = 0; i < input->num_entries; i++) {
+ AMQP_CHECK_RESULT(amqp_encode_field_value(encoded, &(input->entries[i]), &offset));
+ }
+
+ E_32(encoded, arraysize_offset, (offset - *offsetptr - 4));
+ *offsetptr = offset;
+ return 0;
+}
+
int amqp_encode_table(amqp_bytes_t encoded,
amqp_table_t *input,
int *offsetptr)
@@ -114,36 +257,7 @@ int amqp_encode_table(amqp_bytes_t encoded,
E_BYTES(encoded, offset, entry->key.len, entry->key.bytes);
offset += entry->key.len;
- E_8(encoded, offset, entry->kind);
- offset++;
-
- switch (entry->kind) {
- case 'S':
- E_32(encoded, offset, entry->value.bytes.len);
- offset += 4;
- E_BYTES(encoded, offset, entry->value.bytes.len, entry->value.bytes.bytes);
- offset += entry->value.bytes.len;
- break;
- case 'I':
- E_32(encoded, offset, (uint32_t) entry->value.i32);
- offset += 4;
- break;
- case 'D':
- E_8(encoded, offset, entry->value.decimal.decimals);
- offset++;
- E_32(encoded, offset, entry->value.decimal.value);
- offset += 4;
- break;
- case 'T':
- E_64(encoded, offset, entry->value.u64);
- offset += 8;
- break;
- case 'F':
- AMQP_CHECK_RESULT(amqp_encode_table(encoded, &(entry->value.table), &offset));
- break;
- default:
- return -EINVAL;
- }
+ AMQP_CHECK_RESULT(amqp_encode_field_value(encoded, &(entry->value), &offset));
}
E_32(encoded, tablesize_offset, (offset - *offsetptr - 4));
@@ -151,6 +265,96 @@ int amqp_encode_table(amqp_bytes_t encoded,
return 0;
}
+static int amqp_encode_field_value(amqp_bytes_t encoded,
+ amqp_field_value_t *entry,
+ int *offsetptr)
+{
+ int offset = *offsetptr;
+
+ E_8(encoded, offset, entry->kind);
+ offset++;
+
+ switch (entry->kind) {
+ case AMQP_FIELD_KIND_BOOLEAN:
+ E_8(encoded, offset, entry->value.boolean ? 1 : 0);
+ offset++;
+ break;
+ case AMQP_FIELD_KIND_I8:
+ E_8(encoded, offset, (uint8_t) entry->value.i8);
+ offset++;
+ break;
+ case AMQP_FIELD_KIND_U8:
+ E_8(encoded, offset, entry->value.u8);
+ offset++;
+ break;
+ case AMQP_FIELD_KIND_I16:
+ E_16(encoded, offset, (uint16_t) entry->value.i16);
+ offset += 2;
+ break;
+ case AMQP_FIELD_KIND_U16:
+ E_16(encoded, offset, entry->value.u16);
+ offset += 2;
+ break;
+ case AMQP_FIELD_KIND_I32:
+ E_32(encoded, offset, (uint32_t) entry->value.i32);
+ offset += 4;
+ break;
+ case AMQP_FIELD_KIND_U32:
+ E_32(encoded, offset, entry->value.u32);
+ offset += 4;
+ break;
+ case AMQP_FIELD_KIND_I64:
+ E_64(encoded, offset, (uint64_t) entry->value.i64);
+ offset += 8;
+ break;
+ case AMQP_FIELD_KIND_F32:
+ /* by punning, u32 magically gets the right value...! */
+ E_32(encoded, offset, entry->value.u32);
+ offset += 4;
+ break;
+ case AMQP_FIELD_KIND_F64:
+ /* by punning, u64 magically gets the right value...! */
+ E_64(encoded, offset, entry->value.u64);
+ offset += 8;
+ break;
+ case AMQP_FIELD_KIND_DECIMAL:
+ E_8(encoded, offset, entry->value.decimal.decimals);
+ offset++;
+ E_32(encoded, offset, entry->value.decimal.value);
+ offset += 4;
+ break;
+ case AMQP_FIELD_KIND_UTF8:
+ /* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
+ same implementation, but different interpretations. */
+ /* fall through */
+ case AMQP_FIELD_KIND_BYTES:
+ E_32(encoded, offset, entry->value.bytes.len);
+ offset += 4;
+ E_BYTES(encoded, offset, entry->value.bytes.len, entry->value.bytes.bytes);
+ offset += entry->value.bytes.len;
+ break;
+ case AMQP_FIELD_KIND_ARRAY:
+ AMQP_CHECK_RESULT(amqp_encode_array(encoded, &(entry->value.array), &offset));
+ break;
+ case AMQP_FIELD_KIND_TIMESTAMP:
+ E_64(encoded, offset, entry->value.u64);
+ offset += 8;
+ break;
+ case AMQP_FIELD_KIND_TABLE:
+ AMQP_CHECK_RESULT(amqp_encode_table(encoded, &(entry->value.table), &offset));
+ break;
+ case AMQP_FIELD_KIND_VOID:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *offsetptr = offset;
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
int amqp_table_entry_cmp(void const *entry1, void const *entry2) {
amqp_table_entry_t const *p1 = (amqp_table_entry_t const *) entry1;
amqp_table_entry_t const *p2 = (amqp_table_entry_t const *) entry2;
diff --git a/tests/test_tables.c b/tests/test_tables.c
index 9631f49..7618ab5 100644
--- a/tests/test_tables.c
+++ b/tests/test_tables.c
@@ -7,38 +7,241 @@
#include <stdint.h>
#include <amqp.h>
#include <amqp_framing.h>
+#include <amqp_private.h>
#include <unistd.h>
#include <assert.h>
-int main(int argc, char const * const *argv) {
- amqp_table_entry_t entries[8] = { AMQP_TABLE_ENTRY_S("zebra", amqp_cstring_bytes("last")),
- AMQP_TABLE_ENTRY_S("aardvark", amqp_cstring_bytes("first")),
- AMQP_TABLE_ENTRY_S("middle", amqp_cstring_bytes("third")),
- AMQP_TABLE_ENTRY_I("number", 1234),
- AMQP_TABLE_ENTRY_D("decimal", AMQP_DECIMAL(2, 1234)),
- AMQP_TABLE_ENTRY_T("time", (uint64_t) 1234123412341234LL),
- AMQP_TABLE_ENTRY_S("beta", amqp_cstring_bytes("second")),
- AMQP_TABLE_ENTRY_S("wombat", amqp_cstring_bytes("fourth")) };
+#include <math.h>
+
+static void dump_indent(int indent) {
+ int i;
+ for (i = 0; i < indent; i++) { putchar(' '); }
+}
+
+static void dump_value(int indent, amqp_field_value_t v) {
+ dump_indent(indent);
+ putchar(v.kind);
+ putchar(' ');
+ switch (v.kind) {
+ case AMQP_FIELD_KIND_BOOLEAN: puts(v.value.boolean ? "true" : "false"); break;
+ case AMQP_FIELD_KIND_I8: printf("%d\n", v.value.i8); break;
+ case AMQP_FIELD_KIND_U8: printf("%d\n", v.value.u8); break;
+ case AMQP_FIELD_KIND_I16: printf("%d\n", v.value.i16); break;
+ case AMQP_FIELD_KIND_U16: printf("%d\n", v.value.u16); break;
+ case AMQP_FIELD_KIND_I32: printf("%ld\n", (long) v.value.i32); break;
+ case AMQP_FIELD_KIND_U32: printf("%lu\n", (unsigned long) v.value.u32); break;
+ case AMQP_FIELD_KIND_I64: printf("%lld\n", (long long) v.value.i64); break;
+ case AMQP_FIELD_KIND_F32: printf("%g\n", (double) v.value.f32); break;
+ case AMQP_FIELD_KIND_F64: printf("%g\n", v.value.f64); break;
+ case AMQP_FIELD_KIND_DECIMAL:
+ printf("%d:::%u\n", v.value.decimal.decimals, v.value.decimal.value); break;
+ case AMQP_FIELD_KIND_UTF8:
+ printf("%.*s\n", (int) v.value.bytes.len, (char *) v.value.bytes.bytes); break;
+ case AMQP_FIELD_KIND_BYTES:
+ {
+ int i;
+ for (i = 0; i < v.value.bytes.len; i++) {
+ printf("%02x", ((char *) v.value.bytes.bytes)[i]);
+ }
+ putchar('\n');
+ }
+ break;
+ case AMQP_FIELD_KIND_ARRAY:
+ putchar('\n');
+ {
+ int i;
+ for (i = 0; i < v.value.array.num_entries; i++) {
+ dump_value(indent + 2, v.value.array.entries[i]);
+ }
+ }
+ break;
+ case AMQP_FIELD_KIND_TIMESTAMP: printf("%llu\n", (unsigned long long) v.value.u64); break;
+ case AMQP_FIELD_KIND_TABLE:
+ putchar('\n');
+ {
+ int i;
+ for (i = 0; i < v.value.table.num_entries; i++) {
+ dump_indent(indent + 2);
+ printf("%.*s ->\n",
+ (int) v.value.table.entries[i].key.len,
+ (char *) v.value.table.entries[i].key.bytes);
+ dump_value(indent + 4, v.value.table.entries[i].value);
+ }
+ }
+ break;
+ case AMQP_FIELD_KIND_VOID: putchar('\n'); break;
+ default:
+ printf("???\n");
+ break;
+ }
+}
+
+static void test_table_codec(void) {
+ amqp_table_entry_t inner_entries[2] =
+ { AMQP_TABLE_ENTRY_I32("one", 54321),
+ AMQP_TABLE_ENTRY_UTF8("two", amqp_cstring_bytes("A long string")) };
+ amqp_table_t inner_table = { .num_entries = sizeof(inner_entries) / sizeof(inner_entries[0]),
+ .entries = &inner_entries[0] };
+
+ amqp_field_value_t inner_values[2] =
+ { AMQP_FIELD_VALUE_I32(54321),
+ AMQP_FIELD_VALUE_UTF8(amqp_cstring_bytes("A long string")) };
+ amqp_array_t inner_array = { .num_entries = sizeof(inner_values) / sizeof(inner_values[0]),
+ .entries = &inner_values[0] };
+
+ amqp_table_entry_t entries[14] =
+ { AMQP_TABLE_ENTRY_UTF8("longstr", amqp_cstring_bytes("Here is a long string")),
+ AMQP_TABLE_ENTRY_I32("signedint", 12345),
+ AMQP_TABLE_ENTRY_DECIMAL("decimal", AMQP_DECIMAL(3, 123456)),
+ AMQP_TABLE_ENTRY_TIMESTAMP("timestamp", 109876543209876),
+ AMQP_TABLE_ENTRY_TABLE("table", inner_table),
+ AMQP_TABLE_ENTRY_I8("byte", 255),
+ AMQP_TABLE_ENTRY_I64("long", 1234567890),
+ AMQP_TABLE_ENTRY_I16("short", 655),
+ AMQP_TABLE_ENTRY_BOOLEAN("bool", 1),
+ AMQP_TABLE_ENTRY_BYTES("binary", amqp_cstring_bytes("a binary string")),
+ AMQP_TABLE_ENTRY_VOID("void"),
+ AMQP_TABLE_ENTRY_ARRAY("array", inner_array),
+ AMQP_TABLE_ENTRY_F32("float", M_PI),
+ AMQP_TABLE_ENTRY_F64("double", M_PI) };
amqp_table_t table = { .num_entries = sizeof(entries) / sizeof(entries[0]),
.entries = &entries[0] };
- int i;
- qsort(table.entries, table.num_entries, sizeof(amqp_table_entry_t), &amqp_table_entry_cmp);
+ uint8_t pre_encoded_table[] = {
+ 0x00, 0x00, 0x00, 0xff, 0x07, 0x6c, 0x6f, 0x6e,
+ 0x67, 0x73, 0x74, 0x72, 0x53, 0x00, 0x00, 0x00,
+ 0x15, 0x48, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73,
+ 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20,
+ 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x09, 0x73,
+ 0x69, 0x67, 0x6e, 0x65, 0x64, 0x69, 0x6e, 0x74,
+ 0x49, 0x00, 0x00, 0x30, 0x39, 0x07, 0x64, 0x65,
+ 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x44, 0x03, 0x00,
+ 0x01, 0xe2, 0x40, 0x09, 0x74, 0x69, 0x6d, 0x65,
+ 0x73, 0x74, 0x61, 0x6d, 0x70, 0x54, 0x00, 0x00,
+ 0x63, 0xee, 0xa0, 0x53, 0xc1, 0x94, 0x05, 0x74,
+ 0x61, 0x62, 0x6c, 0x65, 0x46, 0x00, 0x00, 0x00,
+ 0x1f, 0x03, 0x6f, 0x6e, 0x65, 0x49, 0x00, 0x00,
+ 0xd4, 0x31, 0x03, 0x74, 0x77, 0x6f, 0x53, 0x00,
+ 0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e,
+ 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x04, 0x62, 0x79, 0x74, 0x65, 0x62, 0xff, 0x04,
+ 0x6c, 0x6f, 0x6e, 0x67, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x96, 0x02, 0xd2, 0x05, 0x73, 0x68,
+ 0x6f, 0x72, 0x74, 0x73, 0x02, 0x8f, 0x04, 0x62,
+ 0x6f, 0x6f, 0x6c, 0x74, 0x01, 0x06, 0x62, 0x69,
+ 0x6e, 0x61, 0x72, 0x79, 0x78, 0x00, 0x00, 0x00,
+ 0x0f, 0x61, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72,
+ 0x79, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x04, 0x76, 0x6f, 0x69, 0x64, 0x56, 0x05, 0x61,
+ 0x72, 0x72, 0x61, 0x79, 0x41, 0x00, 0x00, 0x00,
+ 0x17, 0x49, 0x00, 0x00, 0xd4, 0x31, 0x53, 0x00,
+ 0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e,
+ 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x66, 0x40,
+ 0x49, 0x0f, 0xdb, 0x06, 0x64, 0x6f, 0x75, 0x62,
+ 0x6c, 0x65, 0x64, 0x40, 0x09, 0x21, 0xfb, 0x54,
+ 0x44, 0x2d, 0x18
+ };
+
+ amqp_pool_t pool;
+ int result;
+
+ printf("AAAAAAAAAA\n");
+ dump_value(0, (amqp_field_value_t) AMQP_FIELD_VALUE_TABLE(table));
+
+ init_amqp_pool(&pool, 4096);
- for (i = 0; i < table.num_entries; i++) {
- amqp_table_entry_t *e = &table.entries[i];
- printf("%.*s -> %c (", (int) e->key.len, (char *) e->key.bytes, e->kind);
- switch (e->kind) {
- case 'S': printf("%.*s", (int) e->value.bytes.len, (char *) e->value.bytes.bytes); break;
- case 'I': printf("%d", e->value.i32); break;
- case 'D': printf("%d:::%u", e->value.decimal.decimals, e->value.decimal.value); break;
- case 'T': printf("%llu", e->value.u64); break;
- case 'F': printf("..."); break;
- default: printf("???"); break;
+ {
+ amqp_bytes_t decoding_bytes = { .len = sizeof(pre_encoded_table),
+ .bytes = pre_encoded_table };
+ amqp_table_t decoded;
+ int decoding_offset = 0;
+ result = amqp_decode_table(decoding_bytes, &pool, &decoded, &decoding_offset);
+ if (result < 0) {
+ printf("Table decoding failed: %d (%s)\n", result, strerror(-result));
+ abort();
}
- printf(")\n");
+ printf("BBBBBBBBBB\n");
+ dump_value(0, (amqp_field_value_t) AMQP_FIELD_VALUE_TABLE(decoded));
}
+ {
+ uint8_t encoding_buffer[4096];
+ amqp_bytes_t encoding_result;
+ int offset = 0;
+
+ memset(&encoding_buffer[0], 0, sizeof(encoding_buffer));
+ encoding_result.len = sizeof(encoding_buffer);
+ encoding_result.bytes = &encoding_buffer[0];
+
+ result = amqp_encode_table(encoding_result, &table, &offset);
+ if (result < 0) {
+ printf("Table encoding failed: %d (%s)\n", result, strerror(-result));
+ abort();
+ }
+
+ if (offset != sizeof(pre_encoded_table)) {
+ printf("Offset should be %d, was %d\n", (int) sizeof(pre_encoded_table), offset);
+ abort();
+ }
+
+ result = memcmp(pre_encoded_table, encoding_buffer, offset);
+ if (result != 0) {
+ printf("Table encoding differed, result = %d\n", result);
+ abort();
+ }
+ }
+
+ empty_amqp_pool(&pool);
+}
+
+int main(int argc, char const * const *argv) {
+ amqp_table_entry_t entries[8] =
+ { AMQP_TABLE_ENTRY_UTF8("zebra", amqp_cstring_bytes("last")),
+ AMQP_TABLE_ENTRY_UTF8("aardvark", amqp_cstring_bytes("first")),
+ AMQP_TABLE_ENTRY_UTF8("middle", amqp_cstring_bytes("third")),
+ AMQP_TABLE_ENTRY_I32("number", 1234),
+ AMQP_TABLE_ENTRY_DECIMAL("decimal", AMQP_DECIMAL(2, 1234)),
+ AMQP_TABLE_ENTRY_TIMESTAMP("time", (uint64_t) 1234123412341234LL),
+ AMQP_TABLE_ENTRY_UTF8("beta", amqp_cstring_bytes("second")),
+ AMQP_TABLE_ENTRY_UTF8("wombat", amqp_cstring_bytes("fourth")) };
+ amqp_table_t table = { .num_entries = sizeof(entries) / sizeof(entries[0]),
+ .entries = &entries[0] };
+
+ union {
+ uint32_t i;
+ float f;
+ } vi;
+ union {
+ uint64_t l;
+ double d;
+ } vl;
+
+ vi.f = M_PI;
+ if ((sizeof(float) != 4) || (vi.i != 0x40490fdb)) {
+ printf("*** ERROR: single floating point encoding does not work as expected\n");
+ printf("sizeof float is %lu, float is %g, u32 is 0x%08lx\n",
+ sizeof(float),
+ vi.f,
+ (unsigned long) vi.i);
+ }
+
+ vl.d = M_PI;
+ if ((sizeof(double) != 8) || (vl.l != 0x400921fb54442d18L)) {
+ printf("*** ERROR: double floating point encoding does not work as expected\n");
+ printf("sizeof double is %lu, double is %g, u64 is 0x%16llx\n",
+ sizeof(double),
+ vl.d,
+ (unsigned long long) vl.l);
+ }
+
+ test_table_codec();
+
+ qsort(table.entries, table.num_entries, sizeof(amqp_table_entry_t), &amqp_table_entry_cmp);
+
+ printf("----------\n");
+ dump_value(0, (amqp_field_value_t) AMQP_FIELD_VALUE_TABLE(table));
+
return 0;
}