Add a pair of #defines to emem.c that make ep_alloc() and se_alloc()
[metze/wireshark/wip.git] / epan / emem.c
1 /* emem.c
2  * Ethereal memory management and garbage collection functions
3  * Ronnie Sahlberg 2005
4  *
5  * $Id$
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <glib.h>
34 #include <proto.h>
35 #include "emem.h"
36
37 /* When required, allocate more memory from the OS in this size chunks */
38 #define EMEM_PACKET_CHUNK_SIZE 10485760
39
40 /*
41  * Tools like Valgrind and ElectricFence don't work well with memchunks. 
42  * Uncomment the defines below to make {ep|se}_alloc() allocate each
43  * object individually.
44  */
45 /* #define EP_DEBUG_FREE 1 */
46 /* #define SE_DEBUG_FREE 1 */
47
48 typedef struct _emem_chunk_t {
49         struct _emem_chunk_t *next;
50         unsigned int    amount_free;
51         unsigned int    free_offset;
52         char *buf;
53 } emem_chunk_t;
54
55 typedef struct _emem_header_t {
56   emem_chunk_t *free_list;
57   emem_chunk_t *used_list;
58 } emem_header_t;
59
60 static emem_header_t ep_packet_mem;
61 static emem_header_t se_packet_mem;
62
63 /* Initialize the packet-lifetime memory allocation pool.
64  * This function should be called only once when Etehreal or Tethereal starts
65  * up.
66  */
67 void
68 ep_init_chunk(void)
69 {
70         ep_packet_mem.free_list=NULL;   
71         ep_packet_mem.used_list=NULL;   
72 }
73 /* Initialize the capture-lifetime memory allocation pool.
74  * This function should be called only once when Etehreal or Tethereal starts
75  * up.
76  */
77 void
78 se_init_chunk(void)
79 {
80         se_packet_mem.free_list=NULL;   
81         se_packet_mem.used_list=NULL;   
82 }
83
84 /* allocate 'size' amount of memory with an allocation lifetime until the
85  * next packet.
86  */
87 void *
88 ep_alloc(size_t size)
89 {
90         void *buf;
91
92 #ifndef EP_DEBUG_FREE
93         /* round up to 8 byte boundary */
94         if(size&0x07){
95                 size=(size+7)&0xfffffff8;
96         }
97
98         /* make sure we dont try to allocate too much (arbitrary limit) */
99         DISSECTOR_ASSERT(size<(EMEM_PACKET_CHUNK_SIZE>>2));
100
101         /* we dont have any free data, so we must allocate a new one */
102         if(!ep_packet_mem.free_list){
103                 emem_chunk_t *npc;
104                 npc=g_malloc(sizeof(emem_chunk_t));
105                 npc->next=NULL;
106                 npc->amount_free=EMEM_PACKET_CHUNK_SIZE;
107                 npc->free_offset=0;
108                 npc->buf=g_malloc(EMEM_PACKET_CHUNK_SIZE);
109                 ep_packet_mem.free_list=npc;
110         }
111
112         /* oops, we need to allocate more memory to serve this request
113          * than we have free. move this node to the used list and try again
114          */
115         if(size>ep_packet_mem.free_list->amount_free){
116                 emem_chunk_t *npc;
117                 npc=ep_packet_mem.free_list;
118                 ep_packet_mem.free_list=ep_packet_mem.free_list->next;
119                 npc->next=ep_packet_mem.used_list;
120                 ep_packet_mem.used_list=npc;
121         }
122
123         /* we dont have any free data, so we must allocate a new one */
124         if(!ep_packet_mem.free_list){
125                 emem_chunk_t *npc;
126                 npc=g_malloc(sizeof(emem_chunk_t));
127                 npc->next=NULL;
128                 npc->amount_free=EMEM_PACKET_CHUNK_SIZE;
129                 npc->free_offset=0;
130                 npc->buf=g_malloc(EMEM_PACKET_CHUNK_SIZE);
131                 ep_packet_mem.free_list=npc;
132         }
133
134
135         buf=ep_packet_mem.free_list->buf+ep_packet_mem.free_list->free_offset;
136
137         ep_packet_mem.free_list->amount_free-=size;
138         ep_packet_mem.free_list->free_offset+=size;
139
140 #else /* EP_DEBUG_FREE */
141         emem_chunk_t *npc;
142
143         npc=g_malloc(sizeof(emem_chunk_t));
144         npc->next=ep_packet_mem.used_list;
145         npc->amount_free=size;
146         npc->free_offset=0;
147         npc->buf=g_malloc(size);
148         buf = npc->buf;
149         ep_packet_mem.used_list=npc;
150 #endif /* EP_DEBUG_FREE */
151
152         return buf;
153 }
154 /* allocate 'size' amount of memory with an allocation lifetime until the
155  * next capture.
156  */
157 void *
158 se_alloc(size_t size)
159 {
160         void *buf;
161
162 #ifndef SE_DEBUG_FREE
163         /* round up to 8 byte boundary */
164         if(size&0x07){
165                 size=(size+7)&0xfffffff8;
166         }
167
168         /* make sure we dont try to allocate too much (arbitrary limit) */
169         DISSECTOR_ASSERT(size<(EMEM_PACKET_CHUNK_SIZE>>2));
170
171         /* we dont have any free data, so we must allocate a new one */
172         if(!se_packet_mem.free_list){
173                 emem_chunk_t *npc;
174                 npc=g_malloc(sizeof(emem_chunk_t));
175                 npc->next=NULL;
176                 npc->amount_free=EMEM_PACKET_CHUNK_SIZE;
177                 npc->free_offset=0;
178                 npc->buf=g_malloc(EMEM_PACKET_CHUNK_SIZE);
179                 se_packet_mem.free_list=npc;
180         }
181
182         /* oops, we need to allocate more memory to serve this request
183          * than we have free. move this node to the used list and try again
184          */
185         if(size>se_packet_mem.free_list->amount_free){
186                 emem_chunk_t *npc;
187                 npc=se_packet_mem.free_list;
188                 se_packet_mem.free_list=se_packet_mem.free_list->next;
189                 npc->next=se_packet_mem.used_list;
190                 se_packet_mem.used_list=npc;
191         }
192
193         /* we dont have any free data, so we must allocate a new one */
194         if(!se_packet_mem.free_list){
195                 emem_chunk_t *npc;
196                 npc=g_malloc(sizeof(emem_chunk_t));
197                 npc->next=NULL;
198                 npc->amount_free=EMEM_PACKET_CHUNK_SIZE;
199                 npc->free_offset=0;
200                 npc->buf=g_malloc(EMEM_PACKET_CHUNK_SIZE);
201                 se_packet_mem.free_list=npc;
202         }
203
204
205         buf=se_packet_mem.free_list->buf+se_packet_mem.free_list->free_offset;
206
207         se_packet_mem.free_list->amount_free-=size;
208         se_packet_mem.free_list->free_offset+=size;
209
210 #else /* SE_DEBUG_FREE */
211         emem_chunk_t *npc;
212
213         npc=g_malloc(sizeof(emem_chunk_t));
214         npc->next=se_packet_mem_used_list;
215         npc->amount_free=size;
216         npc->free_offset=0;
217         npc->buf=g_malloc(size);
218         buf = npc->buf;
219         se_packet_mem.used_list=npc;
220 #endif /* SE_DEBUG_FREE */
221
222         return buf;
223 }
224
225
226 void* ep_alloc0(size_t size) {
227         return memset(ep_alloc(size),'\0',size);
228 }
229
230 gchar* ep_strdup(const gchar* src) {
231         guint len = strlen(src);
232         gchar* dst;
233         
234         dst = strncpy(ep_alloc(len+1), src, len);
235
236         dst[len] = '\0';
237         
238         return dst;
239 }
240
241 gchar* ep_strndup(const gchar* src, size_t len) {
242         guint actual_len = strlen(src);
243         gchar* dst;
244         
245         if (len > actual_len)
246                 len = actual_len;
247         
248         dst = strncpy(ep_alloc(len+1), src, len);
249         
250         dst[len] = '\0';
251         
252         return dst;
253 }
254
255 guint8* ep_memdup(const guint8* src, size_t len) {
256         return memcpy(ep_alloc(len), src, len);
257 }
258
259 gchar* ep_strdup_printf(const gchar* fmt, ...) {
260         va_list ap;
261         guint len;
262         gchar* dst;
263         
264         va_start(ap,fmt);
265         len = g_printf_string_upper_bound (fmt, ap);
266         
267         dst = ep_alloc(len+1);
268         g_vsnprintf (dst, len, fmt, ap);
269         
270         va_end(ap);
271         return dst;
272 }
273
274 gchar** ep_strsplit(const gchar* string, const gchar* sep, int max_tokens) {
275         gchar* splitted;
276         gchar* s;
277         guint tokens;
278         guint str_len;
279         guint sep_len;
280         guint i;
281         gchar** vec;
282         enum { AT_START, IN_PAD, IN_TOKEN } state;
283         guint curr_tok = 0;
284         
285         if ( ! string 
286                  || ! sep
287                  || ! sep[0])
288                 return NULL;
289         
290         s = splitted = ep_strdup(string);
291         str_len = strlen(splitted);
292         sep_len = strlen(sep);
293         
294         if (max_tokens < 1) max_tokens = INT_MAX;
295
296         tokens = 1;
297         
298                 
299         while (tokens <= (guint)max_tokens && ( s = strstr(s,sep) )) {
300                 tokens++;
301                 
302                 for(i=0; i < sep_len; i++ )
303                         s[i] = '\0';
304                 
305                 s += sep_len;
306                 
307         } 
308         
309         vec = ep_alloc_array(gchar*,tokens+1);
310         state = AT_START;
311         
312         for (i=0; i< str_len; i++) {
313                 switch(state) {
314                         case AT_START:
315                                 switch(splitted[i]) {
316                                         case '\0':
317                                                 state  = IN_PAD;
318                                                 continue;
319                                         default:
320                                                 vec[curr_tok] = &(splitted[i]);
321                                                 curr_tok++;
322                                                 state = IN_TOKEN;
323                                                 continue;
324                                 }
325                         case IN_TOKEN:
326                                 switch(splitted[i]) {
327                                         case '\0':
328                                                 state = IN_PAD;
329                                         default:
330                                                 continue;
331                                 }
332                         case IN_PAD:
333                                 switch(splitted[i]) {
334                                         default:
335                                                 vec[curr_tok] = &(splitted[i]);
336                                                 curr_tok++;
337                                                 state = IN_TOKEN;
338                                         case '\0':
339                                                 continue;
340                                 }
341                 }
342         }
343         
344         vec[curr_tok] = NULL;
345         
346         return vec;
347 }
348
349
350
351 void* se_alloc0(size_t size) {
352         return memset(se_alloc(size),'\0',size);
353 }
354
355 gchar* se_strdup(const gchar* src) {
356         guint len = strlen(src);
357         gchar* dst;
358         
359         dst = strncpy(se_alloc(len+1), src, len);
360         
361         dst[len] = '\0';
362         
363         return dst;
364 }
365
366 gchar* se_strndup(const gchar* src, size_t len) {
367         guint actual_len = strlen(src);
368         gchar* dst;
369         
370         if (len > actual_len)
371                 len = actual_len;
372         
373         dst = strncpy(se_alloc(len+1), src, len);
374         
375         dst[len] = '\0';
376         
377         return dst;
378 }
379
380 guint8* se_memdup(const guint8* src, size_t len) {
381         return memcpy(se_alloc(len), src, len);
382 }
383
384 gchar* se_strdup_printf(const gchar* fmt, ...) {
385         va_list ap;
386         guint len;
387         gchar* dst;
388         
389         va_start(ap,fmt);
390         len = g_printf_string_upper_bound (fmt, ap);
391         
392         dst = se_alloc(len+1);
393         g_vsnprintf (dst, len, fmt, ap);
394         
395         va_end(ap);
396         return dst;
397 }
398
399 /* release all allocated memory back to the pool.
400  */
401 void
402 ep_free_all(void)
403 {
404         emem_chunk_t *npc;
405
406         /* move all used chunks over to the free list */
407         while(ep_packet_mem.used_list){
408                 npc=ep_packet_mem.used_list;
409                 ep_packet_mem.used_list=ep_packet_mem.used_list->next;
410                 npc->next=ep_packet_mem.free_list;
411                 ep_packet_mem.free_list=npc;
412         }
413
414         /* clear them all out */
415         npc = ep_packet_mem.free_list;
416         while (npc != NULL) {
417 #ifndef EP_DEBUG_FREE
418                 npc->amount_free=EMEM_PACKET_CHUNK_SIZE;
419                 npc->free_offset=0;
420                 npc = npc->next;
421 #else /* EP_DEBUG_FREE */
422                 emem_chunk_t *next = npc->next;
423
424                 g_free(npc->buf);
425                 g_free(npc);
426                 npc = next;
427 #endif /* EP_DEBUG_FREE */
428         }
429
430 #ifdef EP_DEBUG_FREE
431         ep_init_chunk();
432 #endif
433 }
434 /* release all allocated memory back to the pool.
435  */
436 void
437 se_free_all(void)
438 {
439         emem_chunk_t *npc;
440
441         /* move all used chunks ove to the free list */
442         while(se_packet_mem.used_list){
443                 npc=se_packet_mem.used_list;
444                 se_packet_mem.used_list=se_packet_mem.used_list->next;
445                 npc->next=se_packet_mem.free_list;
446                 se_packet_mem.free_list=npc;
447         }
448
449         /* clear them all out */
450         npc = se_packet_mem.free_list;
451         while (npc != NULL) {
452 #ifndef SE_DEBUG_FREE
453                 npc->amount_free=EMEM_PACKET_CHUNK_SIZE;
454                 npc->free_offset=0;
455                 npc = npc->next;
456 #else /* SE_DEBUG_FREE */
457                 emem_chunk_t *next = npc->next;
458
459                 g_free(npc->buf);
460                 g_free(npc);
461                 npc = next;
462 #endif /* SE_DEBUG_FREE */
463         }
464
465 #ifdef SE_DEBUG_FREE
466                 se_init_chunk();
467 #endif
468 }
469                 
470
471