Huge thanks to our Platinum Members Endace and LiveAction,
and our Silver Member Veeam, for supporting the Wireshark Foundation and project.

Wireshark-dev: [Wireshark-dev] [PATCH][UPDATE] ptvcursor : one step further?

From: Sebastien Tandel <sebastien@xxxxxxxxx>
Date: Tue, 09 Jan 2007 02:48:51 +0100
Hi,

  I've updated the patch for ptvcursor taking into account the ideas of Guy.

Features supported by ptvcursor :
  - multiple levels (256 max.), allocation per 8 levels.
  - Two new functions creating an item in the tree and pushing a subtree at the same time. These two functions accept an undefined length (SUBTREE_UNDEFINED_LENGTH). The length of the item is set once the subtree is pop'ed.
    1) ptvcursor_add_with_subtree
    2) ptvcursor_add_text_with_subtree


If it sounds good I'll do a section for the README.developer.


Regards,

Sebastien Tandel



> > What is done hereabove when playing with subtrees is like acting as
> > push/pop actions => then add this feature in the ptvcursor API. It can
> > be done in the following way :
> > - add a field in the ptvcursor structure which retains the push/pop of
> > subtrees (fixed-length table, only one level of push/pop, malloc/free?)
>   

I'd vote for multiple levels, with, perhaps, an allocator with its own 
free list, so that popping adds to the beginning of the free list.

> > - add two functions : ptvcursor_push_subtree(cursor, it, ett_sub),
> > ptvcursor_pop_subtree(cursor)
>   

I might also suggest that, when you have an item with a subtree, and 
that item doesn't correspond to a primitive type (if, for example, you 
have an item that's a 32-bit flag word, with the subtree items being 
individual bits in the flag word, the item is a primitive type, namely a 
FT_UINT32; however, if the item is an array or structure, it's not a 
primitive type, so it doesn't have a value and might not even have a 
fixed length), there should be a way of creating an item with unknown 
length and with a subtree, and when you pop (or "close") the subtree, 
that'd set the length of the item.

The "create item with subtree" operation would create the item without 
advancing the cursor, and push a subtree; that might be done as a 
combination of ptvcursor_add_no_advance() and ptvcursor_push_subtree(). 
  The "close subtree" operation would do a ptvcursor_pop_subtree() and 
then set the length of the parent item to the difference between the 
current ptvcursor offset and the starting offset of the parent item.


Index: epan/ptvcursor.h
===================================================================
--- epan/ptvcursor.h	(r�vision 20343)
+++ epan/ptvcursor.h	(copie de travail)
@@ -34,6 +34,8 @@
 #include <glib.h>
 #include <epan/packet.h>
 
+#define SUBTREE_UNDEFINED_LENGTH -1
+
 typedef struct ptvcursor ptvcursor_t;
 
 /* Allocates an initializes a ptvcursor_t with 3 variables:
@@ -77,4 +79,32 @@
 void
 ptvcursor_set_tree(ptvcursor_t* ptvc, proto_tree *tree);
 
+/* push a subtree in the tree stack of the cursor */
+proto_tree* 
+ptvcursor_push_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree);
+
+/* pop a subtree in the tree stack of the cursor */
+void ptvcursor_pop_subtree(ptvcursor_t *ptvc);
+
+/* Add an item to the tree and create a subtree
+ * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH.
+ * In this case, when the subtree will be closed, the parent item length will
+ * be equal to the advancement of the cursor since the creation of the subtree.
+ */
+proto_tree* ptvcursor_add_with_subtree(ptvcursor_t * ptvc, int hfindex, gint length,  
+gboolean little_endian, gint ett_subtree);
+
+/* Add a text node to the tree and create a subtree
+ * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH.
+ * In this case, when the subtree will be closed, the item length will be equal
+ * to the advancement of the cursor since the creation of the subtree.
+ */
+proto_tree * ptvcursor_add_text_with_subtree(ptvcursor_t * ptvc, gint length, 
+    gint ett_subtree, const char *format, ...);
+
+/* Creates a subtree and adds it to the cursor as the working tree but does not
+ * save the old working tree */
+proto_tree* 
+ptvcursor_set_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree); 
+
 #endif /* __PTVCURSOR_H__ */
Index: epan/proto.c
===================================================================
--- epan/proto.c	(r�vision 20343)
+++ epan/proto.c	(copie de travail)
@@ -44,7 +44,20 @@
 #include "tvbuff.h"
 #include "emem.h"
 
+#define SUBTREE_ONCE_ALLOCATION_NUMBER 8
+#define SUBTREE_MAX_LEVELS 256
+
+
+typedef struct __subtree_lvl {
+  gint cursor_offset;
+  proto_item * it;
+  proto_tree * tree;
+}subtree_lvl;
+
 struct ptvcursor {
+	subtree_lvl	*pushed_tree;
+	guint8		pushed_tree_index;
+	guint8		pushed_tree_max;
 	proto_tree	*tree;
 	tvbuff_t	*tvb;
 	gint		offset;
@@ -596,6 +609,25 @@
 	return g_tree_lookup(gpa_name_tree, field_name);
 }
 
+
+void ptvcursor_new_subtree_levels(ptvcursor_t * ptvc)
+{
+  DISSECTOR_ASSERT(ptvc->pushed_tree_max <= SUBTREE_MAX_LEVELS-SUBTREE_ONCE_ALLOCATION_NUMBER);
+  ptvc->pushed_tree_max += SUBTREE_ONCE_ALLOCATION_NUMBER;
+
+  ptvc->pushed_tree = g_renew(subtree_lvl, ptvc->pushed_tree, ptvc->pushed_tree_max);
+  DISSECTOR_ASSERT(ptvc->pushed_tree != NULL);
+}
+
+void ptvcursor_free_subtree_levels(ptvcursor_t * ptvc)
+{
+  g_free(ptvc->pushed_tree);
+  ptvc->pushed_tree = NULL;
+  ptvc->pushed_tree_max = 0;
+  DISSECTOR_ASSERT(ptvc->pushed_tree_index ==0);
+  ptvc->pushed_tree_index = 0;
+}
+
 /* Allocates an initializes a ptvcursor_t with 3 variables:
  * 	proto_tree, tvbuff, and offset. */
 ptvcursor_t*
@@ -607,13 +639,18 @@
 	ptvc->tree	= tree;
 	ptvc->tvb	= tvb;
 	ptvc->offset	= offset;
+	ptvc->pushed_tree= NULL;
+	ptvc->pushed_tree_max= 0;
+	ptvc->pushed_tree_index= 0;
 	return ptvc;
 }
 
+
 /* Frees memory for ptvcursor_t, but nothing deeper than that. */
 void
 ptvcursor_free(ptvcursor_t *ptvc)
 {
+	ptvcursor_free_subtree_levels(ptvc);
 	g_free(ptvc);
 }
 
@@ -643,6 +680,102 @@
 	ptvc->tree = tree;
 }
 
+/* creates a subtree, sets it as the working tree and save the old working tree */ 
+proto_tree* 
+ptvcursor_push_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree) 
+{
+  subtree_lvl * subtree;
+  if (ptvc->pushed_tree_index >= ptvc->pushed_tree_max)
+    ptvcursor_new_subtree_levels(ptvc);
+
+  subtree = ptvc->pushed_tree+ptvc->pushed_tree_index;
+  subtree->tree = ptvc->tree;
+  subtree->it= NULL;
+  ptvc->pushed_tree_index++;
+  return ptvcursor_set_subtree(ptvc, it, ett_subtree);
+}
+
+/* pops a subtree */
+void 
+ptvcursor_pop_subtree(ptvcursor_t *ptvc) 
+{
+  subtree_lvl * subtree;
+  if (ptvc->pushed_tree_index <= 0)
+    return;
+
+  ptvc->pushed_tree_index--;
+  subtree = ptvc->pushed_tree+ptvc->pushed_tree_index;
+  if (subtree->it != NULL) 
+    proto_item_set_len(subtree->it, ptvcursor_current_offset(ptvc) - subtree->cursor_offset);
+  ptvc->tree = subtree->tree;
+}
+
+/* saves the current tvb offset and the item in the current subtree level */
+void ptvcursor_subtree_set_item(ptvcursor_t * ptvc, proto_item * it)
+{
+  subtree_lvl * subtree;
+
+  DISSECTOR_ASSERT(ptvc->pushed_tree_index > 0);
+
+  subtree = ptvc->pushed_tree+ptvc->pushed_tree_index-1;
+  subtree->it = it;
+  subtree->cursor_offset = ptvcursor_current_offset(ptvc);
+}
+
+/* Creates a subtree and adds it to the cursor as the working tree but does not
+ * save the old working tree */
+proto_tree* 
+ptvcursor_set_subtree(ptvcursor_t *ptvc, proto_item *it, gint ett_subtree) 
+{
+  ptvc->tree = proto_item_add_subtree(it, ett_subtree);
+  return ptvc->tree;
+}
+
+proto_tree* ptvcursor_add_subtree_item(ptvcursor_t * ptvc, proto_item * it, gint ett_subtree, gint length)
+{
+  ptvcursor_push_subtree(ptvc, it, ett_subtree);
+  if (length == SUBTREE_UNDEFINED_LENGTH)
+    ptvcursor_subtree_set_item(ptvc, it);
+  return ptvcursor_tree(ptvc);
+}
+
+/* Add an item to the tree and create a subtree
+ * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH.
+ * In this case, when the subtree will be closed, the parent item length will
+ * be equal to the advancement of the cursor since the creation of the subtree.
+ */
+proto_tree* ptvcursor_add_with_subtree(ptvcursor_t * ptvc, int hfindex, gint length,  
+gboolean little_endian, gint ett_subtree)
+{
+  proto_item * it;
+  it = ptvcursor_add_no_advance(ptvc, hfindex, length, little_endian);
+  return ptvcursor_add_subtree_item(ptvc, it, ett_subtree, length);
+}
+
+static proto_item *
+proto_tree_add_text_node(proto_tree *tree, tvbuff_t *tvb, gint start, gint length);
+
+/* Add a text node to the tree and create a subtree
+ * If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH.
+ * In this case, when the subtree will be closed, the item length will be equal
+ * to the advancement of the cursor since the creation of the subtree.
+ */
+proto_tree * ptvcursor_add_text_with_subtree(ptvcursor_t * ptvc, gint length, 
+    gint ett_subtree, const char *format, ...)
+{
+  proto_item *	it;
+  va_list	ap;
+
+  it = proto_tree_add_text_node(ptvcursor_tree(ptvc), ptvcursor_tvbuff(ptvc), 
+      ptvcursor_current_offset(ptvc), length);
+
+  va_start(ap, format);
+  proto_tree_set_representation(it, format, ap);
+  va_end(ap);
+
+  return ptvcursor_add_subtree_item(ptvc, it, ett_subtree, length);
+}
+
 /* Add a text-only node, leaving it to our caller to fill the text in */
 static proto_item *
 proto_tree_add_text_node(proto_tree *tree, tvbuff_t *tvb, gint start, gint length)
Index: epan/proto.h
===================================================================
--- epan/proto.h	(r�vision 20343)
+++ epan/proto.h	(copie de travail)
@@ -51,6 +51,8 @@
 #include "tvbuff.h"
 #include "ftypes/ftypes.h"
 
+
+
 /** The header-field index for the special text pseudo-field. Exported by libwireshark.dll */
 WS_VAR_IMPORT int hf_text_only;