diff options
author | Tony Garnock-Jones <tonyg@lshift.net> | 2009-11-17 00:29:21 +0000 |
---|---|---|
committer | Tony Garnock-Jones <tonyg@lshift.net> | 2009-11-17 00:29:21 +0000 |
commit | 136afe97cfa1815d28f79fba98f16e342923a4d0 (patch) | |
tree | 84a2384d9a796d27045878dfbe257db2610f7280 | |
parent | 4f7fdf5dd5f2749809f71fc3c556fc23f8832f10 (diff) | |
download | rabbitmq-c-github-ask-136afe97cfa1815d28f79fba98f16e342923a4d0.tar.gz |
Implement Qpid/RabbitMQ/0-9-1 field table types.
-rw-r--r-- | librabbitmq/amqp.h | 131 | ||||
-rw-r--r-- | librabbitmq/amqp_private.h | 6 | ||||
-rw-r--r-- | librabbitmq/amqp_table.c | 326 | ||||
-rw-r--r-- | tests/test_tables.c | 247 |
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; } |