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

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

commit 717d1bde2dc95fc1b259b9b5eaaec1647e73b4fd
Author: Andrew Stitcher <[email protected]>
AuthorDate: Mon Aug 17 19:27:53 2026 -0400

    PROTON-2954: Further reduce pn_data_t memory use
    
    Reduced the per node memory use to 16 bytes from 24 bytes. This is a
    4.5 times reductions over the original 72 bytes. This now mitigates
    significantly the maximum node storage that could occur to 1MiB.
    Practically the intern buffer storage should always be limited by the
    size of the raw protocol bytes that are being decoded.
    
    Achieved by:
    - Moving navigation links into the specific node type that uses them.
    - Removing members that could be computed.
    - Using spare packing space for the base type of an AMQP array.
    - Splitting arrays into regular and arrays of described types.
    
    Surprisingly this hasn't made the code significantly more complex and
    has simplified a few things.
---
 c/src/core/codec.c   | 156 ++++++++++++++++++++++-----------------
 c/src/core/data.h    | 202 ++++++++++++++++++++++++++++++++++++++++++---------
 c/src/core/decoder.c |   4 +-
 c/src/core/encoder.c |  39 ++++++----
 4 files changed, 288 insertions(+), 113 deletions(-)

diff --git a/c/src/core/codec.c b/c/src/core/codec.c
index 3745ab71b..75cc49e04 100644
--- a/c/src/core/codec.c
+++ b/c/src/core/codec.c
@@ -40,9 +40,16 @@
 #include "logger_private.h"
 #include "memory.h"
 
+// Convert internal types to external API types
+static inline pn_type_t pni_type_external(uint8_t internal_type)
+{
+  if (internal_type == PN_ARRAY_DESCRIBED) return PN_ARRAY;
+  return internal_type;
+}
+
 const char *pn_type_name(pn_type_t type)
 {
-  switch (type)
+  switch ((uint8_t)type)
   {
   case PN_NULL: return "PN_NULL";
   case PN_BOOL: return "PN_BOOL";
@@ -67,6 +74,7 @@ const char *pn_type_name(pn_type_t type)
   case PN_SYMBOL: return "PN_SYMBOL";
   case PN_DESCRIBED: return "PN_DESCRIBED";
   case PN_ARRAY: return "PN_ARRAY";
+  case PN_ARRAY_DESCRIBED: return "PN_ARRAY_DESCRIBED";
   case PN_LIST: return "PN_LIST";
   case PN_MAP: return "PN_MAP";
   default: break;
@@ -90,7 +98,7 @@ static const pn_fields_t *pni_node_fields(pn_data_t *data, 
pni_node_t *node)
   if (!node) return NULL;
   if (node->type != PN_DESCRIBED) return NULL;
 
-  pni_node_t *descriptor = pn_data_node(data, node->down);
+  pni_node_t *descriptor = pn_data_node(data, node->u.as_described.down);
 
   if (!descriptor || descriptor->type != PN_ULONG) {
     return NULL;
@@ -291,7 +299,7 @@ int pni_inspect_enter(void *ctx, pn_data_t *data, 
pni_node_t *node)
     return 0;
   case PN_ARRAY:
     // XXX: need to fix for described arrays
-    pn_fixed_string_addf(str, "@%s[", pn_type_name(node->u.as_array.type));
+    pn_fixed_string_addf(str, "@%s[", pn_type_name(node->array_type));
     return 0;
   case PN_LIST:
     pn_fixed_string_addf(str, "[");
@@ -568,6 +576,7 @@ static int pni_normalize_multiple(pn_data_t *data, 
pn_data_t *src) {
   return err;
 }
 
+static int pni_data_put_deferred(pn_data_t *data, pn_type_t type);
 
 /* Format codes:
    code: AMQP-type (arguments)
@@ -652,7 +661,7 @@ int pn_data_vfill(pn_data_t *data, const char *fmt, va_list 
ap)
       break;
     case 'Z':                   /* encode binary, must not be NULL */
       {
-       // For maximum portability, caller must pass these as two separate 
args, not a single struct
+        // 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));
@@ -660,7 +669,7 @@ int pn_data_vfill(pn_data_t *data, const char *fmt, va_list 
ap)
       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
+        // 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) {
@@ -689,14 +698,14 @@ int pn_data_vfill(pn_data_t *data, const char *fmt, 
va_list ap)
       break;
     case 'D':
       /* The next 2 args are the descriptor, value for a described value. */
-      err = pn_data_put_described(data);
+      err = pni_data_put_deferred(data, PN_DESCRIBED);
       pn_data_enter(data);
       break;
     case 'T':                   /* Set type of open array */
       {
         pni_node_t *parent = pn_data_node(data, data->parent);
         if (parent->type == PN_ARRAY) {
-          parent->u.as_array.type = (uint8_t) va_arg(ap, int);
+          parent->array_type = (uint8_t) va_arg(ap, int);
         } else {
           return pn_error_format(pni_data_error(data), PN_ERR, "naked type");
         }
@@ -734,7 +743,7 @@ int pn_data_vfill(pn_data_t *data, const char *fmt, va_list 
ap)
       break;
     case '?':
       if (!va_arg(ap, int)) {
-        err = pn_data_put_null(data);
+        err = pni_data_put_deferred(data, PN_NULL);
         if (err) return err;
         pn_data_enter(data);
       }
@@ -804,22 +813,27 @@ int pn_data_vfill(pn_data_t *data, const char *fmt, 
va_list ap)
     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->type == PN_DESCRIBED && parent->children == 2) {
-        current->u.as_list.described = true;
-        pn_data_exit(data);
-        current = pn_data_node(data, data->current);
-        parent = pn_data_node(data, data->parent);
-      } else if (parent->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;
+      if (parent->type == PN_DEFER) {
+        if (parent->u.as_deferred.type == PN_DESCRIBED && 
parent->u.as_deferred.children_count == 2) {
+          pn_data_exit(data);
+          parent = pn_data_node(data, data->parent);
+          pni_node_t *current = pn_data_node(data, data->current);
+          pni_nid_t down = current->u.as_deferred.down;
+          uint16_t count = current->u.as_deferred.children_count;
+          current->type = PN_DESCRIBED;
+          current->u.as_described.down = down;
+          current->u.as_described.children_count = count;
+          continue;
+        } else if (parent->u.as_deferred.type == PN_NULL && 
parent->u.as_deferred.children_count == 1) {
+          pn_data_exit(data);
+          parent = pn_data_node(data, data->parent);
+          pni_node_t *current = pn_data_node(data, data->current);
+          current->type = PN_NULL;
+          continue;
+        }
       }
+      break;
     }
   }
 
@@ -1288,9 +1302,9 @@ static pni_node_t *pni_data_new(pn_data_t *data)
     return NULL;
   }
   pni_node_t *node = pn_data_node(data, ++(data->size));
+  node->type = PN_NULL;
   node->next = 0;
-  node->down = 0;
-  node->children = 0;
+  node->array_type = 0;
   return node;
 }
 
@@ -1352,7 +1366,7 @@ static pni_node_t *pni_data_peek(pn_data_t *data)
 
   pni_node_t *parent = pn_data_node(data, data->parent);
   if (parent) {
-    return pn_data_node(data, parent->down);
+    return pn_data_node(data, pni_node_get_down(parent));
   }
 
   return NULL;
@@ -1366,8 +1380,8 @@ bool pn_data_next(pn_data_t *data)
 
   if (current) {
     next = current->next;
-  } else if (parent && parent->down) {
-    next = parent->down;
+  } else if (parent && pni_node_get_down(parent)) {
+    next = pni_node_get_down(parent);
   } else if (!parent && data->size) {
     next = 1;
   } else {
@@ -1406,8 +1420,8 @@ int pni_data_traverse(pn_data_t *data,
     if (err) return err;
 
     size_t next = 0;
-    if (node->down) {
-      next = node->down;
+    if (pni_node_get_down(node)) {
+      next = pni_node_get_down(node);
     } else if (node->next) {
       err = exit(ctx, data, node);
       if (err) return err;
@@ -1437,7 +1451,7 @@ pn_type_t pn_data_type(pn_data_t *data)
 {
   pni_node_t *node = pni_data_current(data);
   if (node) {
-    return node->type;
+    return pni_type_external(node->type);
   } else {
     return PN_INVALID;
   }
@@ -1457,7 +1471,7 @@ size_t pn_data_siblings(pn_data_t *data)
 {
   pni_node_t *node = pn_data_node(data, data->parent);
   if (node) {
-    return node->children;
+    return pni_node_get_children(node);
   } else {
     return 0;
   }
@@ -1532,8 +1546,8 @@ void pn_data_dump(pn_data_t *data)
            i + 1, (size_t) node->prev,
            (size_t) node->next,
            (size_t) node->parent,
-           (size_t) node->down,
-           (size_t) node->children,
+           (size_t) pni_node_get_down(node),
+           (size_t) pni_node_get_children(node),
            pn_type_name(node->type), buf);
   }
 }
@@ -1558,15 +1572,15 @@ static pni_node_t *pni_data_add(pn_data_t *data)
       current->next = pni_data_id(data, node);
       node->parent = data->parent;
       if (parent) {
-        if (!parent->down) {
-          parent->down = pni_data_id(data, node);
+        if (!pni_node_get_down(parent)) {
+          pni_node_set_down(parent, pni_data_id(data, node));
         }
-        parent->children++;
+        pni_node_inc_children(parent);
       }
     }
   } else if (parent) {
-    if (parent->down) {
-      node = pn_data_node(data, parent->down);
+    if (pni_node_get_down(parent)) {
+      node = pn_data_node(data, pni_node_get_down(parent));
     } else {
       node = pni_data_new(data);
       if (!node) return NULL;
@@ -1575,8 +1589,8 @@ static pni_node_t *pni_data_add(pn_data_t *data)
       parent = pn_data_node(data, data->parent);
       node->prev = 0;
       node->parent = data->parent;
-      parent->down = pni_data_id(data, node);
-      parent->children++;
+      pni_node_set_down(parent, pni_data_id(data, node));
+      pni_node_inc_children(parent);
     }
   } else if (data->size) {
     node = pn_data_node(data, 1);
@@ -1588,8 +1602,6 @@ static pni_node_t *pni_data_add(pn_data_t *data)
     node->parent = 0;
   }
 
-  node->down = 0;
-  node->children = 0;
   memset(&node->u, 0, sizeof(node->u));
   data->current = pni_data_id(data, node);
   return node;
@@ -1622,12 +1634,22 @@ ssize_t pn_data_decode(pn_data_t *data, const char 
*bytes, size_t size)
   return r;
 }
 
+static int pni_data_put_deferred(pn_data_t *data, pn_type_t type)
+{
+  pni_node_t *node = pni_data_add(data);
+  if (node == NULL) return PN_OUT_OF_MEMORY;
+  node->type = PN_DEFER;
+  node->u.as_deferred.type = (uint8_t) type;
+  return 0;
+}
+
 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->type = PN_LIST;
-  node->u.as_list.described = false;
+  node->u.as_list.down = 0;
+  node->u.as_list.children_count = 0;
   return 0;
 }
 
@@ -1636,6 +1658,8 @@ 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->type = PN_MAP;
+  node->u.as_map.down = 0;
+  node->u.as_map.children_count = 0;
   return 0;
 }
 
@@ -1643,16 +1667,19 @@ 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->type = PN_ARRAY;
-  node->u.as_array.described = described;
-  node->u.as_array.type = (uint8_t) type;
+  node->type = described ? PN_ARRAY_DESCRIBED : PN_ARRAY;
+  node->array_type = (uint8_t)type;
+  node->u.as_array.down = 0;
+  node->u.as_array.children_count = 0;
   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->u.as_array.type = (uint8_t) type;
+  if (array) {
+    array->array_type = (uint8_t)type;
+  }
 }
 
 int pn_data_put_described(pn_data_t *data)
@@ -1660,6 +1687,8 @@ 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->type = PN_DESCRIBED;
+  node->u.as_described.down = 0;
+  node->u.as_described.children_count = 0;
   return 0;
 }
 
@@ -1874,7 +1903,7 @@ size_t pn_data_get_list(pn_data_t *data)
 {
   pni_node_t *node = pni_data_current(data);
   if (node && node->type == PN_LIST) {
-    return node->children;
+    return node->u.as_list.children_count;
   } else {
     return 0;
   }
@@ -1884,7 +1913,7 @@ size_t pn_data_get_map(pn_data_t *data)
 {
   pni_node_t *node = pni_data_current(data);
   if (node && node->type == PN_MAP) {
-    return node->children;
+    return node->u.as_map.children_count;
   } else {
     return 0;
   }
@@ -1893,32 +1922,28 @@ size_t pn_data_get_map(pn_data_t *data)
 size_t pn_data_get_array(pn_data_t *data)
 {
   pni_node_t *node = pni_data_current(data);
-  if (node && node->type == PN_ARRAY) {
-    if (node->u.as_array.described) {
-      return node->children - 1;
-    } else {
-      return node->children;
-    }
-  } else {
-    return 0;
+  if (!node) return 0;
+
+  if (node->type == PN_ARRAY_DESCRIBED) {
+    return node->u.as_array.children_count - 1;  // Descriptor doesn't count
+  }
+  if (node->type == PN_ARRAY) {
+    return node->u.as_array.children_count;
   }
+  return 0;
 }
 
 bool pn_data_is_array_described(pn_data_t *data)
 {
   pni_node_t *node = pni_data_current(data);
-  if (node && node->type == PN_ARRAY) {
-    return node->u.as_array.described;
-  } else {
-    return false;
-  }
+  return node && node->type == PN_ARRAY_DESCRIBED;
 }
 
 pn_type_t pn_data_get_array_type(pn_data_t *data)
 {
   pni_node_t *node = pni_data_current(data);
-  if (node && node->type == PN_ARRAY) {
-    return node->u.as_array.type;
+  if (node && (node->type == PN_ARRAY || node->type == PN_ARRAY_DESCRIBED)) {
+    return node->array_type;
   } else {
     return PN_INVALID;
   }
@@ -2158,7 +2183,8 @@ pn_atom_t pn_data_get_atom(pn_data_t *data)
   if (!node) {
     return (pn_atom_t){PN_NULL};
   }
-  pn_atom_t t = {node->type};
+
+  pn_atom_t t = {pni_type_external(node->type)};
   switch (node->type) {
   case PN_BINARY: case PN_STRING: case PN_SYMBOL:
     t.u.as_bytes = pni_node_bytes(data, node);
diff --git a/c/src/core/data.h b/c/src/core/data.h
index 946bb086d..4dee470e9 100644
--- a/c/src/core/data.h
+++ b/c/src/core/data.h
@@ -31,6 +31,9 @@ typedef uint16_t pni_nid_t;
 #define PNI_NID_MAX ((pni_nid_t)-1)
 #define PNI_INTERN_MINSIZE 64
 
+#define PN_ARRAY_DESCRIBED 26  // Internal type: described array
+#define PN_DEFER 27            // Internal type: node used only in 
pn_data_fill/vfill
+
 /*
  * Value payload for a pni_node_t.
  *
@@ -71,50 +74,68 @@ typedef union {
     uint32_t      size;           /* byte count (always 16 for 
decimal128/uuid) */
   }               as_bytes;
   struct {
-    uint32_t      start;          /* encoder scratch: output offset of size 
field */
-    bool          described;      /* true if first child is a descriptor */
-    uint8_t       type;           /* element type (pn_type_t fits in uint8_t: 
values 1-25) */
-    /* 2 implicit padding bytes */
-  }               as_array;
+    pni_nid_t     down;            // offset 0: 2 bytes
+    pni_nid_t     children_count;  // offset 2: 2 bytes
+    uint8_t       type;           /* deferred type */
+  }               as_deferred;
+
+  // Compound types include navigation
+  struct {
+    pni_nid_t     down;            // offset 0: 2 bytes
+    pni_nid_t     children_count;  // offset 2: 2 bytes
+    uint32_t      start;           // offset 4: 4 bytes
+  }               as_array;        // 8 bytes
+
+  struct {
+    pni_nid_t     down;            // offset 0: 2 bytes
+    pni_nid_t     children_count;  // offset 2: 2 bytes
+    uint32_t      start;           // offset 4: 4 bytes
+  }               as_list;         // 8 bytes
+
   struct {
-    uint32_t      start;          /* encoder scratch: output offset of size 
field */
-    bool          described;      /* true if body of a described composite */
-    /* 3 implicit padding bytes */
-  }               as_list;
+    pni_nid_t     down;            // offset 0: 2 bytes
+    pni_nid_t     children_count;  // offset 2: 2 bytes
+    uint32_t      start;           // offset 4: 4 bytes
+  }               as_map;          // 8 bytes
+
   struct {
-    uint32_t      start;          /* encoder scratch: output offset of size 
field */
-    /* 4 implicit padding bytes */
-  }               as_map;
+    pni_nid_t     down;            // offset 0: 2 bytes
+    pni_nid_t     children_count;  // offset 2: 2 bytes
+  }               as_described;    // 4 bytes (union is 8)
 } pni_node_payload_t;
 
 /*
- * Layout (64-bit): 24 bytes.
+ * Layout (64-bit): 16 bytes.
  *
- *  offset  0  type        (4)  value type tag
- *  offset  4  next        (2)  sibling link
- *  offset  6  prev        (2)  sibling link
- *  offset  8  down        (2)
- *  offset 10  parent      (2)
- *  offset 12  children    (2)
- *  offset 14  <2 implicit alignment bytes before u>
- *  offset 16  u           (8)  value payload (8-byte aligned)
+ *  offset  0  type        (1)  internal value type tag
+ *  offset  1  array_type  (1)  array element type (when applicable)
+ *  offset  2  next        (2)  sibling link
+ *  offset  4  prev        (2)  sibling link
+ *  offset  6  parent      (2)  parent link
+ *  offset  8  u           (8)  value payload (8-byte aligned)
  *
- * Note that there is still a bit of possibility to reduce this further:
- * The type takes 4 bytes, but need only take 1; there is 2 bytes of padding 
still;
- * I'm pretty sure that we could do away with one of the navigation links 
(having down
- * and children seems redundant). However unless we can get it to 16 bytes 
there is
- * little point as it must be 8 byte aligned anyway (because of the int64_t in 
the union).
+ * Compound types (PN_ARRAY, PN_LIST, PN_MAP, PN_DESCRIBED) store down/children
+ * in their union structure. Scalar types have no children, so down/children 
are
+ * not needed for them.
  */
 typedef struct {
-  pn_type_t           type;
-  pni_nid_t           next;
-  pni_nid_t           prev;
-  pni_nid_t           down;
-  pni_nid_t           parent;
-  pni_nid_t           children;
-  pni_node_payload_t  u;
+  uint8_t             type;        // offset 0: 1 byte
+  uint8_t             array_type;  // offset 1: 1 byte
+  pni_nid_t           next;        // offset 2: 2 bytes
+  pni_nid_t           prev;        // offset 4: 2 bytes
+  pni_nid_t           parent;      // offset 6: 2 bytes
+  pni_node_payload_t  u;           // offset 8: 8 bytes (8-byte aligned)
 } pni_node_t;
 
+#ifdef __cplusplus
+static_assert(sizeof(pni_node_t) == 16, "pni_node_t must be 16 bytes");
+static_assert(sizeof(pni_node_payload_t) == 8, "union must be 8 bytes");
+#else
+/* C99 compile-time size assertions */
+typedef char pni_node_size_check[sizeof(pni_node_t) == 16 ? 1 : -1];
+typedef char pni_payload_size_check[sizeof(pni_node_payload_t) == 8 ? 1 : -1];
+#endif
+
 struct pn_data_t {
   pni_node_t *nodes;
   pn_buffer_t *buf;
@@ -146,6 +167,121 @@ static inline pni_node_t * pn_data_node(pn_data_t *data, 
pni_nid_t nd)
   return nd ? (data->nodes + nd - 1) : NULL;
 }
 
+static inline pni_nid_t pni_node_get_down(pni_node_t *node)
+{
+  if (!node) return 0;
+  switch (node->type) {
+    case PN_ARRAY:
+    case PN_ARRAY_DESCRIBED:
+      return node->u.as_array.down;
+    case PN_LIST:
+      return node->u.as_list.down;
+    case PN_MAP:
+      return node->u.as_map.down;
+    case PN_DESCRIBED:
+      return node->u.as_described.down;
+    case PN_DEFER:
+      return node->u.as_deferred.down;
+    default:
+      return 0;  // Scalar types have no children
+  }
+}
+
+static inline void pni_node_set_down(pni_node_t *node, pni_nid_t down)
+{
+  if (!node) return;
+  switch (node->type) {
+    case PN_ARRAY:
+    case PN_ARRAY_DESCRIBED:
+      node->u.as_array.down = down;
+      break;
+    case PN_LIST:
+      node->u.as_list.down = down;
+      break;
+    case PN_MAP:
+      node->u.as_map.down = down;
+      break;
+    case PN_DESCRIBED:
+      node->u.as_described.down = down;
+      break;
+    case PN_DEFER:
+      node->u.as_deferred.down = down;
+      break;
+    default:
+      break;  // Scalar types - do nothing
+  }
+}
+
+static inline pni_nid_t pni_node_get_children(pni_node_t *node)
+{
+  if (!node) return 0;
+  switch (node->type) {
+    case PN_ARRAY:
+    case PN_ARRAY_DESCRIBED:
+      return node->u.as_array.children_count;
+    case PN_LIST:
+      return node->u.as_list.children_count;
+    case PN_MAP:
+      return node->u.as_map.children_count;
+    case PN_DESCRIBED:
+      return node->u.as_described.children_count;
+    case PN_DEFER:
+      return node->u.as_deferred.children_count;
+    default:
+      return 0;
+  }
+}
+
+static inline void pni_node_set_children(pni_node_t *node, pni_nid_t count)
+{
+  if (!node) return;
+  switch (node->type) {
+    case PN_ARRAY:
+    case PN_ARRAY_DESCRIBED:
+      node->u.as_array.children_count = count;
+      break;
+    case PN_LIST:
+      node->u.as_list.children_count = count;
+      break;
+    case PN_MAP:
+      node->u.as_map.children_count = count;
+      break;
+    case PN_DESCRIBED:
+      node->u.as_described.children_count = count;
+      break;
+    case PN_DEFER:
+      node->u.as_deferred.children_count = count;
+      break;
+    default:
+      break;
+  }
+}
+
+static inline void pni_node_inc_children(pni_node_t *node)
+{
+  if (!node) return;
+  switch (node->type) {
+    case PN_ARRAY:
+    case PN_ARRAY_DESCRIBED:
+      node->u.as_array.children_count++;
+      break;
+    case PN_LIST:
+      node->u.as_list.children_count++;
+      break;
+    case PN_MAP:
+      node->u.as_map.children_count++;
+      break;
+    case PN_DESCRIBED:
+      node->u.as_described.children_count++;
+      break;
+    case PN_DEFER:
+      node->u.as_deferred.children_count++;
+      break;
+    default:
+      break;
+  }
+}
+
 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),
diff --git a/c/src/core/decoder.c b/c/src/core/decoder.c
index fbda81495..64d471fe8 100644
--- a/c/src/core/decoder.c
+++ b/c/src/core/decoder.c
@@ -23,6 +23,7 @@
 #include <proton/codec.h>
 #include "encodings.h"
 #include "decoder.h"
+#include "data.h"
 
 #include <string.h>
 
@@ -455,7 +456,8 @@ static int pni_decoder_decode_type(pn_decoder_t *decoder, 
pn_data_t *data, uint8
     return 0;
   }
 
-  if (pni_data_parent_type(data) != PN_ARRAY) {
+  pn_type_t parent_type = pni_data_parent_type(data);
+  if (parent_type != PN_ARRAY && parent_type != PN_ARRAY_DESCRIBED) {
     err = pn_data_put_described(data);
     if (err) return err;
 
diff --git a/c/src/core/encoder.c b/c/src/core/encoder.c
index b312fc4fb..b2a6c4136 100644
--- a/c/src/core/encoder.c
+++ b/c/src/core/encoder.c
@@ -137,6 +137,8 @@ static uint8_t pn_node2code(pn_encoder_t *encoder, 
pni_node_t *node)
     } else {
       return PNE_VBIN32;
     }
+  case PN_ARRAY_DESCRIBED:
+    return PNE_ARRAY32;
   default:
     return pn_type2code(encoder, node->type);
   }
@@ -225,23 +227,29 @@ static inline void pn_encoder_writev32(pn_encoder_t 
*encoder, const pn_bytes_t *
 
 /* True if node is an element of an array - not the descriptor. */
 static bool pn_is_in_array(pn_data_t *data, pni_node_t *parent, pni_node_t 
*node) {
-  return (parent && parent->type == PN_ARRAY) /* In array */
-    && !(parent->u.as_array.described && !node->prev); /* Not the descriptor */
+  if (!parent) return false;
+  if (parent->type == PN_ARRAY) return true;
+  if (parent->type == PN_ARRAY_DESCRIBED) return node->prev != 0;
+  return false;
 }
 
 /** True if node is the first element of an array, not the descriptor.
  *@pre pn_is_in_array(data, parent, node)
  */
 static bool pn_is_first_in_array(pn_data_t *data, pni_node_t *parent, 
pni_node_t *node) {
-  if (!node->prev) return !parent->u.as_array.described; /* First node */
-  return parent->u.as_array.described && (!pn_data_node(data, 
node->prev)->prev);
+  if (!node->prev) return parent->type != PN_ARRAY_DESCRIBED;
+  return (parent->type == PN_ARRAY_DESCRIBED) && (!pn_data_node(data, 
node->prev)->prev);
 }
 
 /** True if node is in a described list - not the descriptor.
  *  - In this case we can omit trailing nulls
  */
 static bool pn_is_in_described_list(pn_data_t *data, pni_node_t *parent, 
pni_node_t *node) {
-  return parent && parent->type == PN_LIST && parent->u.as_list.described;
+  if (!parent || parent->type != PN_LIST) return false;
+  pni_node_t *grandparent = pn_data_node(data, parent->parent);
+  if (!grandparent || grandparent->type != PN_DESCRIBED) return false;
+  // List is the body (value) if it's the second child (has prev sibling)
+  return parent->prev != 0;
 }
 
 typedef union {
@@ -261,7 +269,7 @@ static int pni_encoder_enter(void *ctx, pn_data_t *data, 
pni_node_t *node)
 
   /** In an array we don't write the code before each element, only the first. 
*/
   if (pn_is_in_array(data, parent, node)) {
-    code = pn_type2code(encoder, parent->u.as_array.type);
+    code = pn_type2code(encoder, parent->array_type);
     if (pn_is_first_in_array(data, parent, node)) {
       pn_encoder_writef8(encoder, code);
     }
@@ -323,21 +331,22 @@ static int pni_encoder_enter(void *ctx, pn_data_t *data, 
pni_node_t *node)
     node->u.as_array.start = (uint32_t) encoder->position;
     // we'll backfill the size on exit
     encoder->position += 4;
-    pn_encoder_writef32(encoder, node->u.as_array.described ? node->children - 
1 : node->children);
-    if (node->u.as_array.described)
+    bool described = (node->type == PN_ARRAY_DESCRIBED);
+    pn_encoder_writef32(encoder, described ? node->u.as_array.children_count - 
1 : node->u.as_array.children_count);
+    if (described)
       pn_encoder_writef8(encoder, 0);
     return 0;
   case PNE_LIST32:
     node->u.as_list.start = (uint32_t) encoder->position;
     // we'll backfill the size later
     encoder->position += 4;
-    pn_encoder_writef32(encoder, node->children);
+    pn_encoder_writef32(encoder, node->u.as_list.children_count);
     return 0;
   case PNE_MAP32:
     node->u.as_map.start = (uint32_t) encoder->position;
     // we'll backfill the size later
     encoder->position += 4;
-    pn_encoder_writef32(encoder, node->children);
+    pn_encoder_writef32(encoder, node->u.as_map.children_count);
     return 0;
   default:
     return pn_error_format(pn_data_error(data), PN_ERR, "unrecognized 
encoding: %u", code);
@@ -353,7 +362,7 @@ static int pni_encoder_exit(void *ctx, pn_data_t *data, 
pni_node_t *node)
 
   // Special case 0 length list, but not as element in an array
   pni_node_t *parent = pn_data_node(data, node->parent);
-  if (node->type==PN_LIST && node->children-encoder->null_count==0 && 
!pn_is_in_array(data, parent, node)) {
+  if (node->type==PN_LIST && 
node->u.as_list.children_count-encoder->null_count==0 && !pn_is_in_array(data, 
parent, node)) {
     encoder->position = node->u.as_list.start - 1; // position of list opcode
     pn_encoder_writef8(encoder, PNE_LIST0);
     encoder->null_count = 0;
@@ -362,8 +371,10 @@ static int pni_encoder_exit(void *ctx, pn_data_t *data, 
pni_node_t *node)
 
   switch (node->type) {
   case PN_ARRAY:
-    if ((node->u.as_array.described && node->children == 1) || 
(!node->u.as_array.described && node->children == 0)) {
-      pn_encoder_writef8(encoder, pn_type2code(encoder, 
node->u.as_array.type));
+  case PN_ARRAY_DESCRIBED:
+    if ((node->type == PN_ARRAY_DESCRIBED && node->u.as_array.children_count 
== 1) ||
+        (node->type == PN_ARRAY && node->u.as_array.children_count == 0)) {
+      pn_encoder_writef8(encoder, pn_type2code(encoder, node->array_type));
     }
     PN_FALLTHROUGH;
   case PN_LIST:
@@ -377,7 +388,7 @@ static int pni_encoder_exit(void *ctx, pn_data_t *data, 
pni_node_t *node)
     pn_encoder_writef32(encoder, size);
     // Adjust count
     if (encoder->null_count) {
-      pn_encoder_writef32(encoder, node->children-encoder->null_count);
+      pn_encoder_writef32(encoder, 
pni_node_get_children(node)-encoder->null_count);
     }
     encoder->position = pos;
     encoder->null_count = 0;


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

Reply via email to