A simplified version of tvbuffs:

- Essentially no changes from current dissector de facto tvbuff usage;
- Do away with 'usage_counts' and with 'used_in' GSLists;
- Manage tvb chains via a simple doubly linked list.
- API changes:
  a. tvb_increment_usage_count() and tvb_decrement_usage_count() no
     longer exist;
  b. tvb_free_chain() can only be called for the 'top-level' (initial)
     tvb of a chain) or for a tvb not in a chain.
  c. tvb_free() now just calls tvb_free_chain() [should have no impact
     on existing  dissectors].

svn path=/trunk/; revision=40264
This commit is contained in:
Bill Meier 2011-12-21 17:39:02 +00:00
parent ff30e7df57
commit 14309d2c72
3 changed files with 146 additions and 158 deletions

View File

@ -49,17 +49,15 @@ typedef struct {
} tvb_comp_t;
struct tvbuff {
/* Doubly linked list pointers */
tvbuff_t *next;
tvbuff_t *previous;
/* Record-keeping */
tvbuff_type type;
gboolean initialized;
guint usage_count;
struct tvbuff *ds_tvb; /**< data source top-level tvbuff */
/** The tvbuffs in which this tvbuff is a member
* (that is, a backing tvbuff for a TVBUFF_SUBSET
* or a member for a TVB_COMPOSITE) */
GSList *used_in;
/** TVBUFF_SUBSET and TVBUFF_COMPOSITE keep track
* of the other tvbuff's they use */
union {

View File

@ -66,16 +66,16 @@ tvb_init(tvbuff_t *tvb, const tvbuff_type type)
tvb_backing_t *backing;
tvb_comp_t *composite;
tvb->type = type;
tvb->initialized = FALSE;
tvb->usage_count = 1;
tvb->length = 0;
tvb->reported_length = 0;
tvb->free_cb = NULL;
tvb->real_data = NULL;
tvb->raw_offset = -1;
tvb->used_in = NULL;
tvb->ds_tvb = NULL;
tvb->previous = NULL;
tvb->next = NULL;
tvb->type = type;
tvb->initialized = FALSE;
tvb->length = 0;
tvb->reported_length = 0;
tvb->free_cb = NULL;
tvb->real_data = NULL;
tvb->raw_offset = -1;
tvb->ds_tvb = NULL;
switch(type) {
case TVBUFF_REAL_DATA:
@ -83,17 +83,17 @@ tvb_init(tvbuff_t *tvb, const tvbuff_type type)
break;
case TVBUFF_SUBSET:
backing = &tvb->tvbuffs.subset;
backing = &tvb->tvbuffs.subset;
backing->tvb = NULL;
backing->offset = 0;
backing->length = 0;
break;
case TVBUFF_COMPOSITE:
composite = &tvb->tvbuffs.composite;
composite->tvbs = NULL;
composite->start_offsets = NULL;
composite->end_offsets = NULL;
composite = &tvb->tvbuffs.composite;
composite->tvbs = NULL;
composite->start_offsets = NULL;
composite->end_offsets = NULL;
break;
default:
@ -131,7 +131,7 @@ tvb_new_octet_aligned(tvbuff_t *tvb, guint32 bit_offset, gint32 no_of_bits)
{
tvbuff_t *sub_tvb = NULL;
guint32 byte_offset;
gint32 datalen, i;
gint32 datalen, i;
guint8 left, right, remaining_bits, *buf;
const guint8 *data;
@ -177,7 +177,7 @@ tvb_new_octet_aligned(tvbuff_t *tvb, guint32 bit_offset, gint32 no_of_bits)
buf[datalen-1] &= left_aligned_bitmask[remaining_bits];
sub_tvb = tvb_new_child_real_data(tvb, buf, datalen, datalen);
return sub_tvb;
}
@ -191,102 +191,74 @@ tvb_new_with_subset(const guint subset_tvb_offset, const guint subset_tvb_length
return tvb;
}
void
tvb_free(tvbuff_t* tvb)
static void
tvb_free_internal(tvbuff_t* tvb)
{
tvbuff_t *member_tvb;
tvb_comp_t *composite;
GSList *slist;
tvb->usage_count--;
DISSECTOR_ASSERT(tvb);
if (tvb->usage_count == 0) {
switch (tvb->type) {
case TVBUFF_REAL_DATA:
if (tvb->free_cb) {
/*
* XXX - do this with a union?
*/
tvb->free_cb((gpointer)tvb->real_data);
}
break;
case TVBUFF_SUBSET:
/* This will be NULL if tvb_new_subset() fails because
* reported_length < -1 */
if (tvb->tvbuffs.subset.tvb) {
tvb_decrement_usage_count(tvb->tvbuffs.subset.tvb, 1);
}
break;
case TVBUFF_COMPOSITE:
composite = &tvb->tvbuffs.composite;
for (slist = composite->tvbs; slist != NULL ; slist = slist->next) {
member_tvb = slist->data;
tvb_decrement_usage_count(member_tvb, 1);
}
g_slist_free(composite->tvbs);
g_free(composite->start_offsets);
g_free(composite->end_offsets);
if (tvb->real_data) {
/*
* XXX - do this with a union?
*/
g_free((gpointer)tvb->real_data);
}
break;
switch (tvb->type) {
case TVBUFF_REAL_DATA:
if (tvb->free_cb) {
/*
* XXX - do this with a union?
*/
tvb->free_cb((gpointer)tvb->real_data);
}
break;
if (tvb->used_in) {
g_slist_free(tvb->used_in);
case TVBUFF_SUBSET:
/* Nothing */
break;
case TVBUFF_COMPOSITE:
composite = &tvb->tvbuffs.composite;
g_slist_free(composite->tvbs);
g_free(composite->start_offsets);
g_free(composite->end_offsets);
if (tvb->real_data) {
/*
* XXX - do this with a union?
*/
g_free((gpointer)tvb->real_data);
}
break;
g_slice_free(tvbuff_t, tvb);
default:
DISSECTOR_ASSERT_NOT_REACHED();
}
g_slice_free(tvbuff_t, tvb);
}
guint
tvb_increment_usage_count(tvbuff_t* tvb, const guint count)
/* XXX: just call tvb_free_chain();
* Not removed so that existing dissectors using tvb_free() need not be changed.
* I'd argue that existing calls to tvb_free() should have actually beeen
* calls to tvb_free_chain() although the calls were OK as long as no
* subsets, etc had been created on the tvb. */
void
tvb_free(tvbuff_t *tvb)
{
tvb->usage_count += count;
return tvb->usage_count;
}
guint
tvb_decrement_usage_count(tvbuff_t* tvb, const guint count)
{
if (tvb->usage_count <= count) {
tvb->usage_count = 1;
tvb_free(tvb);
return 0;
}
else {
tvb->usage_count -= count;
return tvb->usage_count;
}
tvb_free_chain(tvb);
}
void
tvb_free_chain(tvbuff_t* tvb)
{
GSList *slist;
/* Recursively call tvb_free_chain() */
for (slist = tvb->used_in; slist != NULL ; slist = slist->next) {
tvb_free_chain( (tvbuff_t*)slist->data );
tvbuff_t *next_tvb;
DISSECTOR_ASSERT(tvb);
DISSECTOR_ASSERT((tvb->previous==NULL) && "tvb_free_chain(): tvb must be initial tvb in chain");
while (tvb) {
next_tvb=tvb->next;
DISSECTOR_ASSERT(((next_tvb==NULL) || (tvb==next_tvb->previous)) && "tvb_free_chain(): corrupt tvb chain ?");
tvb_free_internal(tvb);
tvb = next_tvb;
}
/* Stop the recursion */
tvb_free(tvb);
}
void
tvb_set_free_cb(tvbuff_t* tvb, const tvbuff_free_cb_t func)
{
@ -296,10 +268,15 @@ tvb_set_free_cb(tvbuff_t* tvb, const tvbuff_free_cb_t func)
}
static void
add_to_used_in_list(tvbuff_t *tvb, tvbuff_t *used_in)
add_to_chain(tvbuff_t *parent, tvbuff_t *child)
{
tvb->used_in = g_slist_prepend(tvb->used_in, used_in);
tvb_increment_usage_count(tvb, 1);
DISSECTOR_ASSERT(parent && child);
DISSECTOR_ASSERT(!child->next && !child->previous);
child->next = parent->next;
child->previous = parent;
if (parent->next)
parent->next->previous = child;
parent->next = child;
}
void
@ -309,7 +286,7 @@ tvb_set_child_real_data_tvbuff(tvbuff_t* parent, tvbuff_t* child)
DISSECTOR_ASSERT(parent->initialized);
DISSECTOR_ASSERT(child->initialized);
DISSECTOR_ASSERT(child->type == TVBUFF_REAL_DATA);
add_to_used_in_list(parent, child);
add_to_chain(parent, child);
}
static void
@ -349,7 +326,6 @@ tvb_new_real_data(const guint8* data, const guint length, const gint reported_le
* so its data source tvbuff is itself.
*/
tvb->ds_tvb = tvb;
return tvb;
}
@ -516,7 +492,7 @@ tvb_set_subset_no_exceptions(tvbuff_t *tvb, tvbuff_t *backing, const gint report
tvb->reported_length = reported_length;
}
tvb->initialized = TRUE;
add_to_used_in_list(backing, tvb);
add_to_chain(backing, tvb);
/* Optimization. If the backing buffer has a pointer to contiguous, real data,
* then we can point directly to our starting offset in that buffer */
@ -594,6 +570,20 @@ tvb_new_subset_remaining(tvbuff_t *backing, const gint backing_offset)
return tvb;
}
/*
* Composite tvb
*
* 1. A composite tvb is automatically chained to its first member when the
* tvb is finalized.
* This means that composite tvb members must all be in the same chain.
* ToDo: enforce this: By searching the chain?
*/
tvbuff_t*
tvb_new_composite(void)
{
return tvb_new(TVBUFF_COMPOSITE);
}
void
tvb_composite_append(tvbuff_t* tvb, tvbuff_t* member)
{
@ -603,7 +593,6 @@ tvb_composite_append(tvbuff_t* tvb, tvbuff_t* member)
DISSECTOR_ASSERT(tvb->type == TVBUFF_COMPOSITE);
composite = &tvb->tvbuffs.composite;
composite->tvbs = g_slist_append( composite->tvbs, member );
add_to_used_in_list(tvb, member);
}
void
@ -615,14 +604,8 @@ tvb_composite_prepend(tvbuff_t* tvb, tvbuff_t* member)
DISSECTOR_ASSERT(tvb->type == TVBUFF_COMPOSITE);
composite = &tvb->tvbuffs.composite;
composite->tvbs = g_slist_prepend( composite->tvbs, member );
add_to_used_in_list(tvb, member);
}
tvbuff_t*
tvb_new_composite(void)
{
return tvb_new(TVBUFF_COMPOSITE);
}
void
tvb_composite_finalize(tvbuff_t* tvb)
@ -653,7 +636,7 @@ tvb_composite_finalize(tvbuff_t* tvb)
composite->end_offsets[i] = tvb->length - 1;
i++;
}
add_to_chain((tvbuff_t *)composite->tvbs->data, tvb); /* chain composite tvb to first member */
tvb->initialized = TRUE;
}

View File

@ -50,7 +50,6 @@
* virtual data.
*/
/** The different types of tvbuff's */
typedef enum {
TVBUFF_REAL_DATA,
@ -61,6 +60,30 @@ typedef enum {
struct tvbuff;
typedef struct tvbuff tvbuff_t;
/**
* tvbuffs: dissector use and management
*
* Consider a collection of tvbs as being a chain or stack of tvbs.
*
* The top-level dissector (packet.c) pushes the initial tvb onto the stack
* (starts the chain) and then calls a sub-dissector which in turn calls the next
* sub-dissector and so on. Each sub-dissector may chain additional tvbs to
* the tvb handed to that dissector. After dissection is complete and control has
* returned to the top-level dissector, the chain of tvbs (stack) is free'd
* via a call to tvb_free_chain() (in epan_dissect_cleanup()).
*
* A dissector:
* - Can chain new tvbs (real, subset, composite) to the tvb
* handed to the dissector via tvb_subset(), tvb_new_child_real_data(), etc.
* (Subset and Composite tvbs should reference only tvbs which are
* already part of the chain).
* - Must not save a pointer to a tvb handed to the dissector for
* use when dissecting another frame; A higher level function
* may very well free the chain). This also applies to any tvbs chained
* by the dissector to the tvb handed to the dissector.
* - Can create its own tvb chain (using tvb_new_real_data() which
* the dissector is free to manage as desired. */
/** TVBUFF_REAL_DATA contains a guint8* that points to real data.
* The data is allocated and contiguous.
*
@ -77,63 +100,44 @@ typedef struct tvbuff tvbuff_t;
* Once a tvbuff is create/initialized/finalized, the tvbuff is read-only.
* That is, it cannot point to any other data. A new tvbuff must be created if
* you want a tvbuff that points to other data.
*
* tvbuff's are normally chained together to allow efficient de-allocation of tvbuff's.
*
*/
typedef void (*tvbuff_free_cb_t)(void*);
/** Returns a pointer to a newly initialized tvbuff. Note that
* tvbuff's of types TVBUFF_SUBSET and TVBUFF_COMPOSITE
* require further initialization via the appropriate functions */
* require further initialization via the appropriate functions. */
extern tvbuff_t* tvb_new(tvbuff_type);
/** Extracs from bit offset number of bits and
* Returns a pointer to a newly initialized tvbuff. with the bits
* octet aligned.
/** Extracts 'number of bits' starting at 'bit offset'.
* Returns a pointer to a newly initialized ep_alloc'd REAL_DATA
* tvbuff with the bits octet aligned.
*/
extern tvbuff_t* tvb_new_octet_aligned(tvbuff_t *tvb, guint32 bit_offset, gint32 no_of_bits);
/** Marks a tvbuff for freeing. The guint8* data of a TVBUFF_REAL_DATA
* is *never* freed by the tvbuff routines. The tvbuff itself is actually freed
* once its usage count drops to 0.
*
* Usage counts increment for any time the tvbuff is
* used as a member of another tvbuff, i.e., as the backing buffer for
* a TVBUFF_SUBSET or as a member of a TVBUFF_COMPOSITE.
*
* Although you may call tvb_free(), the tvbuff may still be in use
* by other tvbuff's (TVBUFF_SUBSET or TVBUFF_COMPOSITE), so it is not
* safe, unless you know otherwise, to free your guint8* data. If you
* cannot be sure that your TVBUFF_REAL_DATA is not in use by another
* tvbuff, register a callback with tvb_set_free_cb(); when your tvbuff
* is _really_ freed, then your callback will be called, and at that time
* you can free your original data.
*
* The caller can artificially increment/decrement the usage count
* with tvbuff_increment_usage_count()/tvbuff_decrement_usage_count().
*/
/** Free a tvbuff_t and all tvbuffs chained from it
* The tvbuff must be 'the 'head' (initial) tvb of a chain or
* must not be in a chain.
* If specified, a callback to free the tvbuff data will be invoked
* for each tvbuff free'd */
extern void tvb_free(tvbuff_t*);
/** Free the tvbuff_t and all tvbuff's created from it. */
/** Free the tvbuff_t and all tvbuffs chained from it.
* The tvbuff must be 'the 'head' (initial) tvb of a chain or
* must not be in a chain.
* If specified, a callback to free the tvbuff data will be invoked
* for each tvbuff free'd */
extern void tvb_free_chain(tvbuff_t*);
/** Both return the new usage count, after the increment or decrement */
extern guint tvb_increment_usage_count(tvbuff_t*, const guint count);
/** If a decrement causes the usage count to drop to 0, a the tvbuff
* is immediately freed. Be sure you know exactly what you're doing
* if you decide to use this function, as another tvbuff could
* still have a pointer to the just-freed tvbuff, causing corrupted data
* or a segfault in the future */
extern guint tvb_decrement_usage_count(tvbuff_t*, const guint count);
/** Set a callback function to call when a tvbuff is actually freed
* (once the usage count drops to 0). One argument is passed to
* that callback --- a void* that points to the real data.
* Obviously, this only applies to a TVBUFF_REAL_DATA tvbuff. */
* One argument is passed to that callback --- a void* that points
* to the real data. Obviously, this only applies to a
* TVBUFF_REAL_DATA tvbuff. */
extern void tvb_set_free_cb(tvbuff_t*, const tvbuff_free_cb_t);
/** Attach a TVBUFF_REAL_DATA tvbuff to a parent tvbuff. This connection
* is used during a tvb_free_chain()... the "child" TVBUFF_REAL_DATA acts
* as if is part of the chain-of-creation of the parent tvbuff, although it
@ -141,22 +145,24 @@ extern void tvb_set_free_cb(tvbuff_t*, const tvbuff_free_cb_t);
* run some operation on it, like decryption or decompression, and make a new
* tvbuff from it, yet want the new tvbuff to be part of the chain. The reality
* is that the new tvbuff *is* part of the "chain of creation", but in a way
* that these tvbuff routines is ignorant of. Use this function to make
* that these tvbuff routines are ignorant of. Use this function to make
* the tvbuff routines knowledgable of this fact. */
extern void tvb_set_child_real_data_tvbuff(tvbuff_t* parent, tvbuff_t* child);
extern tvbuff_t* tvb_new_child_real_data(tvbuff_t* parent, const guint8* data, const guint length,
const gint reported_length);
/**Sets parameters for TVBUFF_REAL_DATA. Can throw ReportedBoundsError. */
/** Sets parameters for TVBUFF_REAL_DATA. Can throw ReportedBoundsError. */
extern void tvb_set_real_data(tvbuff_t*, const guint8* data, const guint length,
const gint reported_length);
/** Combination of tvb_new() and tvb_set_real_data(). Can throw ReportedBoundsError. */
/** Combination of tvb_new() and tvb_set_real_data(). Can throw ReportedBoundsError.
* Normally, a callback to free the data should be registered using tvb_set_free_cb();
* when this tvbuff is freed, then your callback will be called, and at that time
* you can free your original data. */
extern tvbuff_t* tvb_new_real_data(const guint8* data, const guint length,
const gint reported_length);
/** Define the subset of the backing buffer to use.
*
* 'backing_offset' can be negative, to indicate bytes from
@ -291,7 +297,8 @@ extern guint16 tvb_get_bits16(tvbuff_t *tvb, gint bit_offset, const gint no_of_b
extern guint32 tvb_get_bits32(tvbuff_t *tvb, gint bit_offset, const gint no_of_bits, const guint encoding);
extern guint64 tvb_get_bits64(tvbuff_t *tvb, gint bit_offset, const gint no_of_bits, const guint encoding);
/* Fetch a specified number of bits from bit offset in a tvb, but allow number
/**
* Fetch a specified number of bits from bit offset in a tvb, but allow number
* of bits to range between 1 and 32. If the requested number of bits is known
* beforehand, or its range can be handled by a single function of the group
* above, use one of them instead.