(Trivial) Fix spellling in a comment.
[metze/wireshark/wip.git] / epan / tvbuff_composite.c
1 /* tvbuff_composite.c
2  *
3  * $Id$
4  *
5  * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26 #include "config.h"
27
28 #include <epan/emem.h>
29
30 #include "tvbuff.h"
31 #include "tvbuff-int.h"
32 #include "proto.h"      /* XXX - only used for DISSECTOR_ASSERT, probably a new header file? */
33
34 typedef struct {
35         GSList          *tvbs;
36
37         /* Used for quick testing to see if this
38          * is the tvbuff that a COMPOSITE is
39          * interested in. */
40         guint           *start_offsets;
41         guint           *end_offsets;
42
43 } tvb_comp_t;
44
45 struct tvb_composite {
46         struct tvbuff tvb;
47
48         tvb_comp_t      composite;
49 };
50
51 static void
52 composite_free(tvbuff_t *tvb)
53 {
54         struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
55         tvb_comp_t *composite = &composite_tvb->composite;
56
57         g_slist_free(composite->tvbs);
58
59         g_free(composite->start_offsets);
60         g_free(composite->end_offsets);
61         if (tvb->real_data) {
62                 /*
63                  * XXX - do this with a union?
64                  */
65                 g_free((gpointer)tvb->real_data);
66         }
67 }
68
69 static guint
70 composite_offset(const tvbuff_t *tvb, const guint counter)
71 {
72         const struct tvb_composite *composite_tvb = (const struct tvb_composite *) tvb;
73         const tvbuff_t *member = (const tvbuff_t *)composite_tvb->composite.tvbs->data;
74
75         return tvb_offset_from_real_beginning_counter(member, counter);
76 }
77
78 static const guint8*
79 composite_get_ptr(tvbuff_t *tvb, guint abs_offset, guint abs_length)
80 {
81         struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
82         guint       i, num_members;
83         tvb_comp_t *composite;
84         tvbuff_t   *member_tvb = NULL;
85         guint       member_offset;
86         GSList     *slist;
87
88         /* DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops); */
89
90         /* Maybe the range specified by offset/length
91          * is contiguous inside one of the member tvbuffs */
92         composite = &composite_tvb->composite;
93         num_members = g_slist_length(composite->tvbs);
94
95         for (i = 0; i < num_members; i++) {
96                 if (abs_offset <= composite->end_offsets[i]) {
97                         slist = g_slist_nth(composite->tvbs, i);
98                         member_tvb = (tvbuff_t *)slist->data;
99                         break;
100                 }
101         }
102
103         /* special case */
104         if (!member_tvb) {
105                 DISSECTOR_ASSERT(abs_offset == tvb->length && abs_length == 0);
106                 return "";
107         }
108
109         member_offset = abs_offset - composite->start_offsets[i];
110
111         if (tvb_bytes_exist(member_tvb, member_offset, abs_length)) {
112                 /*
113                  * The range is, in fact, contiguous within member_tvb.
114                  */
115                 DISSECTOR_ASSERT(!tvb->real_data);
116                 return tvb_get_ptr(member_tvb, member_offset, abs_length);
117         }
118         else {
119                 tvb->real_data = (guint8 *)tvb_memdup(NULL, tvb, 0, -1);
120                 return tvb->real_data + abs_offset;
121         }
122
123         DISSECTOR_ASSERT_NOT_REACHED();
124 }
125
126 static void *
127 composite_memcpy(tvbuff_t *tvb, void* _target, guint abs_offset, guint abs_length)
128 {
129         struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
130         guint8 *target = (guint8 *) _target;
131
132         guint       i, num_members;
133         tvb_comp_t *composite;
134         tvbuff_t   *member_tvb = NULL;
135         guint       member_offset, member_length;
136         GSList     *slist;
137
138         /* DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops); */
139
140         /* Maybe the range specified by offset/length
141          * is contiguous inside one of the member tvbuffs */
142         composite   = &composite_tvb->composite;
143         num_members = g_slist_length(composite->tvbs);
144
145         for (i = 0; i < num_members; i++) {
146                 if (abs_offset <= composite->end_offsets[i]) {
147                         slist = g_slist_nth(composite->tvbs, i);
148                         member_tvb = (tvbuff_t *)slist->data;
149                         break;
150                 }
151         }
152
153         /* special case */
154         if (!member_tvb) {
155                 DISSECTOR_ASSERT(abs_offset == tvb->length && abs_length == 0);
156                 return target;
157         }
158
159         member_offset = abs_offset - composite->start_offsets[i];
160
161         if (tvb_bytes_exist(member_tvb, member_offset, abs_length)) {
162                 DISSECTOR_ASSERT(!tvb->real_data);
163                 return tvb_memcpy(member_tvb, target, member_offset, abs_length);
164         }
165         else {
166                 /* The requested data is non-contiguous inside
167                  * the member tvb. We have to memcpy() the part that's in the member tvb,
168                  * then iterate across the other member tvb's, copying their portions
169                  * until we have copied all data.
170                  */
171                 member_length = tvb_length_remaining(member_tvb, member_offset);
172
173                 /* composite_memcpy() can't handle a member_length of zero. */
174                 DISSECTOR_ASSERT(member_length > 0);
175
176                 tvb_memcpy(member_tvb, target, member_offset, member_length);
177                 abs_offset      += member_length;
178                 abs_length      -= member_length;
179
180                 /* Recurse */
181                 if (abs_length > 0) {
182                         composite_memcpy(tvb, target + member_length, abs_offset, abs_length);
183                 }
184
185                 return target;
186         }
187
188         DISSECTOR_ASSERT_NOT_REACHED();
189 }
190
191 static const struct tvb_ops tvb_composite_ops = {
192         sizeof(struct tvb_composite), /* size */
193
194         composite_free,       /* free */
195         composite_offset,     /* offset */
196         composite_get_ptr,    /* get_ptr */
197         composite_memcpy,     /* memcpy */
198         NULL,                 /* find_guint8 XXX */
199         NULL,                 /* pbrk_guint8 XXX */
200         NULL,                 /* clone */
201 };
202
203 /*
204  * Composite tvb
205  *
206  *   1. A composite tvb is automatically chained to its first member when the
207  *      tvb is finalized.
208  *      This means that composite tvb members must all be in the same chain.
209  *      ToDo: enforce this: By searching the chain?
210  */
211 tvbuff_t *
212 tvb_new_composite(void)
213 {
214         tvbuff_t *tvb = tvb_new(&tvb_composite_ops);
215         struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
216         tvb_comp_t *composite = &composite_tvb->composite;
217
218         composite->tvbs          = NULL;
219         composite->start_offsets = NULL;
220         composite->end_offsets   = NULL;
221
222         return tvb;
223 }
224
225 void
226 tvb_composite_append(tvbuff_t *tvb, tvbuff_t *member)
227 {
228         struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
229         tvb_comp_t *composite;
230
231         DISSECTOR_ASSERT(tvb && !tvb->initialized);
232         DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops);
233
234         /* Don't allow zero-length TVBs: composite_memcpy() can't handle them
235          * and anyway it makes no sense.
236          */
237         DISSECTOR_ASSERT(member->length);
238
239         composite       = &composite_tvb->composite;
240         composite->tvbs = g_slist_append(composite->tvbs, member);
241 }
242
243 void
244 tvb_composite_prepend(tvbuff_t *tvb, tvbuff_t *member)
245 {
246         struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
247         tvb_comp_t *composite;
248
249         DISSECTOR_ASSERT(tvb && !tvb->initialized);
250         DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops);
251
252         /* Don't allow zero-length TVBs: composite_memcpy() can't handle them
253          * and anyway it makes no sense.
254          */
255         DISSECTOR_ASSERT(member->length);
256
257         composite       = &composite_tvb->composite;
258         composite->tvbs = g_slist_prepend(composite->tvbs, member);
259 }
260
261 void
262 tvb_composite_finalize(tvbuff_t *tvb)
263 {
264         struct tvb_composite *composite_tvb = (struct tvb_composite *) tvb;
265         GSList     *slist;
266         guint       num_members;
267         tvbuff_t   *member_tvb;
268         tvb_comp_t *composite;
269         int         i = 0;
270
271         DISSECTOR_ASSERT(tvb && !tvb->initialized);
272         DISSECTOR_ASSERT(tvb->ops == &tvb_composite_ops);
273         DISSECTOR_ASSERT(tvb->length == 0);
274         DISSECTOR_ASSERT(tvb->reported_length == 0);
275
276         composite   = &composite_tvb->composite;
277         num_members = g_slist_length(composite->tvbs);
278
279         /* Dissectors should not create composite TVBs if they're not going to
280          * put at least one TVB in them.
281          * (Without this check--or something similar--we'll seg-fault below.)
282          */
283         DISSECTOR_ASSERT(num_members);
284
285         composite->start_offsets = g_new(guint, num_members);
286         composite->end_offsets = g_new(guint, num_members);
287
288         for (slist = composite->tvbs; slist != NULL; slist = slist->next) {
289                 DISSECTOR_ASSERT((guint) i < num_members);
290                 member_tvb = (tvbuff_t *)slist->data;
291                 composite->start_offsets[i] = tvb->length;
292                 tvb->length += member_tvb->length;
293                 tvb->reported_length += member_tvb->reported_length;
294                 composite->end_offsets[i] = tvb->length - 1;
295                 i++;
296         }
297         tvb_add_to_chain((tvbuff_t *)composite->tvbs->data, tvb); /* chain composite tvb to first member */
298         tvb->initialized = TRUE;
299 }