| /* |
| * |
| * 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 "fixed_string.h" |
| #include "framing.h" |
| #include "protocol.h" |
| #include "util.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_ULONG; |
| } |
| |
| 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_fixed_string_t *output) { |
| switch (type) { |
| case PNE_NULL: |
| pn_fixed_string_addf(output, "null"); |
| break; |
| case PNE_TRUE: |
| pn_fixed_string_addf(output, "true"); |
| break; |
| case PNE_FALSE: |
| pn_fixed_string_addf(output, "false"); |
| break; |
| case PNE_UINT0: |
| pn_fixed_string_addf(output, "0x0"); |
| break; |
| case PNE_ULONG0: |
| pn_fixed_string_addf(output, "0x0"); |
| break; |
| case PNE_LIST0: |
| pn_fixed_string_addf(output, "[]"); |
| break; |
| default: |
| pn_fixed_string_addf(output, "!!<unknown>"); |
| break; |
| } |
| } |
| |
| void pn_value_dump_descriptor_ulong(uint8_t type, pn_bytes_t value, pn_fixed_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_fixed_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_fixed_string_addf(output, "%s(%" PRIu64 ") ", |
| (const char *)FIELD_STRINGPOOL.STRING0+FIELD_NAME[name_index], |
| ulong); |
| return; |
| } |
| } |
| |
| pn_fixed_string_addf(output, "%" PRIu64 " ", ulong); |
| return; |
| } |
| |
| void pn_value_dump_scalar(uint8_t type, pn_bytes_t value, pn_fixed_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_fixed_string_addf(output, "!!"); |
| return; |
| default: |
| // It has to be length 1,2,4 or 8! |
| pn_fixed_string_addf(output, "!!<WeirdLengthHappened(%zu)>", value.size); |
| return; |
| } |
| if (type_isunsigned_ifsimpleint(type)) { |
| // mask high sign extended bits if unsigned |
| uint = uint & mask; |
| pn_fixed_string_addf(output, "0x%" PRIx64, uint); |
| } else { |
| int64_t i = (int64_t)uint; |
| pn_fixed_string_addf(output, "%" PRIi64, i); |
| } |
| } else { |
| // Check if we didn't have enough bytes for the value |
| if (value.size==0) { |
| pn_fixed_string_addf(output, "!!"); |
| return; |
| } |
| switch (type) { |
| case PNE_BOOLEAN: |
| pn_fixed_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_fixed_string_addf(output, "%g", conv.f); |
| break; |
| } |
| case PNE_DOUBLE: { |
| union {uint64_t i; double f;} conv; |
| conv.i = pni_read64(value.start); |
| pn_fixed_string_addf(output, "%g", conv.f); |
| break; |
| } |
| case PNE_UTF32: |
| break; |
| case PNE_MS64: { |
| int64_t timestamp = pni_read64(value.start); |
| pn_fixed_string_addf(output, "%" PRIi64, timestamp); |
| break; |
| } |
| case PNE_DECIMAL32: |
| pn_fixed_string_addf(output, "D32(%04" PRIx32 ")", pni_read32(value.start)); |
| break; |
| case PNE_DECIMAL64: |
| pn_fixed_string_addf(output, "D64(%08" PRIx64 ")", pni_read64(value.start)); |
| break; |
| case PNE_DECIMAL128: |
| pn_fixed_string_addf( |
| output, |
| "D128(%08" PRIx64 "%08" PRIx64 ")", |
| pni_read64(value.start), pni_read64(&value.start[8])); |
| break; |
| case PNE_UUID: |
| pn_fixed_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_fixed_string_addf(output, "!!<UnknownType<0x%02hhx>(", type); |
| for (size_t i=0; i<value.size; i++) { |
| pn_fixed_string_addf(output, "%.2x", value.start[i]); |
| } |
| pn_fixed_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 (value.size==0 || !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_fixed_string_addf(output, "%s", prefix); |
| pn_fixed_string_quote(output, value.start, value.size); |
| pn_fixed_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_fixed_string_t *output); |
| |
| void pn_value_dump_list(uint32_t count, pn_bytes_t value, pn_fixed_string_t *output) { |
| uint32_t elements = 0; |
| pn_fixed_string_addf(output, "["); |
| while (value.size) { |
| elements++; |
| size_t size = pni_value_dump(value, output); |
| value = pn_bytes_advance(value, size); |
| if (value.size) { |
| pn_fixed_string_addf(output, ", "); |
| } |
| } |
| pn_fixed_string_addf(output, "]"); |
| if (elements!=count) { |
| pn_fixed_string_addf(output, "<%" PRIu32 "!=%" PRIu32 ">", elements, count); |
| } |
| } |
| |
| void pn_value_dump_described_list(uint32_t count, pn_bytes_t value, uint64_t dcode, pn_fixed_string_t *output) { |
| uint32_t elements = 0; |
| uint8_t field_count; |
| uint8_t first_field_index; |
| bool known_descriptor = dcode>=FIELD_MIN && dcode<=FIELD_MAX; |
| if (known_descriptor) { |
| const pn_fields_t *fields = &FIELDS[dcode-FIELD_MIN]; |
| field_count = fields->field_count; |
| first_field_index = fields->first_field_index; |
| } |
| bool output_element = false; |
| pn_fixed_string_addf(output, "["); |
| while (value.size) { |
| uint8_t type = value.start[0]; |
| if (type==PNE_NULL) { |
| value = pn_bytes_advance(value, 1); |
| } else { |
| if (output_element) { |
| pn_fixed_string_addf(output, ", "); |
| } |
| if (known_descriptor) { |
| if (elements < field_count) { |
| pn_fixed_string_addf(output, "%s=", |
| (const char*)FIELD_STRINGPOOL.STRING0+FIELD_FIELDS[first_field_index+elements]); |
| } |
| } |
| size_t size = pni_value_dump(value, output); |
| value = pn_bytes_advance(value, size); |
| output_element = true; |
| } |
| elements++; |
| } |
| pn_fixed_string_addf(output, "]"); |
| if (elements!=count) { |
| pn_fixed_string_addf(output, "<%" PRIu32 "!=%" PRIu32 ">", elements, count); |
| } |
| } |
| |
| void pn_value_dump_map(uint32_t count, pn_bytes_t value, pn_fixed_string_t *output) { |
| uint32_t elements = 0; |
| pn_fixed_string_addf(output, "{"); |
| while (value.size) { |
| elements++; |
| size_t size = pni_value_dump(value, output); |
| value = pn_bytes_advance(value, size); |
| if (value.size) { |
| if (elements & 1) { |
| pn_fixed_string_addf(output, "="); |
| } else { |
| pn_fixed_string_addf(output, ", "); |
| } |
| } |
| } |
| pn_fixed_string_addf(output, "}"); |
| if (elements!=count) { |
| pn_fixed_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_fixed_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_fixed_string_addf(output, "@<!!>[]"); |
| return; |
| } |
| const char* type_name = pni_type_name(type); |
| if (type_name) { |
| pn_fixed_string_addf(output, "@<%s>[", type_name); |
| } else { |
| pn_fixed_string_addf(output, "@<%02hhx>[", type); |
| } |
| if (count==0) { |
| pn_fixed_string_addf(output, "]"); |
| return; |
| } |
| |
| pn_value_dump_nondescribed_value(type, evalue, output); |
| if (type_isspecial(type)) { |
| if (count>1) { |
| pn_fixed_string_addf(output, ", ...(%u more)]", count-1); |
| } else { |
| pn_fixed_string_addf(output, "]"); |
| } |
| return; |
| } |
| uint32_t elements = 1; |
| while (value.size) { |
| pn_fixed_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_fixed_string_addf(output, "<error: %zd > %zd>", size, value.size); |
| value.size = 0; |
| } |
| } |
| pn_fixed_string_addf(output, "]"); |
| if (elements!=count) { |
| pn_fixed_string_addf(output, "<%" PRIu32 "!=%" PRIu32 ">", elements, count); |
| } |
| } |
| |
| void pn_value_dump_nondescribed_value(uint8_t type, pn_bytes_t value, pn_fixed_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_fixed_string_addf(output, "[!!]"); |
| break; |
| case PNE_MAP8: |
| case PNE_MAP32: |
| pn_fixed_string_addf(output, "{!!}"); |
| break; |
| case PNE_ARRAY8: |
| case PNE_ARRAY32: |
| pn_fixed_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_fixed_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_fixed_string_addf(output, "!!"); |
| return 0; |
| } |
| |
| // The only described type handled differently is list |
| if (type_islist_notspecial(type) && dcode) { |
| if (value.size==0) { |
| pn_fixed_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_fixed_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_fixed_string_addf(output, "!!"); |
| return 0; |
| } |
| |
| pn_value_dump_nondescribed_value(type, value, output); |
| return size; |
| } |
| |
| size_t pni_value_dump(pn_bytes_t frame, pn_fixed_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_fixed_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_fixed_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); |
| pn_fixed_string_addf(output, " "); |
| fsize += pn_value_dump_nondescribed(frame, output); |
| } |
| return fsize; |
| } else { |
| return pn_value_dump_nondescribed(frame, output); |
| } |
| } |
| |
| size_t pn_value_dump(pn_bytes_t frame, char *bytes, uint32_t size) |
| { |
| pn_fixed_string_t output = pn_fixed_string(bytes, size); |
| size_t fsize = pni_value_dump(frame, &output); |
| pn_fixed_string_terminate(&output); |
| return fsize; |
| } |