This is an automated email from the ASF dual-hosted git repository.

astitcher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-proton.git

commit a4fa8a35f9dc8b6a8ae657bd6853d8cb6a47cc35
Author: Andrew Stitcher <[email protected]>
AuthorDate: Fri Sep 17 17:38:43 2021 -0400

    PROTON-2448: Add code to dump AMQP values direct from a bytestream
    
    All AMQP types except single unicode chars are now supported.
---
 c/CMakeLists.txt                       |   1 +
 c/src/core/consumers.h                 | 164 +++++++++
 c/src/core/framing.c                   |  53 +--
 c/src/core/framing.h                   |   6 +-
 c/src/core/util.h                      |  38 +++
 c/src/core/value_dump.c                | 600 +++++++++++++++++++++++++++++++++
 c/src/core/{framing.h => value_dump.h} |  29 +-
 7 files changed, 819 insertions(+), 72 deletions(-)

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

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to