r2049: talloc now has destructors and reference counts
[bbaumbach/samba-autobuild/.git] / source4 / lib / talloc.c
1 /* 
2    Samba Unix SMB/CIFS implementation.
3
4    Samba temporary memory allocation functions - new interface
5
6    Copyright (C) Andrew Tridgell 2004
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 /*
24   inspired by http://swapped.cc/halloc/
25 */
26
27 #include "includes.h"
28
29 #define MAX_TALLOC_SIZE 0x10000000
30 #define TALLOC_MAGIC 0xe814ec4f
31 #define TALLOC_MAGIC_FREE 0x7faebef3
32
33 struct talloc_chunk {
34         struct talloc_chunk *next, *prev;
35         struct talloc_chunk *parent, *child;
36         size_t size;
37         uint_t magic;
38         uint_t ref_count;
39         int (*destructor)(void *);
40         char *name;
41 };
42
43 /* panic if we get a bad magic value */
44 static struct talloc_chunk *talloc_chunk_from_ptr(void *ptr)
45 {
46         struct talloc_chunk *tc = ((struct talloc_chunk *)ptr)-1;
47         if (tc->magic != TALLOC_MAGIC) {
48                 if (tc->magic == TALLOC_MAGIC_FREE) {
49                         smb_panic("Bad talloc magic value - double free\n");
50                 } else {
51                         smb_panic("Bad talloc magic value\n");
52                 }
53         }
54         return tc;
55 }
56
57 /* 
58    Allocate a bit of memory as a child of an existing pointer
59 */
60 void *talloc(void *context, size_t size)
61 {
62         struct talloc_chunk *tc;
63
64         if (size >= MAX_TALLOC_SIZE) {
65                 return NULL;
66         }
67
68         tc = malloc(sizeof(*tc)+size);
69         if (tc == NULL) {
70                 return NULL;
71         }
72
73         tc->size = size;
74         tc->magic = TALLOC_MAGIC;
75         tc->ref_count = 1;
76         tc->destructor = NULL;
77         tc->child = NULL;
78         tc->name = NULL;
79
80         if (context) {
81                 struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
82
83                 tc->parent = parent;
84
85                 if (parent->child) {
86                         parent->child->parent = NULL;
87                 }
88
89                 DLIST_ADD(parent->child, tc);
90         } else {
91                 tc->next = tc->prev = tc->parent = NULL;
92         }
93
94         return (void *)(tc+1);
95 }
96
97
98 /*
99   setup a destructor to be called on free of a pointer
100   the destructor should return 0 on success, or -1 on failure.
101   if the destructor fails then the free is failed, and the memory can
102   be continued to be used
103 */
104 void talloc_set_destructor(void *ptr, int (*destructor)(void *))
105 {
106         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
107         tc->destructor = destructor;
108 }
109
110 /*
111   increase the reference count on a piece of memory. To decrease the 
112   reference count call talloc_free(), which will free the memory if 
113   the reference count reaches zero
114 */
115 void talloc_increase_ref_count(void *ptr)
116 {
117         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
118         tc->ref_count++;
119 }
120
121
122 /*
123   add a name to an existing pointer - va_list version
124 */
125 static void talloc_set_name_v(void *ptr, const char *fmt, va_list ap)
126 {
127         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
128         vasprintf(&tc->name, fmt, ap);
129 }
130
131 /*
132   add a name to an existing pointer
133 */
134 void talloc_set_name(void *ptr, const char *fmt, ...) _PRINTF_ATTRIBUTE(2,3)
135 {
136         va_list ap;
137         va_start(ap, fmt);
138         talloc_set_name_v(ptr, fmt, ap);
139         va_end(ap);
140 }
141
142 /*
143   create a named talloc pointer. Any talloc pointer can be named, and
144   talloc_named() operates just like talloc() except that it allows you
145   to name the pointer.
146 */
147 void *talloc_named(void *context, size_t size, 
148                    const char *fmt, ...) _PRINTF_ATTRIBUTE(3,4)
149 {
150         va_list ap;
151         void *ptr;
152
153         ptr = talloc(context, size);
154         if (ptr == NULL) {
155                 return NULL;
156         }
157
158         va_start(ap, fmt);
159         talloc_set_name_v(ptr, fmt, ap);
160         va_end(ap);
161
162         return ptr;
163 }
164
165 /*
166   this is for compatibility with older versions of talloc
167 */
168 void *talloc_init(const char *fmt, ...) _PRINTF_ATTRIBUTE(1,2)
169 {
170         va_list ap;
171         void *ptr;
172
173         ptr = talloc(NULL, 0);
174         if (ptr == NULL) {
175                 return NULL;
176         }
177
178         va_start(ap, fmt);
179         talloc_set_name_v(ptr, fmt, ap);
180         va_end(ap);
181
182         return ptr;
183 }
184
185
186
187 /* 
188    free a talloc pointer. This also frees all child pointers of this 
189    pointer recursively
190
191    return 0 if the memory is actually freed, otherwise -1. The memory
192    will not be freed if the ref_count is > 1 or the destructor (if
193    any) returns non-zero
194 */
195 int talloc_free(void *ptr)
196 {
197         struct talloc_chunk *tc;
198
199         if (ptr == NULL) {
200                 return -1;
201         }
202
203         tc = talloc_chunk_from_ptr(ptr);
204
205         tc->ref_count--;
206         if (tc->ref_count != 0) {
207                 return -1;
208         }
209
210         if (tc->destructor && tc->destructor(ptr) == -1) {
211                 tc->ref_count++;
212                 return -1;
213         }
214
215         while (tc->child) {
216                 if (talloc_free(tc->child + 1) != 0) {
217                         tc->child->parent = NULL;
218                         break;
219                 }
220         }
221
222         if (tc->parent) {
223                 DLIST_REMOVE(tc->parent->child, tc);
224                 if (tc->parent->child) {
225                         tc->parent->child->parent = tc->parent;
226                 }
227         } else {
228                 if (tc->prev) tc->prev->next = tc->next;
229                 if (tc->next) tc->next->prev = tc->prev;
230         }
231
232         tc->magic = TALLOC_MAGIC_FREE;
233         if (tc->name) free(tc->name);
234
235         free(tc);
236         return 0;
237 }
238
239
240
241 /*
242   A talloc version of realloc 
243 */
244 void *talloc_realloc(void *ptr, size_t size)
245 {
246         struct talloc_chunk *tc;
247         void *new_ptr;
248
249         /* size zero is equivalent to free() */
250         if (size == 0) {
251                 talloc_free(ptr);
252                 return NULL;
253         }
254
255         /* realloc(NULL) is equavalent to malloc() */
256         if (ptr == NULL) {
257                 return talloc(NULL, size);
258         }
259
260         tc = talloc_chunk_from_ptr(ptr);
261
262         /* by resetting magic we catch users of the old memory */
263         tc->magic = TALLOC_MAGIC_FREE;
264
265         new_ptr = realloc(tc, size + sizeof(*tc));
266         if (!new_ptr) {
267                 tc->magic = TALLOC_MAGIC;
268                 return NULL;
269         }
270
271         tc = new_ptr;
272         tc->magic = TALLOC_MAGIC;
273         if (tc->parent) {
274                 tc->parent->child = new_ptr;
275         }
276
277         if (tc->prev) {
278                 tc->prev->next = tc;
279         }
280         if (tc->next) {
281                 tc->next->prev = tc;
282         }
283
284         tc->size = size;
285
286         return (void *)(tc+1);
287 }
288
289 /* 
290    move a lump of memory from one talloc context to another return the
291    ptr on success, or NUL if it could not be transferred
292 */
293 void *talloc_steal(void *new_ctx, void *ptr)
294 {
295         struct talloc_chunk *tc, *new_tc;
296
297         if (!ptr) {
298                 return NULL;
299         }
300
301         tc = talloc_chunk_from_ptr(ptr);
302         new_tc = talloc_chunk_from_ptr(new_ctx);
303
304         if (tc == new_tc) {
305                 return ptr;
306         }
307
308         if (tc->parent) {
309                 DLIST_REMOVE(tc->parent->child, tc);
310                 if (tc->parent->child) {
311                         tc->parent->child->parent = tc->parent;
312                 }
313         } else {
314                 if (tc->prev) tc->prev->next = tc->next;
315                 if (tc->next) tc->next->prev = tc->prev;
316         }
317
318         tc->parent = new_tc;
319         if (new_tc->child) new_tc->child->parent = NULL;
320         DLIST_ADD(new_tc->child, tc);
321
322         return ptr;
323 }
324
325 /*
326   return the total size of a talloc pool (subtree)
327 */
328 off_t talloc_total_size(void *ptr)
329 {
330         off_t total = 0;
331         struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
332
333         total = tc->size;
334         for (c=tc->child;c;c=c->next) {
335                 total += talloc_total_size(c+1);
336         }
337         return total;
338 }
339
340
341 /* 
342    talloc and zero memory. 
343 */
344 void *talloc_zero(void *t, size_t size)
345 {
346         void *p = talloc(t, size);
347
348         if (p) {
349                 memset(p, '\0', size);
350         }
351
352         return p;
353 }
354
355
356 /*
357   memdup with a talloc. 
358 */
359 void *talloc_memdup(void *t, const void *p, size_t size)
360 {
361         void *newp = talloc(t,size);
362
363         if (newp) {
364                 memcpy(newp, p, size);
365         }
366
367         return newp;
368 }
369
370 /*
371   strdup with a talloc 
372 */
373 char *talloc_strdup(void *t, const char *p)
374 {
375         if (!p) {
376                 return NULL;
377         }
378         return talloc_memdup(t, p, strlen(p) + 1);
379 }
380
381 /*
382   strndup with a talloc 
383 */
384 char *talloc_strndup(void *t, const char *p, size_t n)
385 {
386         size_t len = strnlen(p, n);
387         char *ret;
388
389         ret = talloc(t, len + 1);
390         if (!ret) { return NULL; }
391         memcpy(ret, p, len);
392         ret[len] = 0;
393         return ret;
394 }
395
396  char *talloc_vasprintf(void *t, const char *fmt, va_list ap)
397 {       
398         int len;
399         char *ret;
400         va_list ap2;
401         
402         VA_COPY(ap2, ap);
403
404         len = vsnprintf(NULL, 0, fmt, ap2);
405
406         ret = talloc(t, len+1);
407         if (ret) {
408                 VA_COPY(ap2, ap);
409                 vsnprintf(ret, len+1, fmt, ap2);
410         }
411
412         return ret;
413 }
414
415
416 /*
417   Perform string formatting, and return a pointer to newly allocated
418   memory holding the result, inside a memory pool.
419  */
420 char *talloc_asprintf(void *t, const char *fmt, ...) _PRINTF_ATTRIBUTE(2,3)
421 {
422         va_list ap;
423         char *ret;
424
425         va_start(ap, fmt);
426         ret = talloc_vasprintf(t, fmt, ap);
427         va_end(ap);
428         return ret;
429 }
430
431
432 /**
433  * Realloc @p s to append the formatted result of @p fmt and @p ap,
434  * and return @p s, which may have moved.  Good for gradually
435  * accumulating output into a string buffer.
436  **/
437 char *talloc_vasprintf_append(char *s,
438                               const char *fmt, va_list ap)
439 {       
440         int len, s_len;
441         va_list ap2;
442
443         VA_COPY(ap2, ap);
444
445         if (s) {
446                 s_len = strlen(s);
447         } else {
448                 s_len = 0;
449         }
450         len = vsnprintf(NULL, 0, fmt, ap2);
451
452         s = talloc_realloc(s, s_len + len+1);
453         if (!s) return NULL;
454
455         VA_COPY(ap2, ap);
456
457         vsnprintf(s+s_len, len+1, fmt, ap2);
458
459         return s;
460 }
461
462 /*
463   Realloc @p s to append the formatted result of @p fmt and return @p
464   s, which may have moved.  Good for gradually accumulating output
465   into a string buffer.
466  */
467 char *talloc_asprintf_append(char *s,
468                              const char *fmt, ...) _PRINTF_ATTRIBUTE(2,3)
469 {
470         va_list ap;
471
472         va_start(ap, fmt);
473         s = talloc_vasprintf_append(s, fmt, ap);
474         va_end(ap);
475         return s;
476 }
477
478 /*
479   alloc an array, checking for integer overflow in the array size
480 */
481 void *talloc_array(void *ctx, size_t el_size, uint_t count)
482 {
483         if (count == 0 ||
484             count >= MAX_TALLOC_SIZE/el_size) {
485                 return NULL;
486         }
487         return talloc(ctx, el_size * count);
488 }
489
490
491 /*
492   realloc an array, checking for integer overflow in the array size
493 */
494 void *talloc_realloc_array(void *ptr, size_t el_size, uint_t count)
495 {
496         if (count == 0 ||
497             count >= MAX_TALLOC_SIZE/el_size) {
498                 return NULL;
499         }
500         return talloc_realloc(ptr, el_size * count);
501 }
502
503 /*
504   a alloc function for ldb that uses talloc
505 */
506 void *talloc_ldb_alloc(void *context, void *ptr, size_t size)
507 {
508         if (ptr == NULL) {
509                 return talloc(context, size);
510         }
511         if (size == 0) {
512                 talloc_free(ptr);
513                 return NULL;
514         }
515         return talloc_realloc(ptr, size);
516 }