2006-03-09 12:05:53 +00:00
|
|
|
$Id$
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
1. Introduction
|
|
|
|
|
2007-10-12 19:13:31 +00:00
|
|
|
Binary trees are a well known and popular device in computer science to handle
|
2006-03-09 11:06:21 +00:00
|
|
|
storage of object based on a search key or identity.
|
|
|
|
One particular class of binary trees are Red/Black trees which have nice
|
|
|
|
properties such as being self-balanced.
|
|
|
|
Such trees are often very fast for accessing data, and may average O(log(n))
|
|
|
|
for lookups, compared to linked lists that are of order O(n).
|
|
|
|
|
|
|
|
Benefits of using binary trees are that they are incredibly fast for
|
|
|
|
accessing data and they scale very well with good characteristics even to
|
2007-10-12 19:13:31 +00:00
|
|
|
very large numbers of objects.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
2006-05-31 19:12:15 +00:00
|
|
|
Wireshark provides its own version of red black binary trees designed in
|
2006-03-09 11:06:21 +00:00
|
|
|
particular to be easy to use and to eliminate most of the memory management
|
|
|
|
often associated with such trees.
|
|
|
|
|
2006-05-22 08:14:01 +00:00
|
|
|
The trees supported by wireshark are currently all created using SEasonal
|
2006-05-31 19:12:15 +00:00
|
|
|
storage which means that when you load a new trace into wireshark, the SEasonal
|
2006-03-09 11:06:21 +00:00
|
|
|
memory management will automatically release every single byte of data
|
|
|
|
associated with the tree.
|
|
|
|
|
2006-10-16 03:23:43 +00:00
|
|
|
Please see README.malloc for a description of EPhemeral and SEasonal storage.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
2. Basic Usage
|
2007-10-12 19:13:31 +00:00
|
|
|
For most users it will be sufficient to only know and use three functions
|
2006-08-14 08:29:29 +00:00
|
|
|
emem_tree_t *se_tree_create(int type, char *name);
|
|
|
|
void se_tree_insert32(emem_tree_t *se_tree, guint32 key, void *data);
|
|
|
|
void *se_tree_lookup32(emem_tree_t *se_tree, guint32 key);
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
|
2006-03-11 13:21:41 +00:00
|
|
|
2.1 se_tree_create(int type, char *name);
|
2006-03-09 11:06:21 +00:00
|
|
|
se_tree_create() is used to initialize a tree that will be automatically
|
2010-02-07 11:01:13 +00:00
|
|
|
cleared and reset every time wireshark is resetting all SEasonal storage.
|
2007-10-12 19:13:31 +00:00
|
|
|
That is every time you load a new capture file into wireshark or when
|
2006-03-09 11:06:21 +00:00
|
|
|
you rescan the entire capture file from scratch.
|
|
|
|
|
2006-03-11 13:21:41 +00:00
|
|
|
Name is just a literal text string and serves no other purpose than making
|
|
|
|
debugging of the trees easier. Specify a name here that uniquely identifies
|
|
|
|
both the protocol you create the tree for and its purpose.
|
|
|
|
|
|
|
|
|
2006-03-09 11:06:21 +00:00
|
|
|
This function is most likely called once from your
|
|
|
|
proto_register_<yourprotocol>() function.
|
|
|
|
|
|
|
|
Example (from packet-tcp.c):
|
|
|
|
#include <epan/emem.h>
|
|
|
|
...
|
2006-08-14 08:29:29 +00:00
|
|
|
static emem_tree_t *tcp_pdu_time_table = NULL;
|
2006-03-09 11:06:21 +00:00
|
|
|
...
|
|
|
|
void proto_register_...(void) {
|
|
|
|
...
|
2007-10-12 19:13:31 +00:00
|
|
|
tcp_pdu_time_table=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "PROTO_mytree");
|
2006-03-09 11:06:21 +00:00
|
|
|
...
|
|
|
|
}
|
|
|
|
|
2007-10-12 19:13:31 +00:00
|
|
|
That is how easy it is to create a binary tree. You only need to create it
|
|
|
|
once when wireshark starts and the tree will remain there until you exit
|
2010-02-07 11:01:13 +00:00
|
|
|
wireshark. Every time a new capture is loaded, all nodes allocated to the
|
2007-10-12 19:13:31 +00:00
|
|
|
tree are freed automatically and the tree is reset without you having to do
|
|
|
|
anything at all.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
|
2006-08-14 08:29:29 +00:00
|
|
|
2.2 se_tree_insert32(emem_tree_t *se_tree, guint32 key, void *data);
|
2006-03-09 11:06:21 +00:00
|
|
|
This function is used to add a new node into the tree.
|
|
|
|
This function is used for such trees where you always use a guint32 as the
|
|
|
|
identifier/key for the node. Most trees you want to use are likely in this
|
|
|
|
category.
|
|
|
|
|
|
|
|
As data you should specify a pointer to the data structure you want to be
|
2007-10-12 19:13:31 +00:00
|
|
|
able to retrieve later when you look for that same key.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
NOTE: If you insert a node to a key that already exists in the tree
|
|
|
|
this function will allow you to do that. It will just drop the old pointer
|
|
|
|
to data and replace it with the new one you just provided.
|
|
|
|
This should not be a problem as long as the old and the new data blobs
|
2007-10-12 19:13:31 +00:00
|
|
|
are se_allocated() since you cant free() such memory explicitly anyway
|
2006-03-09 11:06:21 +00:00
|
|
|
and the old one will be release automatically anyway when the SEasonal
|
|
|
|
system reclaims all the SE data.
|
|
|
|
|
|
|
|
NOTE: It is a good idea to only provide data that point to blobs allocated
|
2007-10-12 19:13:31 +00:00
|
|
|
by se_alloc(). By doing that you will have a tree where the tree and all
|
|
|
|
the data pointed to will be automatically released by the system at the
|
|
|
|
same time. This is very neat and makes it real difficult to have memory
|
|
|
|
leaks in your code.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
NOTE: When you insert items in the tree, it is very likely that you only
|
|
|
|
want to add any data to the tree during the very first time you process
|
|
|
|
a particular packet.
|
2010-02-07 11:01:13 +00:00
|
|
|
Wireshark may reprocess the same packet multiple times afterward by the user
|
2006-03-09 11:06:21 +00:00
|
|
|
clicking on the packet or for other reasons.
|
|
|
|
You probably DO want to protect the insert call within an if statement such
|
|
|
|
as
|
|
|
|
|
|
|
|
Example:
|
|
|
|
/* only TRUE first time we process this packet*/
|
|
|
|
if(!pinfo->fd->flags.visited){
|
|
|
|
...
|
|
|
|
data=se_alloc(...);
|
|
|
|
data->...
|
|
|
|
...
|
|
|
|
se_tree_insert32(se_tree, key, data);
|
|
|
|
...
|
|
|
|
}
|
|
|
|
|
|
|
|
Please do think about how and when you want to add items to the tree.
|
2007-10-12 19:13:31 +00:00
|
|
|
If you don't think you should use the if statement to protect the insert
|
2006-03-09 11:06:21 +00:00
|
|
|
please reconsider and think it through one extra time.
|
|
|
|
|
|
|
|
|
2006-08-14 08:29:29 +00:00
|
|
|
2.3 se_tree_lookup32(emem_tree_t *se_tree, guint32 key);
|
2006-03-09 11:06:21 +00:00
|
|
|
This function is used to read back from the tree the data value that is
|
|
|
|
associated with this key.
|
|
|
|
If no such node was found the function will return NULL.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
...
|
|
|
|
data_structure = se_tree_lookup32(se_tree, key);
|
|
|
|
if(data_structure){
|
|
|
|
...
|
|
|
|
}
|
|
|
|
|
2007-10-12 19:13:31 +00:00
|
|
|
Don't forget to check that the returned pointer is non-NULL before you
|
2006-03-09 11:06:21 +00:00
|
|
|
dereference it, please.
|
|
|
|
|
2010-02-07 11:01:13 +00:00
|
|
|
2.4 se_tree_lookup32_le(emem_tree_t *se_tree, guint32 key);
|
|
|
|
The function will return the node that has the largest key that is
|
|
|
|
equal to or smaller than the search key, or NULL if no such key was found.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
|
2010-02-07 11:01:13 +00:00
|
|
|
Simple as that, can it be easier?
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
3. Advanced Usage
|
|
|
|
This will list some of the more unconventional and hopefully rarely used
|
|
|
|
functions.
|
|
|
|
|
2006-03-11 13:21:41 +00:00
|
|
|
3.1 se_tree_create_non_persistent(int type, char *name);
|
2007-10-12 19:13:31 +00:00
|
|
|
Sometimes you don't want a tree that is automatically reset to become an empty
|
2006-03-09 11:06:21 +00:00
|
|
|
tree. You might want a tree that is completely destroyed once the next
|
|
|
|
capture file is read and even the pointer to the tree itself becomes invalid.
|
|
|
|
|
|
|
|
This would most likely be the case when you do NOT want a global tree
|
|
|
|
but instead a tree that is held inside some other SE allocated structure.
|
|
|
|
So that when that encapsulating structure is released the entire tree will
|
2007-10-12 19:13:31 +00:00
|
|
|
disappear completely as well.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
Maybe you have a normal tree to track all conversations for your protocol
|
|
|
|
and for each conversation you se_alloc() a structure to maintain some
|
|
|
|
data about that conversation. Assume you want to add to that structure
|
|
|
|
another binary tree a binary tree that is private to that structure/
|
|
|
|
conversation to hold some other data.
|
|
|
|
In that case, this is the function you want.
|
|
|
|
|
|
|
|
|
|
|
|
3.2 se_tree_insert32_array / se_tree_lookup32_array
|
2006-08-14 08:29:29 +00:00
|
|
|
typedef struct _emem_tree_key_t {
|
2006-03-09 11:06:21 +00:00
|
|
|
guint32 length; /*length in guint32 words */
|
|
|
|
guint32 *key;
|
2006-08-14 08:29:29 +00:00
|
|
|
} emem_tree_key_t;
|
|
|
|
void se_tree_insert32_array(emem_tree_t *se_tree, emem_tree_key_t *key, void *data);
|
|
|
|
void *se_tree_lookup32_array(emem_tree_t *se_tree, emem_tree_key_t *key);
|
2006-03-09 11:06:21 +00:00
|
|
|
|
2007-10-12 19:13:31 +00:00
|
|
|
NOTE: the *key parameter taken by these functions WILL be modified by these
|
|
|
|
functions so if you want to reuse the key for a second call you will have
|
|
|
|
to reinitialize key.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
These functions are used to insert and lookup a tree where nodes are NOT
|
|
|
|
indexed by a single guint32 but more like an array of guint32 elements.
|
|
|
|
|
2006-08-14 08:29:29 +00:00
|
|
|
These functions take as key an array of guint32 vectors : emem_tree_key_t.
|
2007-10-12 19:13:31 +00:00
|
|
|
The functions will use vector by vector to search further down the tree
|
2006-03-09 11:06:21 +00:00
|
|
|
until an array element where length==0 is found indicating the end of the
|
|
|
|
array.
|
|
|
|
|
2006-08-14 08:29:29 +00:00
|
|
|
NOTE: you MUST terminate the emem_tree_key_t array by {0, NULL}
|
2006-05-31 19:12:15 +00:00
|
|
|
If you forget to do this wireshark will immediately crash.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
2007-10-12 19:13:31 +00:00
|
|
|
NOTE: length indicates the number of guint32 values in the vector, not the
|
|
|
|
number of bytes.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
If your keys are always of exactly the same length, always, and you are willing
|
|
|
|
to bet that there can never ever be a key of a different length you can
|
|
|
|
get away with a simple :
|
2006-08-14 08:29:29 +00:00
|
|
|
emem_tree_key_t key[2];
|
2006-03-09 11:06:21 +00:00
|
|
|
key[0].length= LEN;
|
|
|
|
key[0].key= KEY;
|
|
|
|
key[1].length=0;
|
|
|
|
key[1].key=NULL;
|
|
|
|
se_tree_insert32_array(se_tree, &key[0], data);
|
|
|
|
But IF you would accidentally pass a key with a different number of guint32s
|
|
|
|
in its vectors to this same se_tree you will crash.
|
2007-10-12 19:13:31 +00:00
|
|
|
Don't use key like this. Please.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
Instead use this simple workaround to make it all safe :
|
|
|
|
Specify the first index as 1 guint32 holding the total number of guints in the
|
|
|
|
rest of the key.
|
2010-02-07 11:01:13 +00:00
|
|
|
See NFS that does this to handle file handles that may be of different lengths
|
2006-03-09 11:06:21 +00:00
|
|
|
in the same tree :
|
2006-08-14 08:29:29 +00:00
|
|
|
emem_tree_key_t fhkey[3];
|
2006-03-09 11:06:21 +00:00
|
|
|
guint32 tmplen;
|
|
|
|
|
|
|
|
tmplen = <length of filehandle/4>;
|
|
|
|
fhkey[0].length = 1;
|
|
|
|
fhkey[0].key = &tmplen;
|
|
|
|
fhkey[1].length = tmplen;
|
|
|
|
fhkey[1].key = <filehandle>;
|
|
|
|
fhkey[2].length = 0;
|
|
|
|
fhkey[2].key = NULL;
|
|
|
|
|
|
|
|
( /4 since we specify the length here in number of guint32 not number of bytes)
|
|
|
|
|
2010-02-07 11:01:13 +00:00
|
|
|
3.3 se_tree_lookup32_array_le(emem_tree_t *se_tree, emem_tree_key_t *key);
|
|
|
|
Much like the se_tree_lookup32_le, this function will return the node that has
|
|
|
|
the largest key that is equal to or smaller than the search key, or NULL if
|
|
|
|
no such key was found.
|
|
|
|
|
|
|
|
When using _array_ trees, the tree that is created is a "tree of trees" where the
|
|
|
|
last leaf has the indexed data. Thus if you have 3 keys in the emme_tree_key_t
|
|
|
|
structure, the "1st" tree indexes key[0]. Each node in this tree points to a
|
|
|
|
tree indexed using key[1]. The nodes of the final tree will contain the data.
|
|
|
|
|
|
|
|
This construction must be taken into account when using se_tree_lookup32_array_le.
|
|
|
|
The program should verify that the node returned contains data that is expected.
|
2006-03-09 11:06:21 +00:00
|
|
|
|
2010-02-07 11:01:13 +00:00
|
|
|
3.4 se_tree_insert_string / se_tree_lookup_string
|
2008-03-09 19:48:50 +00:00
|
|
|
void emem_tree_insert_string(emem_tree_t* h, const gchar* k, void* v, guint32 flags);
|
|
|
|
void* emem_tree_lookup_string(emem_tree_t* h, const gchar* k, guint32 flags);
|
|
|
|
These functions are essentially wrappers for se_tree_insert32_array and
|
2009-05-02 06:42:52 +00:00
|
|
|
se_tree_lookup32_array, tailored to text strings. They extend the text string
|
2008-03-09 19:48:50 +00:00
|
|
|
into an array key and use that to key the se_tree_insert32_array and
|
|
|
|
se_tree_lookup32_array functions.
|
|
|
|
In order to support text string in a case insensitive way add the
|
|
|
|
EMEM_TREE_STRING_NOCASE flag. This will uppercase all string data before using
|
|
|
|
it as key data.
|
|
|
|
|