This is an automated email from the ASF dual-hosted git repository. astitcher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/qpid-proton.git
commit a4fa8a35f9dc8b6a8ae657bd6853d8cb6a47cc35 Author: Andrew Stitcher <[email protected]> AuthorDate: Fri Sep 17 17:38:43 2021 -0400 PROTON-2448: Add code to dump AMQP values direct from a bytestream All AMQP types except single unicode chars are now supported. --- c/CMakeLists.txt | 1 + c/src/core/consumers.h | 164 +++++++++ c/src/core/framing.c | 53 +-- c/src/core/framing.h | 6 +- c/src/core/util.h | 38 +++ c/src/core/value_dump.c | 600 +++++++++++++++++++++++++++++++++ c/src/core/{framing.h => value_dump.h} | 29 +- 7 files changed, 819 insertions(+), 72 deletions(-) diff --git a/c/CMakeLists.txt b/c/CMakeLists.txt index f505ff8..bba89ef 100644 --- a/c/CMakeLists.txt +++ b/c/CMakeLists.txt @@ -227,6 +227,7 @@ set (qpid-proton-core src/core/types.c src/core/framing.c + src/core/value_dump.c src/core/codec.c src/core/decoder.c diff --git a/c/src/core/consumers.h b/c/src/core/consumers.h new file mode 100644 index 0000000..5bba0c0 --- /dev/null +++ b/c/src/core/consumers.h @@ -0,0 +1,164 @@ +#ifndef PROTON_CONSUMERS_H +#define PROTON_CONSUMERS_H + +/* + * + * 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. + * + */ + +/* Definitions of AMQP type codes */ +#include "encodings.h" + +#include "proton/codec.h" +#include "proton/types.h" + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +typedef struct pni_consumer_t { + const uint8_t* output_start; + size_t size; + size_t position; +} pni_consumer_t; + +static inline pni_consumer_t make_consumer_from_bytes(pn_bytes_t output_bytes) { + return (pni_consumer_t){ + .output_start = (const uint8_t*) output_bytes.start, + .size = output_bytes.size, + .position = 0 + }; +} + +static inline bool pni_consumer_readf8(pni_consumer_t *consumer, uint8_t* result) +{ + if (consumer->position+1 > consumer->size) { + consumer->position = consumer->size; + return false; + } + uint8_t r = consumer->output_start[consumer->position+0]; + consumer->position++; + *result = r; + return true; +} + +static inline bool pni_consumer_readf16(pni_consumer_t *consumer, uint16_t* result) +{ + if (consumer->position+2 > consumer->size) { + consumer->position = consumer->size; + return false; + } + uint16_t a = consumer->output_start[consumer->position+0]; + uint16_t b = consumer->output_start[consumer->position+1]; + uint16_t r = a << 8 + | b; + consumer->position += 2; + *result = r; + return true; +} + +static inline bool pni_consumer_readf32(pni_consumer_t *consumer, uint32_t* result) +{ + if (consumer->position+4 > consumer->size) { + consumer->position = consumer->size; + return false; + } + uint32_t a = consumer->output_start[consumer->position+0]; + uint32_t b = consumer->output_start[consumer->position+1]; + uint32_t c = consumer->output_start[consumer->position+2]; + uint32_t d = consumer->output_start[consumer->position+3]; + uint32_t r = a << 24 + | b << 16 + | c << 8 + | d; + consumer->position += 4; + *result = r; + return true; +} + +static inline bool pni_consumer_read_value_not_described(pni_consumer_t* consumer, uint8_t type, pn_bytes_t *value) { + uint8_t subcategory = type >> 4; + switch (subcategory) { + // Fixed width types: + // No data + case 0x4: + *value = (pn_bytes_t){.size=0,.start=NULL}; + return true; + // 1 Octet + case 0x5: + if (consumer->position+1 > consumer->size) break; + *value = (pn_bytes_t){.size=1,.start=(const char *)consumer->output_start+consumer->position}; + consumer->position += 1; + return true; + // 2 Octets + case 0x6: + if (consumer->position+2 > consumer->size) break; + *value = (pn_bytes_t){.size=2,.start=(const char *)consumer->output_start+consumer->position}; + consumer->position += 2; + return true; + // 4 Octets + case 0x7: + if (consumer->position+4 > consumer->size) break; + *value = (pn_bytes_t){.size=4,.start=(const char *)consumer->output_start+consumer->position}; + consumer->position += 4; + return true; + // 8 Octets + case 0x8: + if (consumer->position+8 > consumer->size) break; + *value = (pn_bytes_t){.size=8,.start=(const char *)consumer->output_start+consumer->position}; + consumer->position += 8; + return true; + // 16 Octets + case 0x9: + if (consumer->position+16 > consumer->size) break; + *value = (pn_bytes_t){.size=16,.start=(const char *)consumer->output_start+consumer->position}; + consumer->position += 16; + return true; + // Variable width types: + // One Octet of size + case 0xA: + case 0xC: + case 0xE: { + uint8_t size; + if (!pni_consumer_readf8(consumer, &size)) return false; + if (consumer->position+size > consumer->size) break; + *value = (pn_bytes_t){.size=size,.start=(const char *)consumer->output_start+consumer->position}; + consumer->position += size; + return true; + } + // 4 Octets of size + case 0xB: + case 0xD: + case 0xF: { + uint32_t size; + if (!pni_consumer_readf32(consumer, &size)) return false; + if (consumer->position+size > consumer->size) break; + *value = (pn_bytes_t){.size=size,.start=(const char *)consumer->output_start+consumer->position}; + consumer->position += size; + return true; + } + default: + break; + } + consumer->position = consumer->size; + return false; +} + +#endif // PROTON_CONSUMERS_H diff --git a/c/src/core/framing.c b/c/src/core/framing.c index 9f78666..bc484a9 100644 --- a/c/src/core/framing.c +++ b/c/src/core/framing.c @@ -19,55 +19,16 @@ * */ -#include <stdio.h> -#include <string.h> #include "framing.h" -// TODO: These are near duplicates of code in codec.c - they should be -// deduplicated. -static inline void pn_i_write16(char *bytes, uint16_t value) -{ - bytes[0] = 0xFF & (value >> 8); - bytes[1] = 0xFF & (value ); -} - - -static inline void pn_i_write32(char *bytes, uint32_t value) -{ - bytes[0] = 0xFF & (value >> 24); - bytes[1] = 0xFF & (value >> 16); - bytes[2] = 0xFF & (value >> 8); - bytes[3] = 0xFF & (value ); -} - -static inline uint16_t pn_i_read16(const char *bytes) -{ - uint16_t a = (uint8_t) bytes[0]; - uint16_t b = (uint8_t) bytes[1]; - uint16_t r = a << 8 - | b; - return r; -} - -static inline uint32_t pn_i_read32(const char *bytes) -{ - uint32_t a = (uint8_t) bytes[0]; - uint32_t b = (uint8_t) bytes[1]; - uint32_t c = (uint8_t) bytes[2]; - uint32_t d = (uint8_t) bytes[3]; - uint32_t r = a << 24 - | b << 16 - | c << 8 - | d; - return r; -} - +#include "engine-internal.h" +#include "util.h" ssize_t pn_read_frame(pn_frame_t *frame, const char *bytes, size_t available, uint32_t max) { if (available < AMQP_HEADER_SIZE) return 0; - uint32_t size = pn_i_read32(&bytes[0]); + uint32_t size = pni_read32(&bytes[0]); if (max && size > max) return PN_ERR; if (available < size) return 0; unsigned int doff = 4 * (uint8_t)bytes[4]; @@ -76,7 +37,7 @@ ssize_t pn_read_frame(pn_frame_t *frame, const char *bytes, size_t available, ui frame->size = size - doff; frame->ex_size = doff - AMQP_HEADER_SIZE; frame->type = bytes[5]; - frame->channel = pn_i_read16(&bytes[6]); + frame->channel = pni_read16(&bytes[6]); frame->extended = bytes + AMQP_HEADER_SIZE; frame->payload = bytes + doff; @@ -90,16 +51,16 @@ size_t pn_write_frame(pn_buffer_t* buffer, pn_frame_t frame) { // Prepare header char bytes[8]; - pn_i_write32(&bytes[0], size); + pni_write32(&bytes[0], size); int doff = (frame.ex_size + AMQP_HEADER_SIZE - 1)/4 + 1; bytes[4] = doff; bytes[5] = frame.type; - pn_i_write16(&bytes[6], frame.channel); + pni_write16(&bytes[6], frame.channel); // Write header then rest of frame pn_buffer_append(buffer, bytes, 8); if (frame.extended) - pn_buffer_append(buffer, frame.extended, frame.ex_size); + pn_buffer_append(buffer, frame.extended, frame.ex_size); pn_buffer_append(buffer, frame.payload, frame.size); return size; } else { diff --git a/c/src/core/framing.h b/c/src/core/framing.h index 92c1f7d..46e0d5e 100644 --- a/c/src/core/framing.h +++ b/c/src/core/framing.h @@ -24,9 +24,9 @@ #include "buffer.h" -#include <proton/import_export.h> -#include <proton/type_compat.h> -#include <proton/error.h> +#include "proton/types.h" + +#include <stddef.h> #define AMQP_HEADER_SIZE (8) #define AMQP_MIN_MAX_FRAME_SIZE ((uint32_t)512) // minimum allowable max-frame diff --git a/c/src/core/util.h b/c/src/core/util.h index ea049fd..7895f9e 100644 --- a/c/src/core/util.h +++ b/c/src/core/util.h @@ -58,6 +58,44 @@ static inline pn_bytes_t pn_string_bytes(pn_string_t *s) { return pn_bytes(pn_string_size(s), pn_string_get(s)); } +static inline void pni_write16(char *bytes, uint16_t value) +{ + bytes[0] = 0xFF & (value >> 8); + bytes[1] = 0xFF & (value ); +} + +static inline void pni_write32(char *bytes, uint32_t value) +{ + bytes[0] = 0xFF & (value >> 24); + bytes[1] = 0xFF & (value >> 16); + bytes[2] = 0xFF & (value >> 8); + bytes[3] = 0xFF & (value ); +} + +static inline uint16_t pni_read16(const char *bytes) +{ + uint16_t a = (uint8_t) bytes[0]; + uint16_t b = (uint8_t) bytes[1]; + uint16_t r = a << 8 | b; + return r; +} + +static inline uint32_t pni_read32(const char *bytes) +{ + uint32_t a = pni_read16(bytes); + uint32_t b = pni_read16(&bytes[2]); + uint32_t r = a << 16 | b; + return r; +} + +static inline uint64_t pni_read64(const char *bytes) +{ + uint64_t a = pni_read32(bytes); + uint64_t b = pni_read32(&bytes[4]); + uint64_t r = a << 32 | b; + return r; +} + /* Create a literal bytes value, e.g. PN_BYTES_LITERAL(foo) == pn_bytes(3, "foo") */ #define PN_BYTES_LITERAL(X) (pn_bytes(sizeof(#X)-1, #X)) diff --git a/c/src/core/value_dump.c b/c/src/core/value_dump.c new file mode 100644 index 0000000..05b39fc --- /dev/null +++ b/c/src/core/value_dump.c @@ -0,0 +1,600 @@ +/* + * + * 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 "value_dump.h" + +#include "encodings.h" +#include "consumers.h" +#include "framing.h" +#include "protocol.h" +#include "util.h" + +#include "proton/object.h" +#include "proton/types.h" + +#include <ctype.h> +#include <inttypes.h> +#include <stdint.h> + +static inline pn_bytes_t pn_bytes_advance(pn_bytes_t bytes, size_t size) { + return (pn_bytes_t) {.size=bytes.size-size,.start=bytes.start+size}; +} + +// This is only used in places where a described value is not wanted/allowed +// So we interpret type to mean the 'base' type of the described value in this case +static inline void pni_frame_get_type_value2(pni_consumer_t* consumer, uint8_t* type, pn_bytes_t* value) { + if (!pni_consumer_readf8(consumer, type)) goto error; + if (*type==PNE_DESCRIPTOR) { + // Skip over descriptor potentially recursively + uint8_t dtype; + pn_bytes_t dvalue; + pni_frame_get_type_value2(consumer, &dtype, &dvalue); + if (!pni_consumer_readf8(consumer, type)) goto error; + } + if (!pni_consumer_read_value_not_described(consumer, *type, value)) goto error; + return; + +error: + *value = (pn_bytes_t){0,0}; +} + +static inline size_t pni_frame_get_type_value(pn_bytes_t bytes, uint8_t* type, pn_bytes_t* value) { + pni_consumer_t consumer = make_consumer_from_bytes(bytes); + pni_frame_get_type_value2(&consumer, type, value); + return consumer.position; +} + +static inline size_t pni_frame_read_value_not_described(pn_bytes_t bytes, uint8_t type, pn_bytes_t* value) { + pni_consumer_t consumer = make_consumer_from_bytes(bytes); + if (!pni_consumer_read_value_not_described(&consumer, type, value)) { + *value = (pn_bytes_t){0,0}; + }; + return consumer.position; +} + +static inline bool type_isfixedsize(uint8_t type) { + return type < 0xa0; +} + +static inline bool type_is8bitsize(uint8_t type) { + uint8_t subcategory = type >> 4; + return subcategory==0xA || subcategory==0xC || subcategory==0xE; +} + +static inline bool type_isspecial(uint8_t type) { + return (type >> 4) == 0x4; +} + +static inline bool type_issimpleint(uint8_t type) { + uint8_t subcategory = type >> 4; + return (subcategory==0x5 && type<=0x55) || ((subcategory>0x5 && subcategory<=0x8) && (type & 0xe) == 0); +} + +static inline bool type_isunsigned_ifsimpleint(uint8_t type) { + uint8_t subtype = type & 0xf; + return (subtype==0 || subtype==2 || subtype==3); +} + +static inline bool type_isulong(uint8_t type) { + return type==PNE_ULONG0 || type==PNE_SMALLULONG || type==PNE_ULONG0; +} + +static inline bool type_iscompund(uint8_t type) { + return type >= 0xc0; +} + +static inline bool type_islist_notspecial(uint8_t type) { + return type==PNE_LIST8 || type==PNE_LIST32; +} + +void pn_value_dump_special(uint8_t type, pn_string_t *output) { + switch (type) { + case PNE_NULL: + pn_string_addf(output, "null"); + break; + case PNE_TRUE: + pn_string_addf(output, "true"); + break; + case PNE_FALSE: + pn_string_addf(output, "false"); + break; + case PNE_UINT0: + pn_string_addf(output, "0x0"); + break; + case PNE_ULONG0: + pn_string_addf(output, "0x0"); + break; + case PNE_LIST0: + pn_string_addf(output, "[]"); + break; + default: + pn_string_addf(output, "!!<unknown>"); + break; + } +} + +void pn_value_dump_descriptor_ulong(uint8_t type, pn_bytes_t value, pn_string_t* output, uint64_t* dcode) { + uint64_t ulong; + switch (type) { + case PNE_ULONG0: + ulong = 0; + break; + case PNE_SMALLULONG: + ulong = *value.start; + break; + case PNE_ULONG: + ulong = pni_read64(value.start); + break; + default: + // If we get a different descriptor type - huh + pn_string_addf(output, "!!<not-a-ulong>"); + return; + } + *dcode = ulong; + + // Check if we have a name for this descriptor + if (ulong>=FIELD_MIN && ulong<=FIELD_MAX) { + uint8_t name_index = FIELDS[ulong-FIELD_MIN].name_index; + if (name_index!=0) { + pn_string_addf(output, "%s(%" PRIu64 ") ", + (const char *)FIELD_STRINGPOOL.STRING0+FIELD_NAME[name_index], + ulong); + return; + } + } + + pn_string_addf(output, "%" PRIu64 " ", ulong); + return; +} + +void pn_value_dump_scalar(uint8_t type, pn_bytes_t value, pn_string_t *output){ + if (type_isfixedsize(type)) { + if (type_isspecial(type)) { + pn_value_dump_special(type, output); + } else if (type_issimpleint(type)) { + // Read bits into unsigned sign extended + uint64_t uint; + uint64_t mask; + switch (value.size) { + case 1: + uint = (int8_t)*value.start; + mask = 0xff; + break; + case 2: + uint = (int16_t)pni_read16(value.start); + mask = 0xffff; + break; + case 4: + uint = (int32_t)pni_read32(value.start); + mask = 0xffffffff; + break; + case 8: + uint = pni_read64(value.start); + mask = 0xffffffffffffffff; + break; + case 0: + // We'll get here if there aren't enough bytes left for the value + pn_string_addf(output, "!!"); + return; + default: + // It has to be length 1,2,4 or 8! + pn_string_addf(output, "!!<WeirdLengthHappened(%zu)>", value.size); + return; + } + if (type_isunsigned_ifsimpleint(type)) { + // mask high sign extended bits if unsigned + uint = uint & mask; + pn_string_addf(output, "0x%" PRIx64, uint); + } else { + int64_t i = (int64_t)uint; + pn_string_addf(output, "%" PRIi64, i); + } + } else { + // Check if we didn't have enough bytes for the value + if (value.size==0) { + pn_string_addf(output, "!!"); + return; + } + switch (type) { + case PNE_BOOLEAN: + pn_string_addf(output, *value.start ? "true" : "false"); + break; + case PNE_FLOAT: { + union {uint32_t i; float f;} conv; + conv.i = pni_read32(value.start); + pn_string_addf(output, "%g", conv.f); + break; + } + case PNE_DOUBLE: { + union {uint64_t i; double f;} conv; + conv.i = pni_read64(value.start); + pn_string_addf(output, "%g", conv.f); + break; + } + case PNE_UTF32: + break; + case PNE_MS64: { + int64_t timestamp = pni_read64(value.start); + pn_string_addf(output, "%" PRIi64, timestamp); + break; + } + case PNE_DECIMAL32: + pn_string_addf(output, "D32(%04" PRIx32 ")", pni_read32(value.start)); + break; + case PNE_DECIMAL64: + pn_string_addf(output, "D64(%08" PRIx64 ")", pni_read64(value.start)); + break; + case PNE_DECIMAL128: + pn_string_addf( + output, + "D128(%08" PRIx64 "%08" PRIx64 ")", + pni_read64(value.start), pni_read64(&value.start[8])); + break; + case PNE_UUID: + pn_string_addf( + output, + "UUID(%02hhx%02hhx%02hhx%02hhx-" + "%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-" + "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx)", + value.start[0], value.start[1], value.start[2],value.start[3], + value.start[4], value.start[5], + value.start[6], value.start[7], + value.start[8], value.start[9], + value.start[10], value.start[11], value.start[12], value.start[13], value.start[14], value.start[15]); + break; + default: + pn_string_addf(output, "!!<UnknownType<0x%02hhx>(", type); + for (size_t i=0; i<value.size; i++) { + pn_string_addf(output, "%.2x", value.start[i]); + } + pn_string_addf(output, ")>"); + } + } + } else { + // given we're variable size scalar and we've already calculated the length we can simply switch on subtype + // which can only be 0 (binary), 1 (utf8 string) or 3 (symbol) [I wonder what happened to subtype 2!] + const char* prefix = ""; + const char* suffix = ""; + switch (type & 0xf) { + case 0: + prefix = "b\""; + suffix = "\""; + break; + case 1: + prefix = "\""; + suffix = "\""; + break; + case 3: { + bool quote = false; + if (!isalpha(value.start[0])) { + quote = true; + } else { + for (size_t i = 1; i < value.size; i++) { + if ( !isalnum(value.start[i]) && value.start[i]!='-' ) { + quote = true; + break; + } + } + } + if (quote) { + prefix = ":\""; + suffix = "\""; + } else { + prefix = ":"; + suffix = ""; + } + break; + } + default: + prefix = "<?<"; + suffix = ">?>"; + } + pn_string_addf(output, "%s", prefix); + pn_quote(output, value.start, value.size); + pn_string_addf(output, "%s", suffix); + } +} + +static inline uint32_t consume_count(uint8_t type, pn_bytes_t *value) { + uint32_t count; + if (type_is8bitsize(type)) { + count = value->start[0]; + *value = pn_bytes_advance(*value, 1); + } else { + count = pni_read32(value->start); + *value = pn_bytes_advance(*value, 4); + } + return count; +} + +void pn_value_dump_nondescribed_value(uint8_t type, pn_bytes_t value, pn_string_t *output); + +void pn_value_dump_list(uint32_t count, pn_bytes_t value, pn_string_t *output) { + uint32_t elements = 0; + pn_string_addf(output, "["); + while (value.size) { + elements++; + size_t size = pn_value_dump(value, output); + value = pn_bytes_advance(value, size); + if (value.size) { + pn_string_addf(output, ", "); + } + } + pn_string_addf(output, "]"); + if (elements!=count) { + pn_string_addf(output, "<%" PRIu32 "!=%" PRIu32 ">", elements, count); + } +} + +void pn_value_dump_described_list(uint32_t count, pn_bytes_t value, uint64_t dcode, pn_string_t *output) { + uint32_t elements = 0; + pn_string_addf(output, "["); + while (value.size) { + const pn_fields_t *fields = &FIELDS[dcode-FIELD_MIN]; + if (elements < fields->field_count) { + pn_string_addf(output, "%s=", + (const char*)FIELD_STRINGPOOL.STRING0+FIELD_FIELDS[fields->first_field_index+elements]); + } + elements++; + size_t size = pn_value_dump(value, output); + value = pn_bytes_advance(value, size); + if (value.size) { + pn_string_addf(output, ", "); + } + } + pn_string_addf(output, "]"); + if (elements!=count) { + pn_string_addf(output, "<%" PRIu32 "!=%" PRIu32 ">", elements, count); + } +} + +void pn_value_dump_map(uint32_t count, pn_bytes_t value, pn_string_t *output) { + uint32_t elements = 0; + pn_string_addf(output, "{"); + while (value.size) { + elements++; + size_t size = pn_value_dump(value, output); + value = pn_bytes_advance(value, size); + if (value.size) { + if (elements & 1) { + pn_string_addf(output, "="); + } else { + pn_string_addf(output, ", "); + } + } + } + pn_string_addf(output, "}"); + if (elements!=count) { + pn_string_addf(output, "<%" PRIu32 "!=%" PRIu32 ">", elements, count); + } +} + +static inline const char* pni_type_name(uint8_t type) { + switch (type) { + case PNE_NULL: return "null"; + case PNE_TRUE: return "true"; + case PNE_FALSE: return "false"; + case PNE_BOOLEAN: return "bool"; + case PNE_BYTE: return "byte"; + case PNE_UBYTE: return "ubyte"; + case PNE_SHORT: return "short"; + case PNE_USHORT: return "ushort"; + case PNE_INT: + case PNE_SMALLINT: return "int"; + case PNE_UINT0: + case PNE_UINT: + case PNE_SMALLUINT: return "uint"; + case PNE_LONG: + case PNE_SMALLLONG: return "long"; + case PNE_ULONG0: + case PNE_ULONG: + case PNE_SMALLULONG: return "ulong"; + case PNE_FLOAT: return "float"; + case PNE_DOUBLE: return "double"; + case PNE_DECIMAL32: return "decimal32"; + case PNE_DECIMAL64: return "decimal64"; + case PNE_DECIMAL128: return "decimal128"; + case PNE_UUID: return "uuid"; + case PNE_MS64: return "timestamp"; + case PNE_SYM8: + case PNE_SYM32: return "symbol"; + case PNE_STR8_UTF8: + case PNE_STR32_UTF8: return "string"; + case PNE_VBIN8: + case PNE_VBIN32: return "binary"; + case PNE_LIST0: + case PNE_LIST8: + case PNE_LIST32: return "list"; + case PNE_MAP8: + case PNE_MAP32: return "map"; + default: + break; + } + return NULL; +} + +void pn_value_dump_array(uint32_t count, pn_bytes_t value, pn_string_t *output) { + // Read base type only (ignoring descriptor) and first value + uint8_t type = 0; + pn_bytes_t evalue; + if (count==0) { + type = *value.start; + } else { + size_t size = pni_frame_get_type_value(value, &type, &evalue); + value = pn_bytes_advance(value, size); + } + if (type==0){ + // Type isn't allowed to be 0 at present because we dump described values as the base type anyway + pn_string_addf(output, "@<!!>[]"); + return; + } + const char* type_name = pni_type_name(type); + if (type_name) { + pn_string_addf(output, "@<%s>[", type_name); + } else { + pn_string_addf(output, "@<%02hhx>[", type); + } + if (count==0) { + pn_string_addf(output, "]"); + return; + } + + pn_value_dump_nondescribed_value(type, evalue, output); + if (type_isspecial(type)) { + if (count>1) { + pn_string_addf(output, ", ...(%d more)]", count-1); + } else { + pn_string_addf(output, "]"); + } + return; + } + uint32_t elements = 1; + while (value.size) { + pn_string_addf(output, ", "); + elements++; + size_t size = pni_frame_read_value_not_described(value, type, &evalue); + pn_value_dump_nondescribed_value(type, evalue, output); + if (size <= value.size) { + value = pn_bytes_advance(value, size); + } else { + pn_string_addf(output, "<error: %zd > %zd>", size, value.size); + value.size = 0; + } + } + pn_string_addf(output, "]"); + if (elements!=count) { + pn_string_addf(output, "<%" PRIu32 "!=%" PRIu32 ">", elements, count); + } +} + +void pn_value_dump_nondescribed_value(uint8_t type, pn_bytes_t value, pn_string_t *output){ + if (!type_iscompund(type)) { + // The one exception to 'scalar' is LIST0 which is encoded as a special type + pn_value_dump_scalar(type, value, output); + return; + } + + if (value.size==0) { + switch (type) { + case PNE_LIST8: + case PNE_LIST32: + pn_string_addf(output, "[!!]"); + break; + case PNE_MAP8: + case PNE_MAP32: + pn_string_addf(output, "{!!}"); + break; + case PNE_ARRAY8: + case PNE_ARRAY32: + pn_string_addf(output, "@<>[!!]"); + break; + } + return; + } + + // Get count + uint32_t count = consume_count(type, &value); + + switch (type) { + case PNE_LIST8: + case PNE_LIST32: + pn_value_dump_list(count, value, output); + break; + case PNE_MAP8: + case PNE_MAP32: + pn_value_dump_map(count, value, output); + break; + case PNE_ARRAY8: + case PNE_ARRAY32: + pn_value_dump_array(count, value, output); + break; + } +} + +size_t pn_value_dump_described(pn_bytes_t bytes, uint64_t dcode, pn_string_t *output) { + uint8_t type; + pn_bytes_t value; + size_t size = pni_frame_get_type_value(bytes, &type, &value); + if (size==0) { + pn_string_addf(output, "!!"); + return 0; + } + + // The only described type handled differently is list + if (type_islist_notspecial(type) && dcode) { + if (value.size==0) { + pn_string_addf(output, "[!!]"); + return size; + } + uint32_t count = consume_count(type, &value); + pn_value_dump_described_list(count, value, dcode, output); + } else { + pn_value_dump_nondescribed_value(type, value, output); + } + return size; +} + +size_t pn_value_dump_nondescribed(pn_bytes_t bytes, pn_string_t *output) { + uint8_t type; + pn_bytes_t value; + size_t size = pni_frame_get_type_value(bytes, &type, &value); + if (size==0) { + pn_string_addf(output, "!!"); + return 0; + } + + pn_value_dump_nondescribed_value(type, value, output); + return size; +} + +size_t pn_value_dump(pn_bytes_t frame, pn_string_t *output) +{ + if (frame.size==0) { + return 0; + } + uint8_t type = frame.start[0]; + // Check for described type first + if (type==PNE_DESCRIPTOR) { + pn_string_addf(output, "@"); + frame = pn_bytes_advance(frame, 1); + size_t fsize = 1; + uint8_t type; + pn_bytes_t value; + size_t size = pni_frame_get_type_value(frame, &type, &value); + frame = pn_bytes_advance(frame, size); + fsize += size; + if (value.size==0) { + pn_string_addf(output, "!!"); + return fsize; + } + if (type_isulong(type)) { + uint64_t dcode; + pn_value_dump_descriptor_ulong(type, value, output, &dcode); + fsize += pn_value_dump_described(frame, dcode, output); + } else { + pn_value_dump_nondescribed_value(type, value, output); + fsize += pn_value_dump_nondescribed(frame, output); + } + return fsize; + } else { + return pn_value_dump_nondescribed(frame, output); + } +} diff --git a/c/src/core/framing.h b/c/src/core/value_dump.h similarity index 56% copy from c/src/core/framing.h copy to c/src/core/value_dump.h index 92c1f7d..b2987aa 100644 --- a/c/src/core/framing.h +++ b/c/src/core/value_dump.h @@ -1,5 +1,5 @@ -#ifndef PROTON_FRAMING_H -#define PROTON_FRAMING_H 1 +#ifndef PROTON_VALUE_DUMP_H +#define PROTON_VALUE_DUMP_H 1 /* * @@ -22,26 +22,9 @@ * */ -#include "buffer.h" +#include "proton/types.h" +#include "proton/object.h" -#include <proton/import_export.h> -#include <proton/type_compat.h> -#include <proton/error.h> +PN_EXTERN size_t pn_value_dump(pn_bytes_t frame, pn_string_t *output); -#define AMQP_HEADER_SIZE (8) -#define AMQP_MIN_MAX_FRAME_SIZE ((uint32_t)512) // minimum allowable max-frame -#define AMQP_MAX_WINDOW_SIZE (2147483647) - -typedef struct { - uint8_t type; - uint16_t channel; - size_t ex_size; - const char *extended; - size_t size; - const char *payload; -} pn_frame_t; - -ssize_t pn_read_frame(pn_frame_t *frame, const char *bytes, size_t available, uint32_t max); -size_t pn_write_frame(pn_buffer_t* buffer, pn_frame_t frame); - -#endif /* framing.h */ +#endif // PROTON_VALUE_DUMP_H --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
