| /* |
| * 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 <qpid/dispatch/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; |
| |
| 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; |
| |
| case QD_AMQP_SHORT: |
| result |= ((int32_t) qd_field_iterator_octet(field->raw_iter)) << 8; |
| // Fall Through... |
| |
| case QD_AMQP_BYTE: |
| case QD_AMQP_SMALLINT: |
| case QD_AMQP_BOOLEAN: |
| result |= (int32_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; |
| // Fall Through... |
| |
| case QD_AMQP_SMALLLONG: |
| result |= (uint64_t) qd_field_iterator_octet(field->raw_iter); |
| 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; |
| } |