summaryrefslogtreecommitdiff
path: root/epan/tvbuff.c
diff options
context:
space:
mode:
authorGilbert Ramirez <gram@alumni.rice.edu>2000-09-27 04:55:05 +0000
committerGilbert Ramirez <gram@alumni.rice.edu>2000-09-27 04:55:05 +0000
commit83ec54675c19f25cfbb2e8a8863c6ee2191d8b0c (patch)
tree35d39e43d20aca160b96d8a85ddc2b80e4646f67 /epan/tvbuff.c
parentf881e13932e0ba390c26cce15d1334b2154a8062 (diff)
downloadwireshark-83ec54675c19f25cfbb2e8a8863c6ee2191d8b0c.tar.gz
First step in moving core Ethereal routines to libepan.
svn path=/trunk/; revision=2458
Diffstat (limited to 'epan/tvbuff.c')
-rw-r--r--epan/tvbuff.c1144
1 files changed, 1144 insertions, 0 deletions
diff --git a/epan/tvbuff.c b/epan/tvbuff.c
new file mode 100644
index 0000000000..74071276ad
--- /dev/null
+++ b/epan/tvbuff.c
@@ -0,0 +1,1144 @@
+/* tvbuff.c
+ *
+ * Testy, Virtual(-izable) Buffer of guint8*'s
+ *
+ * "Testy" -- the buffer gets mad when an attempt to access data
+ * beyond the bounds of the buffer. An exception is thrown.
+ *
+ * "Virtual" -- the buffer can have its own data, can use a subset of
+ * the data of a backing tvbuff, or can be a composite of
+ * other tvbuffs.
+ *
+ * $Id: tvbuff.c,v 1.1 2000/09/27 04:54:53 gram Exp $
+ *
+ * Copyright (c) 2000 by Gilbert Ramirez <gram@xiexie.org>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include "pint.h"
+#include "tvbuff.h"
+#include "strutil.h"
+
+typedef struct {
+ /* The backing tvbuff_t */
+ tvbuff_t *tvb;
+
+ /* The offset/length of 'tvb' to which I'm privy */
+ guint offset;
+ guint length;
+
+} tvb_backing_t;
+
+typedef struct {
+ GSList *tvbs;
+
+ /* Used for quick testing to see if this
+ * is the tvbuff that a COMPOSITE is
+ * interested in. */
+ guint *start_offsets;
+ guint *end_offsets;
+
+} tvb_comp_t;
+
+struct tvbuff {
+ /* Record-keeping */
+ tvbuff_type type;
+ gboolean initialized;
+ guint usage_count;
+
+ /* 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 {
+ tvb_backing_t subset;
+ tvb_comp_t composite;
+ } tvbuffs;
+
+ /* We're either a TVBUFF_REAL_DATA or a
+ * TVBUFF_SUBSET that has a backing buffer that
+ * has real_data != NULL, or a TVBUFF_COMPOSITE
+ * which has flattened its data due to a call
+ * to tvb_get_ptr().
+ */
+ guint8 *real_data;
+
+ /* Length of virtual buffer (and/or real_data). */
+ guint length;
+
+ /* Reported length. */
+ guint reported_length;
+
+ /* Offset from beginning of first TVBUFF_REAL. */
+ gint raw_offset;
+
+ /* Func to call when actually freed */
+ tvbuff_free_cb_t free_cb;
+};
+
+static guint8*
+ensure_contiguous(tvbuff_t *tvb, gint offset, gint length);
+
+/* We dole out tvbuff's from this memchunk. */
+GMemChunk *tvbuff_mem_chunk = NULL;
+
+void
+tvbuff_init(void)
+{
+ if (!tvbuff_mem_chunk)
+ tvbuff_mem_chunk = g_mem_chunk_create(tvbuff_t, 20, G_ALLOC_AND_FREE);
+}
+
+void
+tvbuff_cleanup(void)
+{
+ if (tvbuff_mem_chunk)
+ g_mem_chunk_destroy(tvbuff_mem_chunk);
+
+ tvbuff_mem_chunk = NULL;
+}
+
+
+
+
+static void
+tvb_init(tvbuff_t *tvb, 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;
+
+ switch(type) {
+ case TVBUFF_REAL_DATA:
+ /* Nothing */
+ break;
+
+ case TVBUFF_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;
+ break;
+ }
+}
+
+
+tvbuff_t*
+tvb_new(tvbuff_type type)
+{
+ tvbuff_t *tvb;
+
+ tvb = g_chunk_new(tvbuff_t, tvbuff_mem_chunk);
+ g_assert(tvb);
+
+ tvb_init(tvb, type);
+
+ return tvb;
+}
+
+/* We accept a void* instead of a field_info* to satisfy CLEANUP_POP */
+static void
+tvb_free_void(void *tvb)
+{
+ tvb_free((tvbuff_t*)tvb);
+}
+
+
+
+void
+tvb_free(tvbuff_t* tvb)
+{
+ tvbuff_t *member_tvb;
+ tvb_comp_t *composite;
+ GSList *slist;
+
+ tvb->usage_count--;
+
+ if (tvb->usage_count == 0) {
+ switch (tvb->type) {
+ case TVBUFF_REAL_DATA:
+ if (tvb->free_cb) {
+ tvb->free_cb(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);
+
+ if (composite->start_offsets)
+ g_free(composite->start_offsets);
+ if (composite->end_offsets)
+ g_free(composite->end_offsets);
+ if (tvb->real_data)
+ g_free(tvb->real_data);
+
+ break;
+ }
+
+ if (tvb->used_in) {
+ g_slist_free(tvb->used_in);
+ }
+
+ g_chunk_free(tvb, tvbuff_mem_chunk);
+ }
+}
+
+guint
+tvb_increment_usage_count(tvbuff_t* tvb, guint count)
+{
+ tvb->usage_count += count;
+
+ return tvb->usage_count;
+}
+
+guint
+tvb_decrement_usage_count(tvbuff_t* tvb, 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;
+ }
+
+}
+
+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 );
+ }
+
+ /* Stop the recursion */
+ tvb_free(tvb);
+}
+
+
+
+void
+tvb_set_free_cb(tvbuff_t* tvb, tvbuff_free_cb_t func)
+{
+ g_assert(tvb->type == TVBUFF_REAL_DATA);
+ tvb->free_cb = func;
+}
+
+void
+tvb_set_real_data(tvbuff_t* tvb, const guint8* data, guint length, gint reported_length)
+{
+ g_assert(tvb->type == TVBUFF_REAL_DATA);
+ g_assert(!tvb->initialized);
+
+ if (reported_length < -1) {
+ THROW(ReportedBoundsError);
+ }
+
+ tvb->real_data = (gpointer) data;
+ tvb->length = length;
+ tvb->reported_length = reported_length;
+ tvb->initialized = TRUE;
+}
+
+tvbuff_t*
+tvb_new_real_data(const guint8* data, guint length, gint reported_length)
+{
+ tvbuff_t *tvb;
+
+ tvb = tvb_new(TVBUFF_REAL_DATA);
+
+ CLEANUP_PUSH(tvb_free_void, tvb);
+
+ tvb_set_real_data(tvb, data, length, reported_length);
+
+ CLEANUP_POP;
+
+ return tvb;
+}
+
+/* Computes the absolute offset and length based on a possibly-negative offset
+ * and a length that is possible -1 (which means "to the end of the data").
+ * Returns TRUE/FALSE indicating whether the offset is in bounds or
+ * not. The integer ptrs are modified with the new offset and length.
+ * No exception is thrown.
+ *
+ * XXX - we return TRUE, not FALSE, if the offset is positive and right
+ * after the end of the tvbuff (i.e., equal to the length). We do this
+ * so that a dissector constructing a subset tvbuff for the next protocol
+ * will get a zero-length tvbuff, not an exception, if there's no data
+ * left for the next protocol - we want the next protocol to be the one
+ * that gets an exception, so the error is reported as an error in that
+ * protocol rather than the containing protocol. */
+static gboolean
+compute_offset_length(tvbuff_t *tvb, gint offset, gint length,
+ guint *offset_ptr, guint *length_ptr, int *exception)
+{
+ g_assert(offset_ptr);
+ g_assert(length_ptr);
+
+ /* Compute the offset */
+ if (offset >= 0) {
+ /* Positive offset - relative to the beginning of the packet. */
+ if (offset > tvb->reported_length) {
+ if (exception) {
+ *exception = ReportedBoundsError;
+ }
+ return FALSE;
+ }
+ else if (offset > tvb->length) {
+ if (exception) {
+ *exception = BoundsError;
+ }
+ return FALSE;
+ }
+ else {
+ *offset_ptr = offset;
+ }
+ }
+ else {
+ /* Negative offset - relative to the end of the packet. */
+ if (-offset > tvb->reported_length) {
+ if (exception) {
+ *exception = ReportedBoundsError;
+ }
+ return FALSE;
+ }
+ else if (-offset > tvb->length) {
+ if (exception) {
+ *exception = BoundsError;
+ }
+ return FALSE;
+ }
+ else {
+ *offset_ptr = tvb->length + offset;
+ }
+ }
+
+ /* Compute the length */
+ g_assert(length >= -1);
+ if (length == -1) {
+ *length_ptr = tvb->length - *offset_ptr;
+ }
+ else {
+ *length_ptr = length;
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+check_offset_length_no_exception(tvbuff_t *tvb, gint offset, gint length,
+ guint *offset_ptr, guint *length_ptr, int *exception)
+{
+ g_assert(tvb->initialized);
+
+ if (!compute_offset_length(tvb, offset, length, offset_ptr, length_ptr, exception)) {
+ return FALSE;
+ }
+
+ if (*offset_ptr + *length_ptr <= tvb->length) {
+ return TRUE;
+ }
+ else if (*offset_ptr + *length_ptr <= tvb->reported_length) {
+ if (exception) {
+ *exception = BoundsError;
+ }
+ return FALSE;
+ }
+ else {
+ if (exception) {
+ *exception = ReportedBoundsError;
+ }
+ return FALSE;
+ }
+
+ g_assert_not_reached();
+}
+
+/* Checks (+/-) offset and length and throws BoundsError if
+ * either is out of bounds. Sets integer ptrs to the new offset
+ * and length. */
+static void
+check_offset_length(tvbuff_t *tvb, gint offset, gint length,
+ guint *offset_ptr, guint *length_ptr)
+{
+ int exception = 0;
+
+ if (!check_offset_length_no_exception(tvb, offset, length, offset_ptr, length_ptr, &exception)) {
+ g_assert(exception > 0);
+ THROW(exception);
+ }
+ return;
+}
+
+static void
+add_to_used_in_list(tvbuff_t *tvb, tvbuff_t *used_in)
+{
+ tvb->used_in = g_slist_prepend(tvb->used_in, used_in);
+}
+
+void
+tvb_set_subset(tvbuff_t *tvb, tvbuff_t *backing,
+ gint backing_offset, gint backing_length, gint reported_length)
+{
+ g_assert(tvb->type == TVBUFF_SUBSET);
+ g_assert(!tvb->initialized);
+
+ if (reported_length < -1) {
+ THROW(ReportedBoundsError);
+ }
+
+ check_offset_length(backing, backing_offset, backing_length,
+ &tvb->tvbuffs.subset.offset,
+ &tvb->tvbuffs.subset.length);
+
+ tvb_increment_usage_count(backing, 1);
+ tvb->tvbuffs.subset.tvb = backing;
+ tvb->length = tvb->tvbuffs.subset.length;
+
+ if (reported_length == -1) {
+ tvb->reported_length = backing->reported_length - tvb->tvbuffs.subset.offset;
+ }
+ else {
+ tvb->reported_length = reported_length;
+ }
+ tvb->initialized = TRUE;
+ add_to_used_in_list(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 */
+ if (backing->real_data != NULL) {
+ tvb->real_data = backing->real_data + tvb->tvbuffs.subset.offset;
+ }
+}
+
+
+tvbuff_t*
+tvb_new_subset(tvbuff_t *backing, gint backing_offset, gint backing_length, gint reported_length)
+{
+ tvbuff_t *tvb;
+
+ tvb = tvb_new(TVBUFF_SUBSET);
+
+ CLEANUP_PUSH(tvb_free_void, tvb);
+
+ tvb_set_subset(tvb, backing, backing_offset, backing_length, reported_length);
+
+ CLEANUP_POP;
+
+ return tvb;
+}
+
+void
+tvb_composite_append(tvbuff_t* tvb, tvbuff_t* member)
+{
+ tvb_comp_t *composite;
+
+ g_assert(!tvb->initialized);
+ composite = &tvb->tvbuffs.composite;
+ composite->tvbs = g_slist_append( composite->tvbs, member );
+}
+
+void
+tvb_composite_prepend(tvbuff_t* tvb, tvbuff_t* member)
+{
+ tvb_comp_t *composite;
+
+ g_assert(!tvb->initialized);
+ composite = &tvb->tvbuffs.composite;
+ composite->tvbs = g_slist_prepend( composite->tvbs, member );
+}
+
+tvbuff_t*
+tvb_new_composite(void)
+{
+ return tvb_new(TVBUFF_COMPOSITE);
+}
+
+void
+tvb_composite_finalize(tvbuff_t* tvb)
+{
+ GSList *slist;
+ guint num_members;
+ tvbuff_t *member_tvb;
+ tvb_comp_t *composite;
+ int i = 0;
+
+ g_assert(!tvb->initialized);
+ g_assert(tvb->length == 0);
+
+ composite = &tvb->tvbuffs.composite;
+ num_members = g_slist_length(composite->tvbs);
+
+ composite->start_offsets = g_new(guint, num_members);
+ composite->end_offsets = g_new(guint, num_members);
+
+ for (slist = composite->tvbs; slist != NULL; slist = slist->next) {
+ g_assert(i < num_members);
+ member_tvb = slist->data;
+ composite->start_offsets[i] = tvb->length;
+ tvb->length += member_tvb->length;
+ composite->end_offsets[i] = tvb->length - 1;
+ i++;
+ }
+
+ tvb->initialized = TRUE;
+}
+
+
+
+guint
+tvb_length(tvbuff_t* tvb)
+{
+ g_assert(tvb->initialized);
+
+ return tvb->length;
+}
+
+guint
+tvb_length_remaining(tvbuff_t *tvb, gint offset)
+{
+ guint abs_offset, abs_length;
+
+ g_assert(tvb->initialized);
+
+ if (compute_offset_length(tvb, offset, -1, &abs_offset, &abs_length, NULL)) {
+ return abs_length;
+ }
+ else {
+ return -1;
+ }
+}
+
+
+
+/* Validates that 'length' bytes are available starting from
+ * offset (pos/neg). Does not throw BoundsError exception. */
+gboolean
+tvb_bytes_exist(tvbuff_t *tvb, gint offset, gint length)
+{
+ guint abs_offset, abs_length;
+
+ g_assert(tvb->initialized);
+
+ if (!compute_offset_length(tvb, offset, length, &abs_offset, &abs_length, NULL))
+ return FALSE;
+
+ if (abs_offset + abs_length <= tvb->length) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+gboolean
+tvb_offset_exists(tvbuff_t *tvb, gint offset)
+{
+ guint abs_offset, abs_length;
+
+ g_assert(tvb->initialized);
+ if (!compute_offset_length(tvb, offset, -1, &abs_offset, &abs_length, NULL))
+ return FALSE;
+
+ if (abs_offset < tvb->length) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+guint
+tvb_reported_length(tvbuff_t* tvb)
+{
+ g_assert(tvb->initialized);
+
+ return tvb->reported_length;
+}
+
+
+
+
+static guint8*
+first_real_data_ptr(tvbuff_t *tvb)
+{
+ tvbuff_t *member;
+
+ switch(tvb->type) {
+ case TVBUFF_REAL_DATA:
+ return tvb->real_data;
+ case TVBUFF_SUBSET:
+ member = tvb->tvbuffs.subset.tvb;
+ return first_real_data_ptr(member);
+ case TVBUFF_COMPOSITE:
+ member = tvb->tvbuffs.composite.tvbs->data;
+ return first_real_data_ptr(member);
+ }
+
+ g_assert_not_reached();
+ return NULL;
+}
+
+static int
+offset_from_real_beginning(tvbuff_t *tvb, int counter)
+{
+ tvbuff_t *member;
+
+ switch(tvb->type) {
+ case TVBUFF_REAL_DATA:
+ return counter;
+ case TVBUFF_SUBSET:
+ member = tvb->tvbuffs.subset.tvb;
+ return offset_from_real_beginning(member, counter + tvb->tvbuffs.subset.offset);
+ case TVBUFF_COMPOSITE:
+ member = tvb->tvbuffs.composite.tvbs->data;
+ return offset_from_real_beginning(member, counter);
+ }
+
+ g_assert_not_reached();
+ return 0;
+}
+
+gint
+tvb_raw_offset(tvbuff_t *tvb)
+{
+ if (tvb->raw_offset == -1) {
+ tvb->raw_offset = offset_from_real_beginning(tvb, 0);
+ }
+ return tvb->raw_offset;
+}
+
+void
+tvb_compat(tvbuff_t *tvb, const guint8 **pd, int *offset)
+{
+ g_assert(tvb->initialized);
+ *pd = first_real_data_ptr(tvb);
+ *offset = tvb_raw_offset(tvb);
+}
+
+
+static guint8*
+composite_ensure_contiguous(tvbuff_t *tvb, guint abs_offset, guint abs_length)
+{
+ guint i, num_members;
+ tvb_comp_t *composite;
+ tvbuff_t *member_tvb = NULL;
+ guint member_offset, member_length;
+ GSList *slist;
+
+ g_assert(tvb->type == TVBUFF_COMPOSITE);
+
+ /* Maybe the range specified by offset/length
+ * is contiguous inside one of the member tvbuffs */
+ composite = &tvb->tvbuffs.composite;
+ num_members = g_slist_length(composite->tvbs);
+
+ for (i = 0; i < num_members; i++) {
+ if (abs_offset <= composite->end_offsets[i]) {
+ slist = g_slist_nth(composite->tvbs, i);
+ member_tvb = slist->data;
+ break;
+ }
+ }
+ g_assert(member_tvb);
+
+ if (check_offset_length_no_exception(member_tvb, abs_offset - composite->start_offsets[i],
+ abs_length, &member_offset, &member_length, NULL)) {
+
+ g_assert(!tvb->real_data);
+ return ensure_contiguous(member_tvb, member_offset, member_length);
+ }
+ else {
+ tvb->real_data = tvb_memdup(tvb, 0, -1);
+ return tvb->real_data + abs_offset;
+ }
+
+ g_assert_not_reached();
+ return NULL;
+}
+
+static guint8*
+ensure_contiguous(tvbuff_t *tvb, gint offset, gint length)
+{
+ guint abs_offset, abs_length;
+
+ check_offset_length(tvb, offset, length, &abs_offset, &abs_length);
+
+ if (tvb->real_data) {
+ return tvb->real_data + abs_offset;
+ }
+ else {
+ switch(tvb->type) {
+ case TVBUFF_REAL_DATA:
+ g_assert_not_reached();
+ case TVBUFF_SUBSET:
+ return ensure_contiguous(tvb->tvbuffs.subset.tvb,
+ abs_offset - tvb->tvbuffs.subset.offset,
+ abs_length);
+ case TVBUFF_COMPOSITE:
+ return composite_ensure_contiguous(tvb, abs_offset, abs_length);
+ }
+ }
+
+ g_assert_not_reached();
+ return NULL;
+}
+
+static const guint8*
+guint8_find(const guint8* haystack, size_t haystacklen, guint8 needle)
+{
+ const guint8 *b;
+ int i;
+
+ for (b = haystack, i = 0; i < haystacklen; i++, b++) {
+ if (*b == needle) {
+ return b;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/************** ACCESSORS **************/
+
+static guint8*
+composite_memcpy(tvbuff_t *tvb, guint8* target, guint abs_offset, guint abs_length)
+{
+ guint i, num_members;
+ tvb_comp_t *composite;
+ tvbuff_t *member_tvb = NULL;
+ guint member_offset, member_length;
+ gboolean retval;
+ GSList *slist;
+
+ g_assert(tvb->type == TVBUFF_COMPOSITE);
+
+ /* Maybe the range specified by offset/length
+ * is contiguous inside one of the member tvbuffs */
+ composite = &tvb->tvbuffs.composite;
+ num_members = g_slist_length(composite->tvbs);
+
+ for (i = 0; i < num_members; i++) {
+ if (abs_offset <= composite->end_offsets[i]) {
+ slist = g_slist_nth(composite->tvbs, i);
+ member_tvb = slist->data;
+ break;
+ }
+ }
+ g_assert(member_tvb);
+
+ if (check_offset_length_no_exception(member_tvb, abs_offset - composite->start_offsets[i],
+ abs_length, &member_offset, &member_length, NULL)) {
+
+ g_assert(!tvb->real_data);
+ return tvb_memcpy(member_tvb, target, member_offset, member_length);
+ }
+ else {
+ /* The requested data is non-contiguous inside
+ * the member tvb. We have to memcpy() the part that's in the member tvb,
+ * then iterate across the other member tvb's, copying their portions
+ * until we have copied all data.
+ */
+ retval = compute_offset_length(member_tvb, abs_offset - composite->start_offsets[i], -1,
+ &member_offset, &member_length, NULL);
+ g_assert(retval);
+
+ tvb_memcpy(member_tvb, target, member_offset, member_length);
+ abs_offset += member_length;
+ abs_length -= member_length;
+
+ /* Recurse */
+ if (abs_length > 0) {
+ composite_memcpy(tvb, target + member_length, abs_offset, abs_length);
+ }
+
+ return target;
+ }
+
+ g_assert_not_reached();
+ return NULL;
+}
+
+guint8*
+tvb_memcpy(tvbuff_t *tvb, guint8* target, gint offset, gint length)
+{
+ guint abs_offset, abs_length;
+
+ g_assert(length >= -1);
+ check_offset_length(tvb, offset, length, &abs_offset, &abs_length);
+
+ if (tvb->real_data) {
+ return (guint8*) memcpy(target, tvb->real_data + abs_offset, abs_length);
+ }
+
+ switch(tvb->type) {
+ case TVBUFF_REAL_DATA:
+ g_assert_not_reached();
+
+ case TVBUFF_SUBSET:
+ return tvb_memcpy(tvb->tvbuffs.subset.tvb, target,
+ abs_offset - tvb->tvbuffs.subset.offset,
+ abs_length);
+
+ case TVBUFF_COMPOSITE:
+ return composite_memcpy(tvb, target, offset, length);
+ }
+
+ g_assert_not_reached();
+ return NULL;
+}
+
+
+guint8*
+tvb_memdup(tvbuff_t *tvb, gint offset, gint length)
+{
+ guint abs_offset, abs_length;
+ guint8 *duped;
+
+ check_offset_length(tvb, offset, length, &abs_offset, &abs_length);
+
+ duped = g_malloc(abs_length);
+ return tvb_memcpy(tvb, duped, abs_offset, abs_length);
+}
+
+
+
+guint8*
+tvb_get_ptr(tvbuff_t *tvb, gint offset, gint length)
+{
+ return ensure_contiguous(tvb, offset, length);
+}
+
+guint8
+tvb_get_guint8(tvbuff_t *tvb, gint offset)
+{
+ guint8* ptr;
+
+ ptr = ensure_contiguous(tvb, offset, sizeof(guint8));
+ return *ptr;
+}
+
+guint16
+tvb_get_ntohs(tvbuff_t *tvb, gint offset)
+{
+ guint8* ptr;
+
+ ptr = ensure_contiguous(tvb, offset, sizeof(guint16));
+ return pntohs(ptr);
+}
+
+guint32
+tvb_get_ntoh24(tvbuff_t *tvb, gint offset)
+{
+ guint8* ptr;
+
+ ptr = ensure_contiguous(tvb, offset, 3);
+ return pntoh24(ptr);
+}
+
+guint32
+tvb_get_ntohl(tvbuff_t *tvb, gint offset)
+{
+ guint8* ptr;
+
+ ptr = ensure_contiguous(tvb, offset, sizeof(guint32));
+ return pntohl(ptr);
+}
+
+#ifdef G_HAVE_GINT64
+guint64
+tvb_get_ntohll(tvbuff_t *tvb, gint offset)
+{
+ guint8* ptr;
+
+ ptr = ensure_contiguous(tvb, offset, sizeof(guint64));
+ return pntohll(ptr);
+}
+#endif
+
+guint16
+tvb_get_letohs(tvbuff_t *tvb, gint offset)
+{
+ guint8* ptr;
+
+ ptr = ensure_contiguous(tvb, offset, sizeof(guint16));
+ return pletohs(ptr);
+}
+
+guint32
+tvb_get_letoh24(tvbuff_t *tvb, gint offset)
+{
+ guint8* ptr;
+
+ ptr = ensure_contiguous(tvb, offset, 3);
+ return pletoh24(ptr);
+}
+
+guint32
+tvb_get_letohl(tvbuff_t *tvb, gint offset)
+{
+ guint8* ptr;
+
+ ptr = ensure_contiguous(tvb, offset, sizeof(guint32));
+ return pletohl(ptr);
+}
+
+#ifdef G_HAVE_GINT64
+guint64
+tvb_get_letohll(tvbuff_t *tvb, gint offset)
+{
+ guint8* ptr;
+
+ ptr = ensure_contiguous(tvb, offset, sizeof(guint64));
+ return pletohll(ptr);
+}
+#endif
+
+
+/* Find first occurence of needle in tvbuff, starting at offset. Searches
+ * at most maxlength number of bytes. Returns the offset of the found needle,
+ * or -1 if not found. Will not throw an exception, even if maxlength exceeds
+ * boundary of tvbuff; in that case, -1 will be returned if the boundary is
+ * reached before finding needle. */
+gint
+tvb_find_guint8(tvbuff_t *tvb, gint offset, guint maxlength, guint8 needle)
+{
+ guint abs_offset, junk_length;
+ const guint8 *result;
+ guint limit;
+
+ check_offset_length(tvb, offset, 0, &abs_offset, &junk_length);
+
+ /* Only search to end of tvbuff, w/o throwing exception. */
+ if (tvb_length_remaining(tvb, abs_offset) < maxlength) {
+ limit = maxlength - (tvb_length(tvb) - abs_offset);
+ }
+ else {
+ limit = maxlength;
+ }
+
+ /* If we have real data, perform our search now. */
+ if (tvb->real_data) {
+ result = guint8_find(tvb->real_data + abs_offset, limit, needle);
+ if (result == NULL) {
+ return -1;
+ }
+ else {
+ return result - tvb->real_data;
+ }
+ }
+
+ switch(tvb->type) {
+ case TVBUFF_REAL_DATA:
+ g_assert_not_reached();
+
+ case TVBUFF_SUBSET:
+ return tvb_find_guint8(tvb->tvbuffs.subset.tvb,
+ abs_offset - tvb->tvbuffs.subset.offset,
+ limit, needle);
+
+ case TVBUFF_COMPOSITE:
+ g_assert_not_reached();
+ /* XXX - return composite_find_guint8(tvb, offset, limit, needle); */
+ }
+
+ g_assert_not_reached();
+ return -1;
+}
+
+/* Find length of string by looking for end of string ('\0'), up to
+ * 'max_length' characters'. Returns -1 if 'max_length' reached
+ * before finding EOS. */
+gint
+tvb_strnlen(tvbuff_t *tvb, gint offset, guint maxlength)
+{
+ gint result_offset;
+ guint abs_offset, junk_length;
+
+ check_offset_length(tvb, offset, 0, &abs_offset, &junk_length);
+
+ result_offset = tvb_find_guint8(tvb, abs_offset, maxlength, 0);
+
+ if (result_offset == -1) {
+ return -1;
+ }
+ else {
+ return result_offset;
+ }
+}
+
+/*
+ * Implement strneql etc
+ */
+
+/* Call strncmp after checking if enough chars left, otherwise return -1 */
+gint
+tvb_strneql(tvbuff_t *tvb, gint offset, guint8 *str, gint size)
+{
+ guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, size);
+
+ if (ptr) {
+
+ int cmp = strncmp(ptr, str, size);
+
+ return (cmp == 0 ? 0 : -1); /* Make it -1 if comparison failed */
+
+ }
+ else {
+
+ return -1; /* Not enough chars in the TVB */
+
+ }
+
+}
+
+/*
+ * Format the data in the tvb from offset for length ...
+ */
+
+guint8 *
+tvb_format_text(tvbuff_t *tvb, gint offset, gint size)
+{
+ guint8 *ptr;
+ gint len = size;
+
+ if ((ptr = ensure_contiguous(tvb, offset, size)) == NULL) {
+
+ len = tvb_length_remaining(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, len);
+
+ }
+
+ return format_text(ptr, len);
+
+}
+
+/* Looks for a stringz (NUL-terminated string) in tvbuff and copies
+ * no more than maxlength number of bytes, including terminating NUL, to buffer.
+ * Returns length of string (not including terminating NUL), or -1 if the string was
+ * truncated in the buffer due to not having reached the terminating NUL.
+ * In this way, it acts like snprintf().
+ */
+gint
+tvb_get_nstringz(tvbuff_t *tvb, gint offset, guint maxlength, guint8* buffer)
+{
+ gint stringlen, NUL_offset;
+ guint abs_offset, junk_length;
+ gint limit;
+
+ check_offset_length(tvb, offset, 0, &abs_offset, &junk_length);
+
+ if (maxlength == 0) {
+ buffer[0] = 0;
+ return 0;
+ }
+
+ /* Only copy to end of tvbuff, w/o throwing exception. */
+ if (tvb_length_remaining(tvb, abs_offset) < maxlength) {
+ limit = maxlength - (tvb_length(tvb) - abs_offset);
+ }
+ else {
+ limit = maxlength;
+ }
+
+ NUL_offset = tvb_strnlen(tvb, abs_offset, limit);
+
+ /* If NUL wasn't found, copy the data and return -1 */
+ if (NUL_offset == -1) {
+ tvb_memcpy(tvb, buffer, abs_offset, limit);
+ return -1;
+ }
+
+ /* Copy the string to buffer */
+ stringlen = NUL_offset - abs_offset;
+ tvb_memcpy(tvb, buffer, abs_offset, stringlen + 1);
+ return stringlen;
+}
+
+/* Like tvb_get_nstringz(), but never returns -1. The string is guaranteed to
+ * have a terminating NUL. If the string was truncated when copied into buffer,
+ * a NUL is placed at the end of buffer to terminate it.
+ */
+gint
+tvb_get_nstringz0(tvbuff_t *tvb, gint offset, guint maxlength, guint8* buffer)
+{
+ gint len;
+
+ len = tvb_get_nstringz(tvb, offset, maxlength, buffer);
+
+ if (len == -1) {
+ buffer[maxlength] = 0;
+ return maxlength - 1;
+ }
+ else {
+ return len;
+ }
+}