| /* |
| * |
| * 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/object.h> |
| #include <proton/codec.h> |
| #include <proton/error.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <stddef.h> |
| #include <ctype.h> |
| #include "encodings.h" |
| #define DEFINE_FIELDS |
| #include "protocol.h" |
| #include "platform/platform_fmt.h" |
| #include "util.h" |
| #include "decoder.h" |
| #include "encoder.h" |
| #include "data.h" |
| #include "logger_private.h" |
| #include "memory.h" |
| |
| const char *pn_type_name(pn_type_t type) |
| { |
| switch (type) |
| { |
| case PN_NULL: return "PN_NULL"; |
| case PN_BOOL: return "PN_BOOL"; |
| case PN_UBYTE: return "PN_UBYTE"; |
| case PN_BYTE: return "PN_BYTE"; |
| case PN_USHORT: return "PN_USHORT"; |
| case PN_SHORT: return "PN_SHORT"; |
| case PN_UINT: return "PN_UINT"; |
| case PN_INT: return "PN_INT"; |
| case PN_CHAR: return "PN_CHAR"; |
| case PN_ULONG: return "PN_ULONG"; |
| case PN_LONG: return "PN_LONG"; |
| case PN_TIMESTAMP: return "PN_TIMESTAMP"; |
| case PN_FLOAT: return "PN_FLOAT"; |
| case PN_DOUBLE: return "PN_DOUBLE"; |
| case PN_DECIMAL32: return "PN_DECIMAL32"; |
| case PN_DECIMAL64: return "PN_DECIMAL64"; |
| case PN_DECIMAL128: return "PN_DECIMAL128"; |
| case PN_UUID: return "PN_UUID"; |
| case PN_BINARY: return "PN_BINARY"; |
| case PN_STRING: return "PN_STRING"; |
| case PN_SYMBOL: return "PN_SYMBOL"; |
| case PN_DESCRIBED: return "PN_DESCRIBED"; |
| case PN_ARRAY: return "PN_ARRAY"; |
| case PN_LIST: return "PN_LIST"; |
| case PN_MAP: return "PN_MAP"; |
| default: break; |
| } |
| |
| return "<UNKNOWN>"; |
| } |
| |
| static inline void pni_atom_init(pn_atom_t *atom, pn_type_t type) |
| { |
| memset(atom, 0, sizeof(pn_atom_t)); |
| atom->type = type; |
| } |
| |
| // data |
| |
| static void pn_data_finalize(void *object) |
| { |
| pn_data_t *data = (pn_data_t *) object; |
| pni_mem_subdeallocate(pn_class(data), data, data->nodes); |
| pn_buffer_free(data->buf); |
| pn_error_free(data->error); |
| } |
| |
| static const pn_fields_t *pni_node_fields(pn_data_t *data, pni_node_t *node) |
| { |
| if (!node) return NULL; |
| if (node->atom.type != PN_DESCRIBED) return NULL; |
| |
| pni_node_t *descriptor = pn_data_node(data, node->down); |
| |
| if (!descriptor || descriptor->atom.type != PN_ULONG) { |
| return NULL; |
| } |
| |
| if (descriptor->atom.u.as_ulong >= FIELD_MIN && descriptor->atom.u.as_ulong <= FIELD_MAX) { |
| const pn_fields_t *f = &FIELDS[descriptor->atom.u.as_ulong-FIELD_MIN]; |
| return (f->name_index!=0) ? f : NULL; |
| } else { |
| return NULL; |
| } |
| } |
| |
| int pni_inspect_atom(pn_atom_t *atom, pn_string_t *str) |
| { |
| switch (atom->type) { |
| case PN_NULL: |
| return pn_string_addf(str, "null"); |
| case PN_BOOL: |
| return pn_string_addf(str, atom->u.as_bool ? "true" : "false"); |
| case PN_UBYTE: |
| return pn_string_addf(str, "%" PRIu8, atom->u.as_ubyte); |
| case PN_BYTE: |
| return pn_string_addf(str, "%" PRIi8, atom->u.as_byte); |
| case PN_USHORT: |
| return pn_string_addf(str, "%" PRIu16, atom->u.as_ushort); |
| case PN_SHORT: |
| return pn_string_addf(str, "%" PRIi16, atom->u.as_short); |
| case PN_UINT: |
| return pn_string_addf(str, "%" PRIu32, atom->u.as_uint); |
| case PN_INT: |
| return pn_string_addf(str, "%" PRIi32, atom->u.as_int); |
| case PN_CHAR: |
| return pn_string_addf(str, "%c", atom->u.as_char); |
| case PN_ULONG: |
| return pn_string_addf(str, "%" PRIu64, atom->u.as_ulong); |
| case PN_LONG: |
| return pn_string_addf(str, "%" PRIi64, atom->u.as_long); |
| case PN_TIMESTAMP: |
| return pn_string_addf(str, "%" PRIi64, atom->u.as_timestamp); |
| case PN_FLOAT: |
| return pn_string_addf(str, "%g", atom->u.as_float); |
| case PN_DOUBLE: |
| return pn_string_addf(str, "%g", atom->u.as_double); |
| case PN_DECIMAL32: |
| return pn_string_addf(str, "D32(%" PRIu32 ")", atom->u.as_decimal32); |
| case PN_DECIMAL64: |
| return pn_string_addf(str, "D64(%" PRIu64 ")", atom->u.as_decimal64); |
| case PN_DECIMAL128: |
| return pn_string_addf(str, "D128(%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" |
| "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" |
| "%02hhx%02hhx)", |
| atom->u.as_decimal128.bytes[0], |
| atom->u.as_decimal128.bytes[1], |
| atom->u.as_decimal128.bytes[2], |
| atom->u.as_decimal128.bytes[3], |
| atom->u.as_decimal128.bytes[4], |
| atom->u.as_decimal128.bytes[5], |
| atom->u.as_decimal128.bytes[6], |
| atom->u.as_decimal128.bytes[7], |
| atom->u.as_decimal128.bytes[8], |
| atom->u.as_decimal128.bytes[9], |
| atom->u.as_decimal128.bytes[10], |
| atom->u.as_decimal128.bytes[11], |
| atom->u.as_decimal128.bytes[12], |
| atom->u.as_decimal128.bytes[13], |
| atom->u.as_decimal128.bytes[14], |
| atom->u.as_decimal128.bytes[15]); |
| case PN_UUID: |
| return pn_string_addf(str, "UUID(%02hhx%02hhx%02hhx%02hhx-" |
| "%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-" |
| "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx)", |
| atom->u.as_uuid.bytes[0], |
| atom->u.as_uuid.bytes[1], |
| atom->u.as_uuid.bytes[2], |
| atom->u.as_uuid.bytes[3], |
| atom->u.as_uuid.bytes[4], |
| atom->u.as_uuid.bytes[5], |
| atom->u.as_uuid.bytes[6], |
| atom->u.as_uuid.bytes[7], |
| atom->u.as_uuid.bytes[8], |
| atom->u.as_uuid.bytes[9], |
| atom->u.as_uuid.bytes[10], |
| atom->u.as_uuid.bytes[11], |
| atom->u.as_uuid.bytes[12], |
| atom->u.as_uuid.bytes[13], |
| atom->u.as_uuid.bytes[14], |
| atom->u.as_uuid.bytes[15]); |
| case PN_BINARY: |
| case PN_STRING: |
| case PN_SYMBOL: |
| { |
| int err; |
| const char *pfx; |
| pn_bytes_t bin = atom->u.as_bytes; |
| bool quote; |
| switch (atom->type) { |
| case PN_BINARY: |
| pfx = "b"; |
| quote = true; |
| break; |
| case PN_STRING: |
| pfx = ""; |
| quote = true; |
| break; |
| case PN_SYMBOL: |
| pfx = ":"; |
| quote = false; |
| for (unsigned i = 0; i < bin.size; i++) { |
| if (!isalpha(bin.start[i])) { |
| quote = true; |
| break; |
| } |
| } |
| break; |
| default: |
| assert(false); |
| return PN_ERR; |
| } |
| |
| if ((err = pn_string_addf(str, "%s", pfx))) return err; |
| if (quote) if ((err = pn_string_addf(str, "\""))) return err; |
| if ((err = pn_quote(str, bin.start, bin.size))) return err; |
| if (quote) if ((err = pn_string_addf(str, "\""))) return err; |
| return 0; |
| } |
| case PN_LIST: |
| return pn_string_addf(str, "<list>"); |
| case PN_MAP: |
| return pn_string_addf(str, "<map>"); |
| case PN_ARRAY: |
| return pn_string_addf(str, "<array>"); |
| case PN_DESCRIBED: |
| return pn_string_addf(str, "<described>"); |
| default: |
| return pn_string_addf(str, "<undefined: %i>", atom->type); |
| } |
| } |
| |
| /* Return index in current list, array etc.*/ |
| static int pni_node_lindex(pn_data_t *data, pni_node_t *node) |
| { |
| int count = 0; |
| while (node) { |
| node = pn_data_node(data, node->prev); |
| count++; |
| } |
| return count - 1; |
| } |
| |
| int pni_inspect_enter(void *ctx, pn_data_t *data, pni_node_t *node) |
| { |
| pn_string_t *str = (pn_string_t *) ctx; |
| pn_atom_t *atom = (pn_atom_t *) &node->atom; |
| |
| pni_node_t *parent = pn_data_node(data, node->parent); |
| const pn_fields_t *fields = pni_node_fields(data, parent); |
| pni_node_t *grandparent = parent ? pn_data_node(data, parent->parent) : NULL; |
| const pn_fields_t *grandfields = pni_node_fields(data, grandparent); |
| |
| int err; |
| |
| if (grandfields) { |
| if (atom->type == PN_NULL) { |
| return 0; |
| } |
| pni_nid_t lindex = pni_node_lindex(data, node); |
| const char *name = (lindex < grandfields->field_count) |
| ? (const char*)FIELD_STRINGPOOL.STRING0+FIELD_FIELDS[grandfields->first_field_index+lindex] |
| : NULL; |
| if (name) { |
| err = pn_string_addf(str, "%s=", name); |
| if (err) return err; |
| } |
| } |
| |
| switch (atom->type) { |
| case PN_DESCRIBED: |
| return pn_string_addf(str, "@"); |
| case PN_ARRAY: |
| // XXX: need to fix for described arrays |
| return pn_string_addf(str, "@%s[", pn_type_name(node->type)); |
| case PN_LIST: |
| return pn_string_addf(str, "["); |
| case PN_MAP: |
| return pn_string_addf(str, "{"); |
| default: |
| if (fields && node->prev == 0) { |
| err = pn_string_addf(str, "%s", (const char *)FIELD_STRINGPOOL.STRING0+FIELD_NAME[fields->name_index]); |
| if (err) return err; |
| err = pn_string_addf(str, "("); |
| if (err) return err; |
| err = pni_inspect_atom(atom, str); |
| if (err) return err; |
| return pn_string_addf(str, ")"); |
| } else { |
| return pni_inspect_atom(atom, str); |
| } |
| } |
| } |
| |
| pni_node_t *pni_next_nonnull(pn_data_t *data, pni_node_t *node) |
| { |
| while (node) { |
| node = pn_data_node(data, node->next); |
| if (node && node->atom.type != PN_NULL) { |
| return node; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| int pni_inspect_exit(void *ctx, pn_data_t *data, pni_node_t *node) |
| { |
| pn_string_t *str = (pn_string_t *) ctx; |
| int err; |
| |
| switch (node->atom.type) { |
| case PN_ARRAY: |
| case PN_LIST: |
| err = pn_string_addf(str, "]"); |
| if (err) return err; |
| break; |
| case PN_MAP: |
| err = pn_string_addf(str, "}"); |
| if (err) return err; |
| break; |
| default: |
| break; |
| } |
| |
| pni_node_t *parent = pn_data_node(data, node->parent); |
| pni_node_t *grandparent = parent ? pn_data_node(data, parent->parent) : NULL; |
| const pn_fields_t *grandfields = pni_node_fields(data, grandparent); |
| if (!grandfields || node->atom.type != PN_NULL) { |
| if (node->next) { |
| if (parent && parent->atom.type == PN_MAP && (pni_node_lindex(data, node) % 2) == 0) { |
| err = pn_string_addf(str, "="); |
| if (err) return err; |
| } else if (parent && parent->atom.type == PN_DESCRIBED && node->prev == 0) { |
| err = pn_string_addf(str, " "); |
| if (err) return err; |
| } else { |
| if (!grandfields || pni_next_nonnull(data, node)) { |
| err = pn_string_addf(str, ", "); |
| if (err) return err; |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int pn_data_inspect(void *obj, pn_string_t *dst) |
| { |
| pn_data_t *data = (pn_data_t *) obj; |
| |
| return pni_data_traverse(data, pni_inspect_enter, pni_inspect_exit, dst); |
| } |
| |
| #define pn_data_initialize NULL |
| #define pn_data_hashcode NULL |
| #define pn_data_compare NULL |
| |
| static inline pn_error_t *pni_data_error(pn_data_t *data) |
| { |
| if (data->error == NULL) { |
| data->error = pn_error(); |
| } |
| return data->error; |
| } |
| |
| pn_data_t *pn_data(size_t capacity) |
| { |
| static const pn_class_t clazz = PN_CLASS(pn_data); |
| pn_data_t *data = (pn_data_t *) pn_class_new(&clazz, sizeof(pn_data_t)); |
| data->capacity = capacity; |
| data->size = 0; |
| data->nodes = capacity ? (pni_node_t *) pni_mem_suballocate(&clazz, data, capacity * sizeof(pni_node_t)) : NULL; |
| data->buf = NULL; |
| data->parent = 0; |
| data->current = 0; |
| data->base_parent = 0; |
| data->base_current = 0; |
| data->error = NULL; |
| return data; |
| } |
| |
| void pn_data_free(pn_data_t *data) |
| { |
| pn_free(data); |
| } |
| |
| int pn_data_errno(pn_data_t *data) |
| { |
| return pn_error_code(pni_data_error(data)); |
| } |
| |
| pn_error_t *pn_data_error(pn_data_t *data) |
| { |
| return pni_data_error(data); |
| } |
| |
| size_t pn_data_size(pn_data_t *data) |
| { |
| return data ? data->size : 0; |
| } |
| |
| void pn_data_clear(pn_data_t *data) |
| { |
| if (data) { |
| data->size = 0; |
| data->parent = 0; |
| data->current = 0; |
| data->base_parent = 0; |
| data->base_current = 0; |
| if (data->buf) pn_buffer_clear(data->buf); |
| } |
| } |
| |
| static int pni_data_grow(pn_data_t *data) |
| { |
| size_t capacity = data->capacity ? data->capacity : 2; |
| if (capacity >= PNI_NID_MAX) return PN_OUT_OF_MEMORY; |
| else if (capacity < PNI_NID_MAX/2) capacity *= 2; |
| else capacity = PNI_NID_MAX; |
| |
| pni_node_t *new_nodes = (pni_node_t *) pni_mem_subreallocate(pn_class(data), data, data->nodes, capacity * sizeof(pni_node_t)); |
| if (new_nodes == NULL) return PN_OUT_OF_MEMORY; |
| data->capacity = capacity; |
| data->nodes = new_nodes; |
| return 0; |
| } |
| |
| static ssize_t pni_data_intern(pn_data_t *data, const char *start, size_t size) |
| { |
| size_t offset = pn_buffer_size(data->buf); |
| int err = pn_buffer_append(data->buf, start, size); |
| if (err) return err; |
| err = pn_buffer_append(data->buf, "\0", 1); |
| if (err) return err; |
| return offset; |
| } |
| |
| static pn_bytes_t *pni_data_bytes(pn_data_t *data, pni_node_t *node) |
| { |
| switch (node->atom.type) { |
| case PN_BINARY: |
| case PN_STRING: |
| case PN_SYMBOL: |
| return &node->atom.u.as_bytes; |
| default: return NULL; |
| } |
| } |
| |
| static void pni_data_rebase(pn_data_t *data, char *base) |
| { |
| for (unsigned i = 0; i < data->size; i++) { |
| pni_node_t *node = &data->nodes[i]; |
| if (node->data) { |
| pn_bytes_t *bytes = pni_data_bytes(data, node); |
| bytes->start = base + node->data_offset; |
| } |
| } |
| } |
| |
| static int pni_data_intern_node(pn_data_t *data, pni_node_t *node) |
| { |
| pn_bytes_t *bytes = pni_data_bytes(data, node); |
| if (!bytes) return 0; |
| if (data->buf == NULL) { |
| // Heuristic to avoid growing small buffers too much |
| // size + 1 to allow for zero termination |
| size_t size = pn_max(bytes->size+1, PNI_INTERN_MINSIZE); |
| data->buf = pn_buffer(size); |
| } |
| size_t oldcap = pn_buffer_capacity(data->buf); |
| ssize_t offset = pni_data_intern(data, bytes->start, bytes->size); |
| if (offset < 0) return offset; |
| node->data = true; |
| node->data_offset = offset; |
| node->data_size = bytes->size; |
| pn_rwbytes_t buf = pn_buffer_memory(data->buf); |
| bytes->start = buf.start + offset; |
| |
| if (pn_buffer_capacity(data->buf) != oldcap) { |
| pni_data_rebase(data, buf.start); |
| } |
| |
| return 0; |
| } |
| |
| |
| /* |
| Append src to data after normalizing for "multiple" field encoding. |
| |
| AMQP composite field definitions can be declared "multiple", see: |
| |
| - http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-types-v1.0-os.html#doc-idp115568 |
| - http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-types-v1.0-os.html#section-composite-type-representation |
| |
| Multiple fields allow redundant encoding of two cases: |
| |
| 1. empty: null or an empty array. |
| 2. single-value: direct encoding of value, or array with one element |
| |
| For encoding compactness and inter-operability, normalize multiple |
| field values to always use null for empty, and direct encoding for |
| single value. |
| */ |
| static int pni_normalize_multiple(pn_data_t *data, pn_data_t *src) { |
| int err = 0; |
| pn_handle_t point = pn_data_point(src); |
| pn_data_rewind(src); |
| pn_data_next(src); |
| if (pn_data_type(src) == PN_ARRAY) { |
| switch (pn_data_get_array(src)) { |
| case 0: /* Empty array => null */ |
| err = pn_data_put_null(data); |
| break; |
| case 1: /* Single-element array => encode the element */ |
| pn_data_enter(src); |
| pn_data_narrow(src); |
| err = pn_data_appendn(data, src, 1); |
| pn_data_widen(src); |
| break; |
| default: /* Multi-element array, encode unchanged */ |
| err = pn_data_appendn(data, src, 1); |
| break; |
| } |
| } else { |
| err = pn_data_appendn(data, src, 1); /* Non-array, append the value */ |
| } |
| pn_data_restore(src, point); |
| return err; |
| } |
| |
| |
| /* Format codes: |
| code: AMQP-type (arguments) |
| n: null () |
| o: bool (int) |
| B: ubyte (unsigned int) |
| b: byte (int) |
| H: ushort (unsigned int) |
| h: short (int) |
| I: uint (uint32_t) |
| i: int (int32_t) |
| L: ulong (ulong32_t) |
| l: long (long32_t) |
| t: timestamp (pn_timestamp_t) |
| f: float (float) |
| d: double (double) |
| Z: binary (size_t, char*) - must not be NULL |
| z: binary (size_t, char*) - encode as AMQP null if NULL |
| S: string (char*) |
| s: symbol (char*) |
| D: described - next two codes are [descriptor, body] |
| @: enter array. If followed by D, a described array. Following codes to matching ']' are elements. |
| T: type (pn_type_t) - set array type while in array |
| [: enter list. Following codes up to matching ']' are elements |
| {: enter map. Following codes up to matching '}' are key, value pairs |
| ]: exit list or array |
| }: exit map |
| ?: TODO document |
| *: TODO document |
| C: single value (pn_data_t*) - append the pn_data_t unmodified |
| M: multiple value (pn_data_t*) - normalize and append multiple field value, |
| see pni_normalize_multiple() |
| */ |
| int pn_data_vfill(pn_data_t *data, const char *fmt, va_list ap) |
| { |
| int err = 0; |
| const char *begin = fmt; |
| while (*fmt) { |
| char code = *(fmt++); |
| if (!code) return 0; |
| |
| switch (code) { |
| case 'n': |
| err = pn_data_put_null(data); |
| break; |
| case 'o': |
| err = pn_data_put_bool(data, va_arg(ap, int)); |
| break; |
| case 'B': |
| err = pn_data_put_ubyte(data, va_arg(ap, unsigned int)); |
| break; |
| case 'b': |
| err = pn_data_put_byte(data, va_arg(ap, int)); |
| break; |
| case 'H': |
| err = pn_data_put_ushort(data, va_arg(ap, unsigned int)); |
| break; |
| case 'h': |
| err = pn_data_put_short(data, va_arg(ap, int)); |
| break; |
| case 'I': |
| err = pn_data_put_uint(data, va_arg(ap, uint32_t)); |
| break; |
| case 'i': |
| err = pn_data_put_int(data, va_arg(ap, uint32_t)); |
| break; |
| case 'L': |
| err = pn_data_put_ulong(data, va_arg(ap, uint64_t)); |
| break; |
| case 'l': |
| err = pn_data_put_long(data, va_arg(ap, int64_t)); |
| break; |
| case 't': |
| err = pn_data_put_timestamp(data, va_arg(ap, pn_timestamp_t)); |
| break; |
| case 'f': |
| err = pn_data_put_float(data, va_arg(ap, double)); |
| break; |
| case 'd': |
| err = pn_data_put_double(data, va_arg(ap, double)); |
| break; |
| case 'Z': /* encode binary, must not be NULL */ |
| { |
| // For maximum portability, caller must pass these as two separate args, not a single struct |
| size_t size = va_arg(ap, size_t); |
| char *start = va_arg(ap, char *); |
| err = pn_data_put_binary(data, pn_bytes(size, start)); |
| } |
| break; |
| case 'z': /* encode binary or null if pointer is NULL */ |
| { |
| // For maximum portability, caller must pass these as two separate args, not a single struct |
| size_t size = va_arg(ap, size_t); |
| char *start = va_arg(ap, char *); |
| if (start) { |
| err = pn_data_put_binary(data, pn_bytes(size, start)); |
| } else { |
| err = pn_data_put_null(data); |
| } |
| } |
| break; |
| case 'S': /* encode string or null if NULL */ |
| case 's': /* encode symbol or null if NULL */ |
| { |
| char *start = va_arg(ap, char *); |
| size_t size; |
| if (start) { |
| size = strlen(start); |
| if (code == 'S') { |
| err = pn_data_put_string(data, pn_bytes(size, start)); |
| } else { |
| err = pn_data_put_symbol(data, pn_bytes(size, start)); |
| } |
| } else { |
| err = pn_data_put_null(data); |
| } |
| } |
| break; |
| case 'D': |
| /* The next 2 args are the descriptor, value for a described value. */ |
| err = pn_data_put_described(data); |
| pn_data_enter(data); |
| break; |
| case 'T': /* Set type of open array */ |
| { |
| pni_node_t *parent = pn_data_node(data, data->parent); |
| if (parent->atom.type == PN_ARRAY) { |
| parent->type = (pn_type_t) va_arg(ap, int); |
| } else { |
| return pn_error_format(pni_data_error(data), PN_ERR, "naked type"); |
| } |
| } |
| break; |
| case '@': /* begin array */ |
| { |
| bool described; |
| if (*(fmt + 1) == 'D') { |
| fmt++; |
| described = true; |
| } else { |
| described = false; |
| } |
| err = pn_data_put_array(data, described, (pn_type_t) 0); |
| pn_data_enter(data); |
| } |
| break; |
| case '[': /* begin list */ |
| if (fmt < (begin + 2) || *(fmt - 2) != 'T') { |
| err = pn_data_put_list(data); |
| if (err) return err; |
| pn_data_enter(data); |
| } |
| break; |
| case '{': /* begin map */ |
| err = pn_data_put_map(data); |
| if (err) return err; |
| pn_data_enter(data); |
| break; |
| case '}': |
| case ']': |
| if (!pn_data_exit(data)) |
| return pn_error_format(pni_data_error(data), PN_ERR, "exit failed"); |
| break; |
| case '?': |
| if (!va_arg(ap, int)) { |
| err = pn_data_put_null(data); |
| if (err) return err; |
| pn_data_enter(data); |
| } |
| break; |
| case '*': |
| { |
| int count = va_arg(ap, int); |
| void *ptr = va_arg(ap, void *); |
| |
| char c = *(fmt++); |
| |
| switch (c) |
| { |
| case 's': |
| { |
| char **sptr = (char **) ptr; |
| for (int i = 0; i < count; i++) |
| { |
| char *sym = *(sptr++); |
| err = pn_data_fill(data, "s", sym); |
| if (err) return err; |
| } |
| } |
| break; |
| default: |
| PN_LOG_DEFAULT(PN_SUBSYSTEM_AMQP, PN_LEVEL_CRITICAL, "unrecognized * code: 0x%.2X '%c'", code, code); |
| return PN_ARG_ERR; |
| } |
| } |
| break; |
| case 'C': /* Append an existing pn_data_t * */ |
| { |
| pn_data_t *src = va_arg(ap, pn_data_t *); |
| if (src && pn_data_size(src) > 0) { |
| err = pn_data_appendn(data, src, 1); |
| if (err) return err; |
| } else { |
| err = pn_data_put_null(data); |
| if (err) return err; |
| } |
| } |
| break; |
| case 'M': |
| { |
| pn_data_t *src = va_arg(ap, pn_data_t *); |
| err = (src && pn_data_size(src) > 0) ? |
| pni_normalize_multiple(data, src) : pn_data_put_null(data); |
| break; |
| } |
| default: |
| PN_LOG_DEFAULT(PN_SUBSYSTEM_AMQP, PN_LEVEL_CRITICAL, "unrecognized fill code: 0x%.2X '%c'", code, code); |
| return PN_ARG_ERR; |
| } |
| |
| if (err) return err; |
| |
| pni_node_t *parent = pn_data_node(data, data->parent); |
| pni_node_t *current = pn_data_node(data, data->current); |
| while (parent) { |
| if (parent->atom.type == PN_DESCRIBED && parent->children == 2) { |
| current->described = true; |
| pn_data_exit(data); |
| current = pn_data_node(data, data->current); |
| parent = pn_data_node(data, data->parent); |
| } else if (parent->atom.type == PN_NULL && parent->children == 1) { |
| pn_data_exit(data); |
| current = pn_data_node(data, data->current); |
| current->down = 0; |
| current->children = 0; |
| parent = pn_data_node(data, data->parent); |
| } else { |
| break; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| int pn_data_fill(pn_data_t *data, const char *fmt, ...) |
| { |
| va_list ap; |
| va_start(ap, fmt); |
| int err = pn_data_vfill(data, fmt, ap); |
| va_end(ap); |
| return err; |
| } |
| |
| static bool pn_scan_next(pn_data_t *data, pn_type_t *type, bool suspend) |
| { |
| if (suspend) return false; |
| bool found = pn_data_next(data); |
| if (found) { |
| *type = pn_data_type(data); |
| return true; |
| } else { |
| pni_node_t *parent = pn_data_node(data, data->parent); |
| if (parent && parent->atom.type == PN_DESCRIBED) { |
| pn_data_exit(data); |
| return pn_scan_next(data, type, suspend); |
| } else { |
| *type = PN_INVALID; |
| return false; |
| } |
| } |
| } |
| |
| static pni_node_t *pni_data_peek(pn_data_t *data); |
| |
| int pn_data_vscan(pn_data_t *data, const char *fmt, va_list ap) |
| { |
| pn_data_rewind(data); |
| bool *scanarg = NULL; |
| bool at = false; |
| int level = 0; |
| int count_level = -1; |
| int resume_count = 0; |
| |
| while (*fmt) { |
| char code = *(fmt++); |
| |
| bool found = false; |
| pn_type_t type; |
| |
| bool scanned = false; |
| bool suspend = resume_count > 0; |
| |
| switch (code) { |
| case 'n': |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_NULL) { |
| scanned = true; |
| } else { |
| scanned = false; |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'o': |
| { |
| bool *value = va_arg(ap, bool *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_BOOL) { |
| *value = pn_data_get_bool(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'B': |
| { |
| uint8_t *value = va_arg(ap, uint8_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_UBYTE) { |
| *value = pn_data_get_ubyte(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'b': |
| { |
| int8_t *value = va_arg(ap, int8_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_BYTE) { |
| *value = pn_data_get_byte(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'H': |
| { |
| uint16_t *value = va_arg(ap, uint16_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_USHORT) { |
| *value = pn_data_get_ushort(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'h': |
| { |
| int16_t *value = va_arg(ap, int16_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_SHORT) { |
| *value = pn_data_get_short(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'I': |
| { |
| uint32_t *value = va_arg(ap, uint32_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_UINT) { |
| *value = pn_data_get_uint(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'i': |
| { |
| int32_t *value = va_arg(ap, int32_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_INT) { |
| *value = pn_data_get_int(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'c': |
| { |
| pn_char_t *value = va_arg(ap, pn_char_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_CHAR) { |
| *value = pn_data_get_char(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'L': |
| { |
| uint64_t *value = va_arg(ap, uint64_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_ULONG) { |
| *value = pn_data_get_ulong(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'l': |
| { |
| int64_t *value = va_arg(ap, int64_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_LONG) { |
| *value = pn_data_get_long(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 't': |
| { |
| pn_timestamp_t *value = va_arg(ap, pn_timestamp_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_TIMESTAMP) { |
| *value = pn_data_get_timestamp(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'f': |
| { |
| float *value = va_arg(ap, float *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_FLOAT) { |
| *value = pn_data_get_float(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'd': |
| { |
| double *value = va_arg(ap, double *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_DOUBLE) { |
| *value = pn_data_get_double(data); |
| scanned = true; |
| } else { |
| *value = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'z': |
| { |
| pn_bytes_t *bytes = va_arg(ap, pn_bytes_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_BINARY) { |
| *bytes = pn_data_get_binary(data); |
| scanned = true; |
| } else { |
| bytes->start = 0; |
| bytes->size = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'S': |
| { |
| pn_bytes_t *bytes = va_arg(ap, pn_bytes_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_STRING) { |
| *bytes = pn_data_get_string(data); |
| scanned = true; |
| } else { |
| bytes->start = 0; |
| bytes->size = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 's': |
| { |
| pn_bytes_t *bytes = va_arg(ap, pn_bytes_t *); |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_SYMBOL) { |
| *bytes = pn_data_get_symbol(data); |
| scanned = true; |
| } else { |
| bytes->start = 0; |
| bytes->size = 0; |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case 'D': |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_DESCRIBED) { |
| pn_data_enter(data); |
| scanned = true; |
| } else { |
| if (!suspend) { |
| resume_count = 3; |
| count_level = level; |
| } |
| scanned = false; |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case '@': |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_ARRAY) { |
| pn_data_enter(data); |
| scanned = true; |
| at = true; |
| } else { |
| if (!suspend) { |
| resume_count = 3; |
| count_level = level; |
| } |
| scanned = false; |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case '[': |
| if (at) { |
| scanned = true; |
| at = false; |
| } else { |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_LIST) { |
| pn_data_enter(data); |
| scanned = true; |
| } else { |
| if (!suspend) { |
| resume_count = 1; |
| count_level = level; |
| } |
| scanned = false; |
| } |
| } |
| level++; |
| break; |
| case '{': |
| found = pn_scan_next(data, &type, suspend); |
| if (found && type == PN_MAP) { |
| pn_data_enter(data); |
| scanned = true; |
| } else { |
| if (resume_count) { |
| resume_count = 1; |
| count_level = level; |
| } |
| scanned = false; |
| } |
| level++; |
| break; |
| case ']': |
| case '}': |
| level--; |
| if (!suspend && !pn_data_exit(data)) |
| return pn_error_format(pni_data_error(data), PN_ERR, "exit failed"); |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case '.': |
| found = pn_scan_next(data, &type, suspend); |
| scanned = found; |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| case '?': |
| if (!*fmt || *fmt == '?') |
| return pn_error_format(pni_data_error(data), PN_ARG_ERR, "codes must follow a ?"); |
| scanarg = va_arg(ap, bool *); |
| break; |
| case 'C': |
| { |
| pn_data_t *dst = va_arg(ap, pn_data_t *); |
| if (!suspend) { |
| size_t old = pn_data_size(dst); |
| pni_node_t *next = pni_data_peek(data); |
| if (next && next->atom.type != PN_NULL) { |
| pn_data_narrow(data); |
| int err = pn_data_appendn(dst, data, 1); |
| pn_data_widen(data); |
| if (err) return err; |
| scanned = pn_data_size(dst) > old; |
| } else { |
| scanned = false; |
| } |
| pn_data_next(data); |
| } else { |
| scanned = false; |
| } |
| } |
| if (resume_count && level == count_level) resume_count--; |
| break; |
| default: |
| return pn_error_format(pni_data_error(data), PN_ARG_ERR, "unrecognized scan code: 0x%.2X '%c'", code, code); |
| } |
| |
| if (scanarg && code != '?') { |
| *scanarg = scanned; |
| scanarg = NULL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int pn_data_scan(pn_data_t *data, const char *fmt, ...) |
| { |
| va_list ap; |
| va_start(ap, fmt); |
| int err = pn_data_vscan(data, fmt, ap); |
| va_end(ap); |
| return err; |
| } |
| |
| int pn_data_print(pn_data_t *data) |
| { |
| pn_string_t *str = pn_string(""); |
| int err = pn_data_inspect(data, str); |
| if (err) { |
| pn_free(str); |
| return err; |
| } |
| printf("%s", pn_string_get(str)); |
| pn_free(str); |
| return 0; |
| } |
| |
| int pn_data_format(pn_data_t *data, char *bytes, size_t *size) |
| { |
| pn_string_t *str = pn_string(""); |
| int err = pn_data_inspect(data, str); |
| if (err) return err; |
| if (pn_string_size(str) >= *size) { |
| pn_free(str); |
| return PN_OVERFLOW; |
| } else { |
| pn_string_put(str, bytes); |
| *size = pn_string_size(str); |
| pn_free(str); |
| return 0; |
| } |
| } |
| |
| static size_t pni_data_id(pn_data_t *data, pni_node_t *node) |
| { |
| return node - data->nodes + 1; |
| } |
| |
| static pni_node_t *pni_data_new(pn_data_t *data) |
| { |
| if ((data->capacity <= data->size) && (pni_data_grow(data) != 0)) return NULL; |
| pni_node_t *node = pn_data_node(data, ++(data->size)); |
| node->next = 0; |
| node->down = 0; |
| node->children = 0; |
| return node; |
| } |
| |
| void pn_data_rewind(pn_data_t *data) |
| { |
| data->parent = data->base_parent; |
| data->current = data->base_current; |
| } |
| |
| static pni_node_t *pni_data_current(pn_data_t *data) |
| { |
| return pn_data_node(data, data->current); |
| } |
| |
| void pn_data_narrow(pn_data_t *data) |
| { |
| data->base_parent = data->parent; |
| data->base_current = data->current; |
| } |
| |
| void pn_data_widen(pn_data_t *data) |
| { |
| data->base_parent = 0; |
| data->base_current = 0; |
| } |
| |
| pn_handle_t pn_data_point(pn_data_t *data) |
| { |
| if (data->current) { |
| return (pn_handle_t)(uintptr_t)data->current; |
| } else { |
| return (pn_handle_t)(uintptr_t)-data->parent; |
| } |
| } |
| |
| bool pn_data_restore(pn_data_t *data, pn_handle_t point) |
| { |
| pn_shandle_t spoint = (pn_shandle_t) point; |
| if (spoint <= 0 && ((size_t) (-spoint)) <= data->size) { |
| data->parent = -((pn_shandle_t) point); |
| data->current = 0; |
| return true; |
| } else if (spoint && spoint <= data->size) { |
| data->current = spoint; |
| pni_node_t *current = pni_data_current(data); |
| data->parent = current->parent; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static pni_node_t *pni_data_peek(pn_data_t *data) |
| { |
| pni_node_t *current = pni_data_current(data); |
| if (current) { |
| return pn_data_node(data, current->next); |
| } |
| |
| pni_node_t *parent = pn_data_node(data, data->parent); |
| if (parent) { |
| return pn_data_node(data, parent->down); |
| } |
| |
| return NULL; |
| } |
| |
| bool pn_data_next(pn_data_t *data) |
| { |
| pni_node_t *current = pni_data_current(data); |
| pni_node_t *parent = pn_data_node(data, data->parent); |
| size_t next; |
| |
| if (current) { |
| next = current->next; |
| } else if (parent && parent->down) { |
| next = parent->down; |
| } else if (!parent && data->size) { |
| next = 1; |
| } else { |
| return false; |
| } |
| |
| if (next) { |
| data->current = next; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| bool pn_data_prev(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->prev) { |
| data->current = node->prev; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| int pni_data_traverse(pn_data_t *data, |
| int (*enter)(void *ctx, pn_data_t *data, pni_node_t *node), |
| int (*exit)(void *ctx, pn_data_t *data, pni_node_t *node), |
| void *ctx) |
| { |
| pni_node_t *node = data->size ? pn_data_node(data, 1) : NULL; |
| while (node) { |
| pni_node_t *parent = pn_data_node(data, node->parent); |
| |
| int err = enter(ctx, data, node); |
| if (err) return err; |
| |
| size_t next = 0; |
| if (node->down) { |
| next = node->down; |
| } else if (node->next) { |
| err = exit(ctx, data, node); |
| if (err) return err; |
| next = node->next; |
| } else { |
| err = exit(ctx, data, node); |
| if (err) return err; |
| while (parent) { |
| err = exit(ctx, data, parent); |
| if (err) return err; |
| if (parent->next) { |
| next = parent->next; |
| break; |
| } else { |
| parent = pn_data_node(data, parent->parent); |
| } |
| } |
| } |
| |
| node = pn_data_node(data, next); |
| } |
| |
| return 0; |
| } |
| |
| pn_type_t pn_data_type(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node) { |
| return node->atom.type; |
| } else { |
| return PN_INVALID; |
| } |
| } |
| |
| pn_type_t pni_data_parent_type(pn_data_t *data) |
| { |
| pni_node_t *node = pn_data_node(data, data->parent); |
| if (node) { |
| return node->atom.type; |
| } else { |
| return PN_INVALID; |
| } |
| } |
| |
| size_t pn_data_siblings(pn_data_t *data) |
| { |
| pni_node_t *node = pn_data_node(data, data->parent); |
| if (node) { |
| return node->children; |
| } else { |
| return 0; |
| } |
| } |
| |
| bool pn_data_enter(pn_data_t *data) |
| { |
| if (data->current) { |
| data->parent = data->current; |
| data->current = 0; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| bool pn_data_exit(pn_data_t *data) |
| { |
| if (data->parent) { |
| pni_node_t *parent = pn_data_node(data, data->parent); |
| data->current = data->parent; |
| data->parent = parent->parent; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| bool pn_data_lookup(pn_data_t *data, const char *name) |
| { |
| while (pn_data_next(data)) { |
| pn_type_t type = pn_data_type(data); |
| |
| switch (type) { |
| case PN_STRING: |
| case PN_SYMBOL: |
| { |
| pn_bytes_t bytes = pn_data_get_bytes(data); |
| if (pn_bytes_equal(bytes, pn_bytes(strlen(name), name))) { |
| return pn_data_next(data); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| |
| // skip the value |
| pn_data_next(data); |
| } |
| |
| return false; |
| } |
| |
| void pn_data_dump(pn_data_t *data) |
| { |
| pn_string_t *str = pn_string(0); |
| printf("{current=%" PN_ZI ", parent=%" PN_ZI "}\n", (size_t) data->current, (size_t) data->parent); |
| for (unsigned i = 0; i < data->size; i++) |
| { |
| pni_node_t *node = &data->nodes[i]; |
| pn_string_setn(str, "", 0); |
| pni_inspect_atom((pn_atom_t *) &node->atom, str); |
| printf("Node %i: prev=%" PN_ZI ", next=%" PN_ZI ", parent=%" PN_ZI ", down=%" PN_ZI |
| ", children=%" PN_ZI ", type=%s (%s)\n", |
| i + 1, (size_t) node->prev, |
| (size_t) node->next, |
| (size_t) node->parent, |
| (size_t) node->down, |
| (size_t) node->children, |
| pn_type_name(node->atom.type), pn_string_get(str)); |
| } |
| pn_free(str); |
| } |
| |
| static pni_node_t *pni_data_add(pn_data_t *data) |
| { |
| pni_node_t *current = pni_data_current(data); |
| pni_node_t *parent = pn_data_node(data, data->parent); |
| pni_node_t *node; |
| |
| if (current) { |
| if (current->next) { |
| node = pn_data_node(data, current->next); |
| } else { |
| node = pni_data_new(data); |
| if (!node) return NULL; |
| |
| // refresh the pointers in case we grew |
| current = pni_data_current(data); |
| parent = pn_data_node(data, data->parent); |
| node->prev = data->current; |
| current->next = pni_data_id(data, node); |
| node->parent = data->parent; |
| if (parent) { |
| if (!parent->down) { |
| parent->down = pni_data_id(data, node); |
| } |
| parent->children++; |
| } |
| } |
| } else if (parent) { |
| if (parent->down) { |
| node = pn_data_node(data, parent->down); |
| } else { |
| node = pni_data_new(data); |
| if (!node) return NULL; |
| |
| // refresh the pointers in case we grew |
| parent = pn_data_node(data, data->parent); |
| node->prev = 0; |
| node->parent = data->parent; |
| parent->down = pni_data_id(data, node); |
| parent->children++; |
| } |
| } else if (data->size) { |
| node = pn_data_node(data, 1); |
| } else { |
| node = pni_data_new(data); |
| if (!node) return NULL; |
| |
| node->prev = 0; |
| node->parent = 0; |
| } |
| |
| node->down = 0; |
| node->children = 0; |
| node->data = false; |
| node->described = false; |
| node->data_offset = 0; |
| node->data_size = 0; |
| data->current = pni_data_id(data, node); |
| return node; |
| } |
| |
| ssize_t pn_data_encode(pn_data_t *data, char *bytes, size_t size) |
| { |
| pn_encoder_t encoder; |
| pn_encoder_initialize(&encoder); |
| ssize_t r = pn_encoder_encode(&encoder, data, bytes, size); |
| pn_encoder_finalize(&encoder); |
| return r; |
| } |
| |
| ssize_t pn_data_encoded_size(pn_data_t *data) |
| { |
| pn_encoder_t encoder; |
| pn_encoder_initialize(&encoder); |
| ssize_t r = pn_encoder_size(&encoder, data); |
| pn_encoder_finalize(&encoder); |
| return r; |
| } |
| |
| ssize_t pn_data_decode(pn_data_t *data, const char *bytes, size_t size) |
| { |
| pn_decoder_t decoder; |
| pn_decoder_initialize(&decoder); |
| ssize_t r = pn_decoder_decode(&decoder, bytes, size, data); |
| pn_decoder_finalize(&decoder); |
| return r; |
| } |
| |
| int pn_data_put_list(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_LIST; |
| return 0; |
| } |
| |
| int pn_data_put_map(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_MAP; |
| return 0; |
| } |
| |
| int pn_data_put_array(pn_data_t *data, bool described, pn_type_t type) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_ARRAY; |
| node->described = described; |
| node->type = type; |
| return 0; |
| } |
| |
| void pni_data_set_array_type(pn_data_t *data, pn_type_t type) |
| { |
| pni_node_t *array = pni_data_current(data); |
| if (array) array->type = type; |
| } |
| |
| int pn_data_put_described(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_DESCRIBED; |
| return 0; |
| } |
| |
| int pn_data_put_null(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| pni_atom_init(&node->atom, PN_NULL); |
| return 0; |
| } |
| |
| int pn_data_put_bool(pn_data_t *data, bool b) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_BOOL; |
| node->atom.u.as_bool = b; |
| return 0; |
| } |
| |
| int pn_data_put_ubyte(pn_data_t *data, uint8_t ub) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_UBYTE; |
| node->atom.u.as_ubyte = ub; |
| return 0; |
| } |
| |
| int pn_data_put_byte(pn_data_t *data, int8_t b) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_BYTE; |
| node->atom.u.as_byte = b; |
| return 0; |
| } |
| |
| int pn_data_put_ushort(pn_data_t *data, uint16_t us) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_USHORT; |
| node->atom.u.as_ushort = us; |
| return 0; |
| } |
| |
| int pn_data_put_short(pn_data_t *data, int16_t s) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_SHORT; |
| node->atom.u.as_short = s; |
| return 0; |
| } |
| |
| int pn_data_put_uint(pn_data_t *data, uint32_t ui) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_UINT; |
| node->atom.u.as_uint = ui; |
| return 0; |
| } |
| |
| int pn_data_put_int(pn_data_t *data, int32_t i) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_INT; |
| node->atom.u.as_int = i; |
| return 0; |
| } |
| |
| int pn_data_put_char(pn_data_t *data, pn_char_t c) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_CHAR; |
| node->atom.u.as_char = c; |
| return 0; |
| } |
| |
| int pn_data_put_ulong(pn_data_t *data, uint64_t ul) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_ULONG; |
| node->atom.u.as_ulong = ul; |
| return 0; |
| } |
| |
| int pn_data_put_long(pn_data_t *data, int64_t l) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_LONG; |
| node->atom.u.as_long = l; |
| return 0; |
| } |
| |
| int pn_data_put_timestamp(pn_data_t *data, pn_timestamp_t t) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_TIMESTAMP; |
| node->atom.u.as_timestamp = t; |
| return 0; |
| } |
| |
| int pn_data_put_float(pn_data_t *data, float f) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_FLOAT; |
| node->atom.u.as_float = f; |
| return 0; |
| } |
| |
| int pn_data_put_double(pn_data_t *data, double d) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_DOUBLE; |
| node->atom.u.as_double = d; |
| return 0; |
| } |
| |
| int pn_data_put_decimal32(pn_data_t *data, pn_decimal32_t d) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_DECIMAL32; |
| node->atom.u.as_decimal32 = d; |
| return 0; |
| } |
| |
| int pn_data_put_decimal64(pn_data_t *data, pn_decimal64_t d) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_DECIMAL64; |
| node->atom.u.as_decimal64 = d; |
| return 0; |
| } |
| |
| int pn_data_put_decimal128(pn_data_t *data, pn_decimal128_t d) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_DECIMAL128; |
| memmove(node->atom.u.as_decimal128.bytes, d.bytes, 16); |
| return 0; |
| } |
| |
| int pn_data_put_uuid(pn_data_t *data, pn_uuid_t u) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_UUID; |
| memmove(node->atom.u.as_uuid.bytes, u.bytes, 16); |
| return 0; |
| } |
| |
| int pn_data_put_binary(pn_data_t *data, pn_bytes_t bytes) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_BINARY; |
| node->atom.u.as_bytes = bytes; |
| return pni_data_intern_node(data, node); |
| } |
| |
| int pn_data_put_string(pn_data_t *data, pn_bytes_t string) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_STRING; |
| node->atom.u.as_bytes = string; |
| return pni_data_intern_node(data, node); |
| } |
| |
| int pn_data_put_symbol(pn_data_t *data, pn_bytes_t symbol) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom.type = PN_SYMBOL; |
| node->atom.u.as_bytes = symbol; |
| return pni_data_intern_node(data, node); |
| } |
| |
| int pn_data_put_atom(pn_data_t *data, pn_atom_t atom) |
| { |
| pni_node_t *node = pni_data_add(data); |
| if (node == NULL) return PN_OUT_OF_MEMORY; |
| node->atom = atom; |
| return pni_data_intern_node(data, node); |
| } |
| |
| size_t pn_data_get_list(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_LIST) { |
| return node->children; |
| } else { |
| return 0; |
| } |
| } |
| |
| size_t pn_data_get_map(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_MAP) { |
| return node->children; |
| } else { |
| return 0; |
| } |
| } |
| |
| size_t pn_data_get_array(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_ARRAY) { |
| if (node->described) { |
| return node->children - 1; |
| } else { |
| return node->children; |
| } |
| } else { |
| return 0; |
| } |
| } |
| |
| bool pn_data_is_array_described(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_ARRAY) { |
| return node->described; |
| } else { |
| return false; |
| } |
| } |
| |
| pn_type_t pn_data_get_array_type(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_ARRAY) { |
| return node->type; |
| } else { |
| return PN_INVALID; |
| } |
| } |
| |
| bool pn_data_is_described(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| return node && node->atom.type == PN_DESCRIBED; |
| } |
| |
| bool pn_data_is_null(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| return node && node->atom.type == PN_NULL; |
| } |
| |
| bool pn_data_get_bool(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_BOOL) { |
| return node->atom.u.as_bool; |
| } else { |
| return false; |
| } |
| } |
| |
| uint8_t pn_data_get_ubyte(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_UBYTE) { |
| return node->atom.u.as_ubyte; |
| } else { |
| return 0; |
| } |
| } |
| |
| int8_t pn_data_get_byte(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_BYTE) { |
| return node->atom.u.as_byte; |
| } else { |
| return 0; |
| } |
| } |
| |
| uint16_t pn_data_get_ushort(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_USHORT) { |
| return node->atom.u.as_ushort; |
| } else { |
| return 0; |
| } |
| } |
| |
| int16_t pn_data_get_short(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_SHORT) { |
| return node->atom.u.as_short; |
| } else { |
| return 0; |
| } |
| } |
| |
| uint32_t pn_data_get_uint(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_UINT) { |
| return node->atom.u.as_uint; |
| } else { |
| return 0; |
| } |
| } |
| |
| int32_t pn_data_get_int(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_INT) { |
| return node->atom.u.as_int; |
| } else { |
| return 0; |
| } |
| } |
| |
| pn_char_t pn_data_get_char(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_CHAR) { |
| return node->atom.u.as_char; |
| } else { |
| return 0; |
| } |
| } |
| |
| uint64_t pn_data_get_ulong(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_ULONG) { |
| return node->atom.u.as_ulong; |
| } else { |
| return 0; |
| } |
| } |
| |
| int64_t pn_data_get_long(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_LONG) { |
| return node->atom.u.as_long; |
| } else { |
| return 0; |
| } |
| } |
| |
| pn_timestamp_t pn_data_get_timestamp(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_TIMESTAMP) { |
| return node->atom.u.as_timestamp; |
| } else { |
| return 0; |
| } |
| } |
| |
| float pn_data_get_float(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_FLOAT) { |
| return node->atom.u.as_float; |
| } else { |
| return 0; |
| } |
| } |
| |
| double pn_data_get_double(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_DOUBLE) { |
| return node->atom.u.as_double; |
| } else { |
| return 0; |
| } |
| } |
| |
| pn_decimal32_t pn_data_get_decimal32(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_DECIMAL32) { |
| return node->atom.u.as_decimal32; |
| } else { |
| return 0; |
| } |
| } |
| |
| pn_decimal64_t pn_data_get_decimal64(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_DECIMAL64) { |
| return node->atom.u.as_decimal64; |
| } else { |
| return 0; |
| } |
| } |
| |
| pn_decimal128_t pn_data_get_decimal128(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_DECIMAL128) { |
| return node->atom.u.as_decimal128; |
| } else { |
| pn_decimal128_t t = {{0}}; |
| return t; |
| } |
| } |
| |
| pn_uuid_t pn_data_get_uuid(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_UUID) { |
| return node->atom.u.as_uuid; |
| } else { |
| pn_uuid_t t = {{0}}; |
| return t; |
| } |
| } |
| |
| pn_bytes_t pn_data_get_binary(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_BINARY) { |
| return node->atom.u.as_bytes; |
| } else { |
| pn_bytes_t t = {0}; |
| return t; |
| } |
| } |
| |
| pn_bytes_t pn_data_get_string(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_STRING) { |
| return node->atom.u.as_bytes; |
| } else { |
| pn_bytes_t t = {0}; |
| return t; |
| } |
| } |
| |
| pn_bytes_t pn_data_get_symbol(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && node->atom.type == PN_SYMBOL) { |
| return node->atom.u.as_bytes; |
| } else { |
| pn_bytes_t t = {0}; |
| return t; |
| } |
| } |
| |
| pn_bytes_t pn_data_get_bytes(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node && (node->atom.type == PN_BINARY || |
| node->atom.type == PN_STRING || |
| node->atom.type == PN_SYMBOL)) { |
| return node->atom.u.as_bytes; |
| } else { |
| pn_bytes_t t = {0}; |
| return t; |
| } |
| } |
| |
| pn_atom_t pn_data_get_atom(pn_data_t *data) |
| { |
| pni_node_t *node = pni_data_current(data); |
| if (node) { |
| return *((pn_atom_t *) &node->atom); |
| } else { |
| pn_atom_t t = {PN_NULL, {0,}}; |
| return t; |
| } |
| } |
| |
| int pn_data_copy(pn_data_t *data, pn_data_t *src) |
| { |
| pn_data_clear(data); |
| int err = pn_data_append(data, src); |
| pn_data_rewind(data); |
| return err; |
| } |
| |
| int pn_data_append(pn_data_t *data, pn_data_t *src) |
| { |
| return pn_data_appendn(data, src, -1); |
| } |
| |
| int pn_data_appendn(pn_data_t *data, pn_data_t *src, int limit) |
| { |
| int err = 0; |
| int level = 0, count = 0; |
| bool stop = false; |
| pn_handle_t point = pn_data_point(src); |
| pn_data_rewind(src); |
| |
| while (true) { |
| while (!pn_data_next(src)) { |
| if (level > 0) { |
| pn_data_exit(data); |
| pn_data_exit(src); |
| level--; |
| continue; |
| } |
| |
| if (!pn_data_next(src)) { |
| stop = true; |
| } |
| break; |
| } |
| |
| if (stop) break; |
| |
| if (level == 0 && count == limit) |
| break; |
| |
| pn_type_t type = pn_data_type(src); |
| switch (type) { |
| case PN_NULL: |
| err = pn_data_put_null(data); |
| if (level == 0) count++; |
| break; |
| case PN_BOOL: |
| err = pn_data_put_bool(data, pn_data_get_bool(src)); |
| if (level == 0) count++; |
| break; |
| case PN_UBYTE: |
| err = pn_data_put_ubyte(data, pn_data_get_ubyte(src)); |
| if (level == 0) count++; |
| break; |
| case PN_BYTE: |
| err = pn_data_put_byte(data, pn_data_get_byte(src)); |
| if (level == 0) count++; |
| break; |
| case PN_USHORT: |
| err = pn_data_put_ushort(data, pn_data_get_ushort(src)); |
| if (level == 0) count++; |
| break; |
| case PN_SHORT: |
| err = pn_data_put_short(data, pn_data_get_short(src)); |
| if (level == 0) count++; |
| break; |
| case PN_UINT: |
| err = pn_data_put_uint(data, pn_data_get_uint(src)); |
| if (level == 0) count++; |
| break; |
| case PN_INT: |
| err = pn_data_put_int(data, pn_data_get_int(src)); |
| if (level == 0) count++; |
| break; |
| case PN_CHAR: |
| err = pn_data_put_char(data, pn_data_get_char(src)); |
| if (level == 0) count++; |
| break; |
| case PN_ULONG: |
| err = pn_data_put_ulong(data, pn_data_get_ulong(src)); |
| if (level == 0) count++; |
| break; |
| case PN_LONG: |
| err = pn_data_put_long(data, pn_data_get_long(src)); |
| if (level == 0) count++; |
| break; |
| case PN_TIMESTAMP: |
| err = pn_data_put_timestamp(data, pn_data_get_timestamp(src)); |
| if (level == 0) count++; |
| break; |
| case PN_FLOAT: |
| err = pn_data_put_float(data, pn_data_get_float(src)); |
| if (level == 0) count++; |
| break; |
| case PN_DOUBLE: |
| err = pn_data_put_double(data, pn_data_get_double(src)); |
| if (level == 0) count++; |
| break; |
| case PN_DECIMAL32: |
| err = pn_data_put_decimal32(data, pn_data_get_decimal32(src)); |
| if (level == 0) count++; |
| break; |
| case PN_DECIMAL64: |
| err = pn_data_put_decimal64(data, pn_data_get_decimal64(src)); |
| if (level == 0) count++; |
| break; |
| case PN_DECIMAL128: |
| err = pn_data_put_decimal128(data, pn_data_get_decimal128(src)); |
| if (level == 0) count++; |
| break; |
| case PN_UUID: |
| err = pn_data_put_uuid(data, pn_data_get_uuid(src)); |
| if (level == 0) count++; |
| break; |
| case PN_BINARY: |
| err = pn_data_put_binary(data, pn_data_get_binary(src)); |
| if (level == 0) count++; |
| break; |
| case PN_STRING: |
| err = pn_data_put_string(data, pn_data_get_string(src)); |
| if (level == 0) count++; |
| break; |
| case PN_SYMBOL: |
| err = pn_data_put_symbol(data, pn_data_get_symbol(src)); |
| if (level == 0) count++; |
| break; |
| case PN_DESCRIBED: |
| err = pn_data_put_described(data); |
| if (level == 0) count++; |
| if (err) { pn_data_restore(src, point); return err; } |
| pn_data_enter(data); |
| pn_data_enter(src); |
| level++; |
| break; |
| case PN_ARRAY: |
| err = pn_data_put_array(data, pn_data_is_array_described(src), |
| pn_data_get_array_type(src)); |
| if (level == 0) count++; |
| if (err) { pn_data_restore(src, point); return err; } |
| pn_data_enter(data); |
| pn_data_enter(src); |
| level++; |
| break; |
| case PN_LIST: |
| err = pn_data_put_list(data); |
| if (level == 0) count++; |
| if (err) { pn_data_restore(src, point); return err; } |
| pn_data_enter(data); |
| pn_data_enter(src); |
| level++; |
| break; |
| case PN_MAP: |
| err = pn_data_put_map(data); |
| if (level == 0) count++; |
| if (err) { pn_data_restore(src, point); return err; } |
| pn_data_enter(data); |
| pn_data_enter(src); |
| level++; |
| break; |
| default: |
| break; |
| } |
| |
| if (err) { pn_data_restore(src, point); return err; } |
| } |
| |
| pn_data_restore(src, point); |
| |
| return 0; |
| } |