blob: 55050cac6748e104241ddc1f7d69a000428dd6db [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "alloc.h"
#include <qpid/dispatch/ctools.h>
#include <qpid/dispatch/parse.h>
#include <qpid/dispatch/amqp.h>
DEQ_DECLARE(qd_parsed_field_t, qd_parsed_field_list_t);
struct qd_parsed_field_t {
DEQ_LINKS(qd_parsed_field_t);
qd_parsed_field_t *parent;
qd_parsed_field_list_t children;
uint8_t tag;
qd_field_iterator_t *raw_iter;
const char *parse_error;
};
ALLOC_DECLARE(qd_parsed_field_t);
ALLOC_DEFINE(qd_parsed_field_t);
static char *get_type_info(qd_field_iterator_t *iter, uint8_t *tag, uint32_t *length, uint32_t *count, uint32_t *clen)
{
if (qd_field_iterator_end(iter))
return "Insufficient Data to Determine Tag";
*tag = qd_field_iterator_octet(iter);
*count = 0;
*length = 0;
*clen = 0;
switch (*tag & 0xF0) {
case 0x40: *length = 0; break;
case 0x50: *length = 1; break;
case 0x60: *length = 2; break;
case 0x70: *length = 4; break;
case 0x80: *length = 8; break;
case 0x90: *length = 16; break;
case 0xB0:
case 0xD0:
case 0xF0:
*length += ((unsigned int) qd_field_iterator_octet(iter)) << 24;
*length += ((unsigned int) qd_field_iterator_octet(iter)) << 16;
*length += ((unsigned int) qd_field_iterator_octet(iter)) << 8;
// fall through to the next case
case 0xA0:
case 0xC0:
case 0xE0:
if (qd_field_iterator_end(iter))
return "Insufficient Data to Determine Length";
*length += (unsigned int) qd_field_iterator_octet(iter);
break;
default:
return "Invalid Tag - No Length Information";
}
switch (*tag & 0xF0) {
case 0xD0:
case 0xF0:
*count += ((unsigned int) qd_field_iterator_octet(iter)) << 24;
*count += ((unsigned int) qd_field_iterator_octet(iter)) << 16;
*count += ((unsigned int) qd_field_iterator_octet(iter)) << 8;
*clen = 3;
// fall through to the next case
case 0xC0:
case 0xE0:
if (qd_field_iterator_end(iter))
return "Insufficient Data to Determine Count";
*count += (unsigned int) qd_field_iterator_octet(iter);
*clen += 1;
break;
}
if ((*tag == QD_AMQP_MAP8 || *tag == QD_AMQP_MAP32) && (*count & 1))
return "Odd Number of Elements in a Map";
if (*clen > *length)
return "Insufficient Length to Determine Count";
return 0;
}
static qd_parsed_field_t *qd_parse_internal(qd_field_iterator_t *iter, qd_parsed_field_t *p)
{
qd_parsed_field_t *field = new_qd_parsed_field_t();
if (!field)
return 0;
DEQ_ITEM_INIT(field);
DEQ_INIT(field->children);
field->parent = p;
field->raw_iter = 0;
uint32_t length;
uint32_t count;
uint32_t length_of_count;
field->parse_error = get_type_info(iter, &field->tag, &length, &count, &length_of_count);
if (!field->parse_error) {
field->raw_iter = qd_field_iterator_sub(iter, length);
qd_field_iterator_advance(iter, length - length_of_count);
for (uint32_t idx = 0; idx < count; idx++) {
qd_parsed_field_t *child = qd_parse_internal(field->raw_iter, field);
DEQ_INSERT_TAIL(field->children, child);
if (!qd_parse_ok(child)) {
field->parse_error = child->parse_error;
break;
}
}
}
return field;
}
qd_parsed_field_t *qd_parse(qd_field_iterator_t *iter)
{
return qd_parse_internal(iter, 0);
}
void qd_parse_free(qd_parsed_field_t *field)
{
if (!field)
return;
assert(field->parent == 0);
if (field->raw_iter)
qd_field_iterator_free(field->raw_iter);
qd_parsed_field_t *sub_field = DEQ_HEAD(field->children);
while (sub_field) {
qd_parsed_field_t *next = DEQ_NEXT(sub_field);
DEQ_REMOVE_HEAD(field->children);
sub_field->parent = 0;
qd_parse_free(sub_field);
sub_field = next;
}
free_qd_parsed_field_t(field);
}
int qd_parse_ok(qd_parsed_field_t *field)
{
return field->parse_error == 0;
}
const char *qd_parse_error(qd_parsed_field_t *field)
{
return field->parse_error;
}
uint8_t qd_parse_tag(qd_parsed_field_t *field)
{
return field->tag;
}
qd_field_iterator_t *qd_parse_raw(qd_parsed_field_t *field)
{
return field->raw_iter;
}
uint32_t qd_parse_as_uint(qd_parsed_field_t *field)
{
uint32_t result = 0;
qd_field_iterator_reset(field->raw_iter);
switch (field->tag) {
case QD_AMQP_UINT:
result |= ((uint32_t) qd_field_iterator_octet(field->raw_iter)) << 24;
result |= ((uint32_t) qd_field_iterator_octet(field->raw_iter)) << 16;
// fallthrough
case QD_AMQP_USHORT:
result |= ((uint32_t) qd_field_iterator_octet(field->raw_iter)) << 8;
// Fall Through...
case QD_AMQP_UBYTE:
case QD_AMQP_SMALLUINT:
case QD_AMQP_BOOLEAN:
result |= (uint32_t) qd_field_iterator_octet(field->raw_iter);
break;
case QD_AMQP_TRUE:
result = 1;
break;
}
return result;
}
uint64_t qd_parse_as_ulong(qd_parsed_field_t *field)
{
uint64_t result = 0;
qd_field_iterator_reset(field->raw_iter);
switch (field->tag) {
case QD_AMQP_ULONG:
case QD_AMQP_TIMESTAMP:
result |= ((uint64_t) qd_field_iterator_octet(field->raw_iter)) << 56;
result |= ((uint64_t) qd_field_iterator_octet(field->raw_iter)) << 48;
result |= ((uint64_t) qd_field_iterator_octet(field->raw_iter)) << 40;
result |= ((uint64_t) qd_field_iterator_octet(field->raw_iter)) << 32;
result |= ((uint64_t) qd_field_iterator_octet(field->raw_iter)) << 24;
result |= ((uint64_t) qd_field_iterator_octet(field->raw_iter)) << 16;
result |= ((uint64_t) qd_field_iterator_octet(field->raw_iter)) << 8;
// Fall Through...
case QD_AMQP_SMALLULONG:
result |= (uint64_t) qd_field_iterator_octet(field->raw_iter);
// Fall Through...
case QD_AMQP_ULONG0:
break;
}
return result;
}
int32_t qd_parse_as_int(qd_parsed_field_t *field)
{
int32_t result = 0;
qd_field_iterator_reset(field->raw_iter);
switch (field->tag) {
case QD_AMQP_INT:
result |= ((int32_t) qd_field_iterator_octet(field->raw_iter)) << 24;
result |= ((int32_t) qd_field_iterator_octet(field->raw_iter)) << 16;
// Fall Through...
case QD_AMQP_SHORT:
result |= ((int32_t) qd_field_iterator_octet(field->raw_iter)) << 8;
// Fall Through...
case QD_AMQP_BYTE:
case QD_AMQP_BOOLEAN:
result |= (int32_t) qd_field_iterator_octet(field->raw_iter);
break;
case QD_AMQP_SMALLINT:
result = (int8_t) qd_field_iterator_octet(field->raw_iter);
break;
case QD_AMQP_TRUE:
result = 1;
break;
}
return result;
}
int64_t qd_parse_as_long(qd_parsed_field_t *field)
{
int64_t result = 0;
qd_field_iterator_reset(field->raw_iter);
switch (field->tag) {
case QD_AMQP_LONG:
result |= ((int64_t) qd_field_iterator_octet(field->raw_iter)) << 56;
result |= ((int64_t) qd_field_iterator_octet(field->raw_iter)) << 48;
result |= ((int64_t) qd_field_iterator_octet(field->raw_iter)) << 40;
result |= ((int64_t) qd_field_iterator_octet(field->raw_iter)) << 32;
result |= ((int64_t) qd_field_iterator_octet(field->raw_iter)) << 24;
result |= ((int64_t) qd_field_iterator_octet(field->raw_iter)) << 16;
result |= ((int64_t) qd_field_iterator_octet(field->raw_iter)) << 8;
result |= (uint64_t) qd_field_iterator_octet(field->raw_iter);
break;
case QD_AMQP_SMALLLONG:
result = (int8_t) qd_field_iterator_octet(field->raw_iter);
break;
}
return result;
}
bool qd_parse_as_bool(qd_parsed_field_t *field)
{
bool result = false;
qd_field_iterator_reset(field->raw_iter);
switch (field->tag) {
case QD_AMQP_BYTE:
case QD_AMQP_BOOLEAN:
result = !!qd_field_iterator_octet(field->raw_iter);
break;
case QD_AMQP_TRUE:
result = true;
break;
}
return result;
}
uint32_t qd_parse_sub_count(qd_parsed_field_t *field)
{
uint32_t count = DEQ_SIZE(field->children);
if (field->tag == QD_AMQP_MAP8 || field->tag == QD_AMQP_MAP32)
count = count >> 1;
return count;
}
qd_parsed_field_t *qd_parse_sub_key(qd_parsed_field_t *field, uint32_t idx)
{
if (field->tag != QD_AMQP_MAP8 && field->tag != QD_AMQP_MAP32)
return 0;
idx = idx << 1;
qd_parsed_field_t *key = DEQ_HEAD(field->children);
while (idx && key) {
idx--;
key = DEQ_NEXT(key);
}
return key;
}
qd_parsed_field_t *qd_parse_sub_value(qd_parsed_field_t *field, uint32_t idx)
{
if (field->tag == QD_AMQP_MAP8 || field->tag == QD_AMQP_MAP32)
idx = (idx << 1) + 1;
qd_parsed_field_t *key = DEQ_HEAD(field->children);
while (idx && key) {
idx--;
key = DEQ_NEXT(key);
}
return key;
}
int qd_parse_is_map(qd_parsed_field_t *field)
{
return field->tag == QD_AMQP_MAP8 || field->tag == QD_AMQP_MAP32;
}
int qd_parse_is_list(qd_parsed_field_t *field)
{
return field->tag == QD_AMQP_LIST8 || field->tag == QD_AMQP_LIST32;
}
int qd_parse_is_scalar(qd_parsed_field_t *field)
{
return DEQ_SIZE(field->children) == 0;
}
qd_parsed_field_t *qd_parse_value_by_key(qd_parsed_field_t *field, const char *key)
{
uint32_t count = qd_parse_sub_count(field);
for (uint32_t idx = 0; idx < count; idx++) {
qd_parsed_field_t *sub = qd_parse_sub_key(field, idx);
if (!sub)
return 0;
qd_field_iterator_t *iter = qd_parse_raw(sub);
if (!iter)
return 0;
if (qd_field_iterator_equal(iter, (const unsigned char*) key)) {
return qd_parse_sub_value(field, idx);
}
}
return 0;
}