| /* |
| * |
| * 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 <proton/error.h> |
| #include <proton/object.h> |
| #include <proton/codec.h> |
| #include "encodings.h" |
| #include "decoder.h" |
| |
| #include <string.h> |
| |
| static inline pn_error_t *pni_decoder_error(pn_decoder_t *decoder) |
| { |
| if (!decoder->error) decoder->error = pn_error(); |
| return decoder->error; |
| } |
| |
| void pn_decoder_initialize(pn_decoder_t *decoder) |
| { |
| decoder->input = NULL; |
| decoder->size = 0; |
| decoder->position = NULL; |
| decoder->error = NULL; |
| } |
| |
| void pn_decoder_finalize(pn_decoder_t *decoder) |
| { |
| pn_error_free(decoder->error); |
| } |
| |
| static inline uint8_t pn_decoder_readf8(pn_decoder_t *decoder) |
| { |
| uint8_t r = decoder->position[0]; |
| decoder->position++; |
| return r; |
| } |
| |
| static inline uint16_t pn_decoder_readf16(pn_decoder_t *decoder) |
| { |
| uint16_t a = (uint8_t) decoder->position[0]; |
| uint16_t b = (uint8_t) decoder->position[1]; |
| uint16_t r = a << 8 |
| | b; |
| decoder->position += 2; |
| return r; |
| } |
| |
| static inline uint32_t pn_decoder_readf32(pn_decoder_t *decoder) |
| { |
| uint32_t a = (uint8_t) decoder->position[0]; |
| uint32_t b = (uint8_t) decoder->position[1]; |
| uint32_t c = (uint8_t) decoder->position[2]; |
| uint32_t d = (uint8_t) decoder->position[3]; |
| uint32_t r = a << 24 |
| | b << 16 |
| | c << 8 |
| | d; |
| decoder->position += 4; |
| return r; |
| } |
| |
| static inline uint64_t pn_decoder_readf64(pn_decoder_t *decoder) |
| { |
| uint64_t a = pn_decoder_readf32(decoder); |
| uint64_t b = pn_decoder_readf32(decoder); |
| return a << 32 | b; |
| } |
| |
| static inline void pn_decoder_readf128(pn_decoder_t *decoder, void *dst) |
| { |
| memmove(dst, decoder->position, 16); |
| decoder->position += 16; |
| } |
| |
| static inline size_t pn_decoder_remaining(pn_decoder_t *decoder) |
| { |
| return decoder->input + decoder->size - decoder->position; |
| } |
| |
| typedef union { |
| uint32_t i; |
| uint32_t a[2]; |
| uint64_t l; |
| float f; |
| double d; |
| } conv_t; |
| |
| static inline pn_type_t pn_code2type(uint8_t code) |
| { |
| switch (code) |
| { |
| case PNE_DESCRIPTOR: |
| return (pn_type_t) PN_ARG_ERR; |
| case PNE_NULL: |
| return PN_NULL; |
| case PNE_TRUE: |
| case PNE_FALSE: |
| case PNE_BOOLEAN: |
| return PN_BOOL; |
| case PNE_UBYTE: |
| return PN_UBYTE; |
| case PNE_BYTE: |
| return PN_BYTE; |
| case PNE_USHORT: |
| return PN_USHORT; |
| case PNE_SHORT: |
| return PN_SHORT; |
| case PNE_UINT0: |
| case PNE_SMALLUINT: |
| case PNE_UINT: |
| return PN_UINT; |
| case PNE_SMALLINT: |
| case PNE_INT: |
| return PN_INT; |
| case PNE_UTF32: |
| return PN_CHAR; |
| case PNE_FLOAT: |
| return PN_FLOAT; |
| case PNE_LONG: |
| case PNE_SMALLLONG: |
| return PN_LONG; |
| case PNE_MS64: |
| return PN_TIMESTAMP; |
| case PNE_DOUBLE: |
| return PN_DOUBLE; |
| case PNE_DECIMAL32: |
| return PN_DECIMAL32; |
| case PNE_DECIMAL64: |
| return PN_DECIMAL64; |
| case PNE_DECIMAL128: |
| return PN_DECIMAL128; |
| case PNE_UUID: |
| return PN_UUID; |
| case PNE_ULONG0: |
| case PNE_SMALLULONG: |
| case PNE_ULONG: |
| return PN_ULONG; |
| case PNE_VBIN8: |
| case PNE_VBIN32: |
| return PN_BINARY; |
| case PNE_STR8_UTF8: |
| case PNE_STR32_UTF8: |
| return PN_STRING; |
| case PNE_SYM8: |
| case PNE_SYM32: |
| return PN_SYMBOL; |
| case PNE_LIST0: |
| case PNE_LIST8: |
| case PNE_LIST32: |
| return PN_LIST; |
| case PNE_ARRAY8: |
| case PNE_ARRAY32: |
| return PN_ARRAY; |
| case PNE_MAP8: |
| case PNE_MAP32: |
| return PN_MAP; |
| default: |
| return (pn_type_t) PN_ARG_ERR; |
| } |
| } |
| |
| static int pni_decoder_decode_type(pn_decoder_t *decoder, pn_data_t *data, uint8_t *code); |
| static int pni_decoder_single_described(pn_decoder_t *decoder, pn_data_t *data); |
| static int pni_decoder_single(pn_decoder_t *decoder, pn_data_t *data); |
| void pni_data_set_array_type(pn_data_t *data, pn_type_t type); |
| |
| static int pni_decoder_decode_value(pn_decoder_t *decoder, pn_data_t *data, uint8_t code) |
| { |
| int err; |
| conv_t conv; |
| pn_decimal128_t dec128; |
| pn_uuid_t uuid; |
| size_t size; |
| size_t count; |
| |
| switch (code) |
| { |
| case PNE_NULL: |
| err = pn_data_put_null(data); |
| break; |
| case PNE_TRUE: |
| err = pn_data_put_bool(data, true); |
| break; |
| case PNE_FALSE: |
| err = pn_data_put_bool(data, false); |
| break; |
| case PNE_BOOLEAN: |
| if (!pn_decoder_remaining(decoder)) return PN_UNDERFLOW; |
| err = pn_data_put_bool(data, pn_decoder_readf8(decoder) != 0); |
| break; |
| case PNE_UBYTE: |
| if (!pn_decoder_remaining(decoder)) return PN_UNDERFLOW; |
| err = pn_data_put_ubyte(data, pn_decoder_readf8(decoder)); |
| break; |
| case PNE_BYTE: |
| if (!pn_decoder_remaining(decoder)) return PN_UNDERFLOW; |
| err = pn_data_put_byte(data, pn_decoder_readf8(decoder)); |
| break; |
| case PNE_USHORT: |
| if (pn_decoder_remaining(decoder) < 2) return PN_UNDERFLOW; |
| err = pn_data_put_ushort(data, pn_decoder_readf16(decoder)); |
| break; |
| case PNE_SHORT: |
| if (pn_decoder_remaining(decoder) < 2) return PN_UNDERFLOW; |
| err = pn_data_put_short(data, pn_decoder_readf16(decoder)); |
| break; |
| case PNE_UINT: |
| if (pn_decoder_remaining(decoder) < 4) return PN_UNDERFLOW; |
| err = pn_data_put_uint(data, pn_decoder_readf32(decoder)); |
| break; |
| case PNE_UINT0: |
| err = pn_data_put_uint(data, 0); |
| break; |
| case PNE_SMALLUINT: |
| if (!pn_decoder_remaining(decoder)) return PN_UNDERFLOW; |
| err = pn_data_put_uint(data, pn_decoder_readf8(decoder)); |
| break; |
| case PNE_SMALLINT: |
| if (!pn_decoder_remaining(decoder)) return PN_UNDERFLOW; |
| err = pn_data_put_int(data, (int8_t)pn_decoder_readf8(decoder)); |
| break; |
| case PNE_INT: |
| if (pn_decoder_remaining(decoder) < 4) return PN_UNDERFLOW; |
| err = pn_data_put_int(data, pn_decoder_readf32(decoder)); |
| break; |
| case PNE_UTF32: |
| if (pn_decoder_remaining(decoder) < 4) return PN_UNDERFLOW; |
| err = pn_data_put_char(data, pn_decoder_readf32(decoder)); |
| break; |
| case PNE_FLOAT: |
| if (pn_decoder_remaining(decoder) < 4) return PN_UNDERFLOW; |
| // XXX: this assumes the platform uses IEEE floats |
| conv.i = pn_decoder_readf32(decoder); |
| err = pn_data_put_float(data, conv.f); |
| break; |
| case PNE_DECIMAL32: |
| if (pn_decoder_remaining(decoder) < 4) return PN_UNDERFLOW; |
| err = pn_data_put_decimal32(data, pn_decoder_readf32(decoder)); |
| break; |
| case PNE_ULONG: |
| if (pn_decoder_remaining(decoder) < 8) return PN_UNDERFLOW; |
| err = pn_data_put_ulong(data, pn_decoder_readf64(decoder)); |
| break; |
| case PNE_LONG: |
| if (pn_decoder_remaining(decoder) < 8) return PN_UNDERFLOW; |
| err = pn_data_put_long(data, pn_decoder_readf64(decoder)); |
| break; |
| case PNE_MS64: |
| if (pn_decoder_remaining(decoder) < 8) return PN_UNDERFLOW; |
| err = pn_data_put_timestamp(data, pn_decoder_readf64(decoder)); |
| break; |
| case PNE_DOUBLE: |
| // XXX: this assumes the platform uses IEEE floats |
| if (pn_decoder_remaining(decoder) < 8) return PN_UNDERFLOW; |
| conv.l = pn_decoder_readf64(decoder); |
| err = pn_data_put_double(data, conv.d); |
| break; |
| case PNE_DECIMAL64: |
| if (pn_decoder_remaining(decoder) < 8) return PN_UNDERFLOW; |
| err = pn_data_put_decimal64(data, pn_decoder_readf64(decoder)); |
| break; |
| case PNE_ULONG0: |
| err = pn_data_put_ulong(data, 0); |
| break; |
| case PNE_SMALLULONG: |
| if (!pn_decoder_remaining(decoder)) return PN_UNDERFLOW; |
| err = pn_data_put_ulong(data, pn_decoder_readf8(decoder)); |
| break; |
| case PNE_SMALLLONG: |
| if (!pn_decoder_remaining(decoder)) return PN_UNDERFLOW; |
| err = pn_data_put_long(data, (int8_t)pn_decoder_readf8(decoder)); |
| break; |
| case PNE_DECIMAL128: |
| if (pn_decoder_remaining(decoder) < 16) return PN_UNDERFLOW; |
| pn_decoder_readf128(decoder, &dec128); |
| err = pn_data_put_decimal128(data, dec128); |
| break; |
| case PNE_UUID: |
| if (pn_decoder_remaining(decoder) < 16) return PN_UNDERFLOW; |
| pn_decoder_readf128(decoder, &uuid); |
| err = pn_data_put_uuid(data, uuid); |
| break; |
| case PNE_VBIN8: |
| case PNE_STR8_UTF8: |
| case PNE_SYM8: |
| case PNE_VBIN32: |
| case PNE_STR32_UTF8: |
| case PNE_SYM32: |
| switch (code & 0xF0) |
| { |
| case 0xA0: |
| if (!pn_decoder_remaining(decoder)) return PN_UNDERFLOW; |
| size = pn_decoder_readf8(decoder); |
| break; |
| case 0xB0: |
| if (pn_decoder_remaining(decoder) < 4) return PN_UNDERFLOW; |
| size = pn_decoder_readf32(decoder); |
| break; |
| default: |
| return PN_ARG_ERR; |
| } |
| |
| if (pn_decoder_remaining(decoder) < size) return PN_UNDERFLOW; |
| |
| { |
| char *start = (char *) decoder->position; |
| pn_bytes_t bytes = {size, start}; |
| switch (code & 0x0F) |
| { |
| case 0x0: |
| err = pn_data_put_binary(data, bytes); |
| break; |
| case 0x1: |
| err = pn_data_put_string(data, bytes); |
| break; |
| case 0x3: |
| err = pn_data_put_symbol(data, bytes); |
| break; |
| default: |
| return PN_ARG_ERR; |
| } |
| } |
| |
| decoder->position += size; |
| break; |
| case PNE_LIST0: |
| err = pn_data_put_list(data); |
| break; |
| case PNE_ARRAY8: |
| case PNE_ARRAY32: |
| case PNE_LIST8: |
| case PNE_LIST32: |
| case PNE_MAP8: |
| case PNE_MAP32: { |
| size_t min_expected_size = 0; |
| switch (code) |
| { |
| case PNE_ARRAY8: |
| min_expected_size += 1; // Array has a constructor of at least 1 byte |
| case PNE_LIST8: |
| case PNE_MAP8: |
| min_expected_size += 1; // All these types have a count |
| if (pn_decoder_remaining(decoder) < min_expected_size+1) return PN_UNDERFLOW; |
| size = pn_decoder_readf8(decoder); |
| // size must be at least big enough for count or count+constructor |
| if (size < min_expected_size) return PN_ARG_ERR; |
| if (pn_decoder_remaining(decoder) < size) return PN_UNDERFLOW; |
| count = pn_decoder_readf8(decoder); |
| break; |
| case PNE_ARRAY32: |
| min_expected_size += 1; // Array has a constructor of at least 1 byte |
| case PNE_LIST32: |
| case PNE_MAP32: |
| min_expected_size += 4; // All these types have a count |
| if (pn_decoder_remaining(decoder) < min_expected_size+4) return PN_UNDERFLOW; |
| size = pn_decoder_readf32(decoder); |
| // size must be at least big enough for count or count+constructor |
| if (size < min_expected_size) return PN_ARG_ERR; |
| if (pn_decoder_remaining(decoder) < size) return PN_UNDERFLOW; |
| count = pn_decoder_readf32(decoder); |
| break; |
| default: |
| return PN_ARG_ERR; |
| } |
| |
| switch (code) |
| { |
| case PNE_ARRAY8: |
| case PNE_ARRAY32: |
| { |
| uint8_t next = *decoder->position; |
| bool described = (next == PNE_DESCRIPTOR); |
| err = pn_data_put_array(data, described, (pn_type_t) 0); |
| if (err) return err; |
| |
| pn_data_enter(data); |
| uint8_t acode; |
| int e = pni_decoder_decode_type(decoder, data, &acode); |
| if (e) return e; |
| pn_type_t type = pn_code2type(acode); |
| if ((int)type < 0) return (int)type; |
| for (size_t i = 0; i < count; i++) |
| { |
| e = pni_decoder_decode_value(decoder, data, acode); |
| if (e) return e; |
| } |
| pn_data_exit(data); |
| |
| pni_data_set_array_type(data, type); |
| } |
| return 0; |
| case PNE_LIST8: |
| case PNE_LIST32: |
| err = pn_data_put_list(data); |
| if (err) return err; |
| break; |
| case PNE_MAP8: |
| case PNE_MAP32: |
| err = pn_data_put_map(data); |
| if (err) return err; |
| break; |
| default: |
| return PN_ARG_ERR; |
| } |
| pn_data_enter(data); |
| for (size_t i = 0; i < count; i++) |
| { |
| int e = pni_decoder_single(decoder, data); |
| if (e) return e; |
| } |
| pn_data_exit(data); |
| |
| return 0; |
| } |
| default: |
| return pn_error_format(pni_decoder_error(decoder), PN_ARG_ERR, "unrecognized typecode: %u", code); |
| } |
| |
| return err; |
| } |
| |
| pn_type_t pni_data_parent_type(pn_data_t *data); |
| |
| static int pni_decoder_decode_type(pn_decoder_t *decoder, pn_data_t *data, uint8_t *code) |
| { |
| int err; |
| |
| if (!pn_decoder_remaining(decoder)) { |
| return PN_UNDERFLOW; |
| } |
| |
| uint8_t next = *decoder->position++; |
| |
| if (next != PNE_DESCRIPTOR) { |
| *code = next; |
| return 0; |
| } |
| |
| if (pni_data_parent_type(data) != PN_ARRAY) { |
| err = pn_data_put_described(data); |
| if (err) return err; |
| |
| // pni_decoder_single has the corresponding exit |
| pn_data_enter(data); |
| } |
| |
| err = pni_decoder_single_described(decoder, data); |
| if (err) return err; |
| |
| err = pni_decoder_decode_type(decoder, data, code); |
| if (err) return err; |
| |
| return 0; |
| } |
| |
| size_t pn_data_siblings(pn_data_t *data); |
| |
| // We disallow using any compound type as a described descriptor to avoid recursion |
| // in decoding. Although these seem syntactically valid they don't seem to be of any |
| // conceivable use! |
| static inline bool pni_allowed_descriptor_code(uint8_t code) |
| { |
| return |
| code != PNE_DESCRIPTOR && |
| code != PNE_ARRAY8 && code != PNE_ARRAY32 && |
| code != PNE_LIST8 && code != PNE_LIST32 && |
| code != PNE_MAP8 && code != PNE_MAP32; |
| } |
| |
| int pni_decoder_single_described(pn_decoder_t *decoder, pn_data_t *data) |
| { |
| if (!pn_decoder_remaining(decoder)) { |
| return PN_UNDERFLOW; |
| } |
| |
| uint8_t code = *decoder->position++;; |
| |
| if (!pni_allowed_descriptor_code(code)) { |
| return PN_ARG_ERR; |
| } |
| |
| int err = pni_decoder_decode_value(decoder, data, code); |
| if (err) return err; |
| |
| if (pni_data_parent_type(data) == PN_DESCRIBED && pn_data_siblings(data) > 1) { |
| pn_data_exit(data); |
| } |
| return 0; |
| } |
| |
| int pni_decoder_single(pn_decoder_t *decoder, pn_data_t *data) |
| { |
| uint8_t code; |
| int err = pni_decoder_decode_type(decoder, data, &code); |
| if (err) return err; |
| err = pni_decoder_decode_value(decoder, data, code); |
| if (err) return err; |
| if (pni_data_parent_type(data) == PN_DESCRIBED && pn_data_siblings(data) > 1) { |
| pn_data_exit(data); |
| } |
| return 0; |
| } |
| |
| ssize_t pn_decoder_decode(pn_decoder_t *decoder, const char *src, size_t size, pn_data_t *dst) |
| { |
| decoder->input = src; |
| decoder->size = size; |
| decoder->position = src; |
| |
| int err = pni_decoder_single(decoder, dst); |
| |
| if (err == PN_UNDERFLOW) |
| return pn_error_format(pn_data_error(dst), PN_UNDERFLOW, "not enough data to decode"); |
| if (err) return err; |
| |
| return decoder->position - decoder->input; |
| } |