#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include "amqp.h"
#include "amqp_private.h"
#include <assert.h>
#define INITIAL_TABLE_SIZE 16
int amqp_decode_table(amqp_bytes_t encoded,
amqp_pool_t *pool,
amqp_table_t *output,
int *offsetptr)
{
int offset = *offsetptr;
uint32_t tablesize = D_32(encoded, offset);
int num_entries = 0;
amqp_table_entry_t *entries = malloc(INITIAL_TABLE_SIZE * sizeof(amqp_table_entry_t));
int allocated_entries = INITIAL_TABLE_SIZE;
int limit;
if (entries == NULL) {
return -ENOMEM;
}
offset += 4;
limit = offset + tablesize;
while (offset < limit) {
size_t keylen;
amqp_table_entry_t *entry;
keylen = D_8(encoded, offset);
offset++;
if (num_entries >= allocated_entries) {
void *newentries;
allocated_entries = allocated_entries * 2;
newentries = realloc(entries, allocated_entries * sizeof(amqp_table_entry_t));
if (newentries == NULL) {
free(entries);
return -ENOMEM;
}
entries = newentries;
}
entry = &entries[num_entries];
entry->key.len = keylen;
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;
case 't':
entry->value.boolean = D_8(encoded, offset);
offset += 1;
break;
default:
return -EINVAL;
}
num_entries++;
}
output->num_entries = num_entries;
output->entries = amqp_pool_alloc(pool, num_entries * sizeof(amqp_table_entry_t));
output->size = num_entries;
memcpy(output->entries, entries, num_entries * sizeof(amqp_table_entry_t));
*offsetptr = offset;
return 0;
}
int amqp_encode_table(amqp_bytes_t encoded,
amqp_table_t *input,
int *offsetptr)
{
int offset = *offsetptr;
int tablesize_offset = offset;
int i;
offset += 4; /* skip space for the size of the table to be filled in later */
for (i = 0; i < input->num_entries; i++) {
amqp_table_entry_t *entry = &(input->entries[i]);
E_8(encoded, offset, entry->key.len);
offset++;
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;
}
}
E_32(encoded, tablesize_offset, (offset - *offsetptr - 4));
*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;
int d;
int minlen;
minlen = p1->key.len;
if (p2->key.len < minlen) minlen = p2->key.len;
d = memcmp(p1->key.bytes, p2->key.bytes, minlen);
if (d != 0) {
return d;
}
return p1->key.len - p2->key.len;
}
void amqp_create_table(amqp_connection_state_t state,
amqp_table_t *output,
int initialSize) {
output->entries = amqp_pool_alloc(&state->frame_pool, initialSize * sizeof(amqp_table_entry_t));
output->size = initialSize;
output->num_entries = 0;
}
amqp_table_entry_t *amqp_table_add_entry( amqp_connection_state_t state,
amqp_table_t *table,
amqp_bytes_t key)
{
amqp_table_entry_t *entry;
if (table->num_entries == table->size)
{
int new_size = table->size * 2;
amqp_table_entry_t *new_entries = amqp_pool_alloc( &state->frame_pool, new_size * sizeof(amqp_table_entry_t) );
memcpy( new_entries, table->entries, table->size * sizeof(amqp_table_entry_t) );
table->size = new_size;
table->entries = new_entries;
}
entry = &table->entries[table->num_entries];
table->num_entries++;
entry->key = key;
return entry;
}
void amqp_table_add_string(amqp_connection_state_t state,
amqp_table_t *output,
amqp_bytes_t key,
amqp_bytes_t value)
{
amqp_table_entry_t *entry = amqp_table_add_entry( state, output, key );
entry->kind = 'S';
entry->value.bytes = value;
}
void amqp_table_add_int(amqp_connection_state_t state,
amqp_table_t *output,
amqp_bytes_t key,
int value)
{
amqp_table_entry_t *entry = amqp_table_add_entry( state, output, key );
entry->kind = 'I';
entry->value.i32 = value;
}