OLD | NEW |
| (Empty) |
1 /* | |
2 Samba Unix SMB/CIFS implementation. | |
3 | |
4 Samba trivial allocation library - new interface | |
5 | |
6 NOTE: Please read talloc_guide.txt for full documentation | |
7 | |
8 Copyright (C) Andrew Tridgell 2004 | |
9 Copyright (C) Stefan Metzmacher 2006 | |
10 | |
11 ** NOTE! The following LGPL license applies to the talloc | |
12 ** library. This does NOT imply that all of Samba is released | |
13 ** under the LGPL | |
14 | |
15 This library is free software; you can redistribute it and/or | |
16 modify it under the terms of the GNU Lesser General Public | |
17 License as published by the Free Software Foundation; either | |
18 version 3 of the License, or (at your option) any later version. | |
19 | |
20 This library is distributed in the hope that it will be useful, | |
21 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
23 Lesser General Public License for more details. | |
24 | |
25 You should have received a copy of the GNU Lesser General Public | |
26 License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
27 */ | |
28 | |
29 /* | |
30 inspired by http://swapped.cc/halloc/ | |
31 */ | |
32 | |
33 /* Commented out for building within Chromium */ | |
34 /* #include "replace.h" */ | |
35 #include "talloc.h" | |
36 | |
37 #ifdef TALLOC_BUILD_VERSION_MAJOR | |
38 #if (TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR) | |
39 #error "TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR" | |
40 #endif | |
41 #endif | |
42 | |
43 #ifdef TALLOC_BUILD_VERSION_MINOR | |
44 #if (TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR) | |
45 #error "TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR" | |
46 #endif | |
47 #endif | |
48 | |
49 /* use this to force every realloc to change the pointer, to stress test | |
50 code that might not cope */ | |
51 #define ALWAYS_REALLOC 0 | |
52 | |
53 | |
54 #define MAX_TALLOC_SIZE 0x10000000 | |
55 #define TALLOC_MAGIC_BASE 0xe814ec70 | |
56 #define TALLOC_MAGIC ( \ | |
57 TALLOC_MAGIC_BASE + \ | |
58 (TALLOC_VERSION_MAJOR << 12) + \ | |
59 (TALLOC_VERSION_MINOR << 4) \ | |
60 ) | |
61 | |
62 #define TALLOC_FLAG_FREE 0x01 | |
63 #define TALLOC_FLAG_LOOP 0x02 | |
64 #define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ | |
65 #define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */ | |
66 #define TALLOC_MAGIC_REFERENCE ((const char *)1) | |
67 | |
68 /* by default we abort when given a bad pointer (such as when talloc_free() is c
alled | |
69 on a pointer that came from malloc() */ | |
70 #ifndef TALLOC_ABORT | |
71 #define TALLOC_ABORT(reason) abort() | |
72 #endif | |
73 | |
74 #ifndef discard_const_p | |
75 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) | |
76 # define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) | |
77 #else | |
78 # define discard_const_p(type, ptr) ((type *)(ptr)) | |
79 #endif | |
80 #endif | |
81 | |
82 /* these macros gain us a few percent of speed on gcc */ | |
83 #if (__GNUC__ >= 3) | |
84 /* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 | |
85 as its first argument */ | |
86 #ifndef likely | |
87 #define likely(x) __builtin_expect(!!(x), 1) | |
88 #endif | |
89 #ifndef unlikely | |
90 #define unlikely(x) __builtin_expect(!!(x), 0) | |
91 #endif | |
92 #else | |
93 #ifndef likely | |
94 #define likely(x) (x) | |
95 #endif | |
96 #ifndef unlikely | |
97 #define unlikely(x) (x) | |
98 #endif | |
99 #endif | |
100 | |
101 /* inline isn't supported in C files in Visual Studio 2008 on Windows */ | |
102 #ifdef _MSC_VER | |
103 #define INLINE | |
104 #else | |
105 #define INLINE inline | |
106 #endif | |
107 | |
108 /* this null_context is only used if talloc_enable_leak_report() or | |
109 talloc_enable_leak_report_full() is called, otherwise it remains | |
110 NULL | |
111 */ | |
112 static void *null_context; | |
113 static void *autofree_context; | |
114 | |
115 struct talloc_reference_handle { | |
116 struct talloc_reference_handle *next, *prev; | |
117 void *ptr; | |
118 const char *location; | |
119 }; | |
120 | |
121 typedef int (*talloc_destructor_t)(void *); | |
122 | |
123 struct talloc_chunk { | |
124 struct talloc_chunk *next, *prev; | |
125 struct talloc_chunk *parent, *child; | |
126 struct talloc_reference_handle *refs; | |
127 talloc_destructor_t destructor; | |
128 const char *name; | |
129 size_t size; | |
130 unsigned flags; | |
131 | |
132 /* | |
133 * "pool" has dual use: | |
134 * | |
135 * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool" | |
136 * marks the end of the currently allocated area. | |
137 * | |
138 * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool" | |
139 * is a pointer to the struct talloc_chunk of the pool that it was | |
140 * allocated from. This way children can quickly find the pool to chew | |
141 * from. | |
142 */ | |
143 void *pool; | |
144 }; | |
145 | |
146 /* 16 byte alignment seems to keep everyone happy */ | |
147 #define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) | |
148 #define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) | |
149 | |
150 int talloc_version_major(void) | |
151 { | |
152 return TALLOC_VERSION_MAJOR; | |
153 } | |
154 | |
155 int talloc_version_minor(void) | |
156 { | |
157 return TALLOC_VERSION_MINOR; | |
158 } | |
159 | |
160 static void (*talloc_log_fn)(const char *message); | |
161 | |
162 void talloc_set_log_fn(void (*log_fn)(const char *message)) | |
163 { | |
164 talloc_log_fn = log_fn; | |
165 } | |
166 | |
167 static void talloc_log(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); | |
168 static void talloc_log(const char *fmt, ...) | |
169 { | |
170 va_list ap; | |
171 char *message; | |
172 | |
173 if (!talloc_log_fn) { | |
174 return; | |
175 } | |
176 | |
177 va_start(ap, fmt); | |
178 message = talloc_vasprintf(NULL, fmt, ap); | |
179 va_end(ap); | |
180 | |
181 talloc_log_fn(message); | |
182 talloc_free(message); | |
183 } | |
184 | |
185 static void talloc_log_stderr(const char *message) | |
186 { | |
187 fprintf(stderr, "%s", message); | |
188 } | |
189 | |
190 void talloc_set_log_stderr(void) | |
191 { | |
192 talloc_set_log_fn(talloc_log_stderr); | |
193 } | |
194 | |
195 static void (*talloc_abort_fn)(const char *reason); | |
196 | |
197 void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) | |
198 { | |
199 talloc_abort_fn = abort_fn; | |
200 } | |
201 | |
202 static void talloc_abort(const char *reason) | |
203 { | |
204 talloc_log("%s\n", reason); | |
205 | |
206 if (!talloc_abort_fn) { | |
207 TALLOC_ABORT(reason); | |
208 } | |
209 | |
210 talloc_abort_fn(reason); | |
211 } | |
212 | |
213 static void talloc_abort_magic(unsigned magic) | |
214 { | |
215 unsigned striped = magic - TALLOC_MAGIC_BASE; | |
216 unsigned major = (striped & 0xFFFFF000) >> 12; | |
217 unsigned minor = (striped & 0x00000FF0) >> 4; | |
218 talloc_log("Bad talloc magic[0x%08X/%u/%u] expected[0x%08X/%u/%u]\n", | |
219 magic, major, minor, | |
220 TALLOC_MAGIC, TALLOC_VERSION_MAJOR, TALLOC_VERSION_MINOR); | |
221 talloc_abort("Bad talloc magic value - wrong talloc version used/mixed")
; | |
222 } | |
223 | |
224 static void talloc_abort_double_free(void) | |
225 { | |
226 talloc_abort("Bad talloc magic value - double free"); | |
227 } | |
228 | |
229 static void talloc_abort_unknown_value(void) | |
230 { | |
231 talloc_abort("Bad talloc magic value - unknown value"); | |
232 } | |
233 | |
234 /* panic if we get a bad magic value */ | |
235 static INLINE struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) | |
236 { | |
237 const char *pp = (const char *)ptr; | |
238 struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_H
DR_SIZE); | |
239 if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) {
| |
240 if ((tc->flags & (~0xFFF)) == TALLOC_MAGIC_BASE) { | |
241 talloc_abort_magic(tc->flags & (~0xF)); | |
242 return NULL; | |
243 } | |
244 | |
245 if (tc->flags & TALLOC_FLAG_FREE) { | |
246 talloc_log("talloc: double free error - first free may b
e at %s\n", tc->name); | |
247 talloc_abort_double_free(); | |
248 return NULL; | |
249 } else { | |
250 talloc_abort_unknown_value(); | |
251 return NULL; | |
252 } | |
253 } | |
254 return tc; | |
255 } | |
256 | |
257 /* hook into the front of the list */ | |
258 #define _TLIST_ADD(list, p) \ | |
259 do { \ | |
260 if (!(list)) { \ | |
261 (list) = (p); \ | |
262 (p)->next = (p)->prev = NULL; \ | |
263 } else { \ | |
264 (list)->prev = (p); \ | |
265 (p)->next = (list); \ | |
266 (p)->prev = NULL; \ | |
267 (list) = (p); \ | |
268 }\ | |
269 } while (0) | |
270 | |
271 /* remove an element from a list - element doesn't have to be in list. */ | |
272 #define _TLIST_REMOVE(list, p) \ | |
273 do { \ | |
274 if ((p) == (list)) { \ | |
275 (list) = (p)->next; \ | |
276 if (list) (list)->prev = NULL; \ | |
277 } else { \ | |
278 if ((p)->prev) (p)->prev->next = (p)->next; \ | |
279 if ((p)->next) (p)->next->prev = (p)->prev; \ | |
280 } \ | |
281 if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ | |
282 } while (0) | |
283 | |
284 | |
285 /* | |
286 return the parent chunk of a pointer | |
287 */ | |
288 static INLINE struct talloc_chunk *talloc_parent_chunk(const void *ptr) | |
289 { | |
290 struct talloc_chunk *tc; | |
291 | |
292 if (unlikely(ptr == NULL)) { | |
293 return NULL; | |
294 } | |
295 | |
296 tc = talloc_chunk_from_ptr(ptr); | |
297 while (tc->prev) tc=tc->prev; | |
298 | |
299 return tc->parent; | |
300 } | |
301 | |
302 void *talloc_parent(const void *ptr) | |
303 { | |
304 struct talloc_chunk *tc = talloc_parent_chunk(ptr); | |
305 return tc? TC_PTR_FROM_CHUNK(tc) : NULL; | |
306 } | |
307 | |
308 /* | |
309 find parents name | |
310 */ | |
311 const char *talloc_parent_name(const void *ptr) | |
312 { | |
313 struct talloc_chunk *tc = talloc_parent_chunk(ptr); | |
314 return tc? tc->name : NULL; | |
315 } | |
316 | |
317 /* | |
318 A pool carries an in-pool object count count in the first 16 bytes. | |
319 bytes. This is done to support talloc_steal() to a parent outside of the | |
320 pool. The count includes the pool itself, so a talloc_free() on a pool will | |
321 only destroy the pool if the count has dropped to zero. A talloc_free() of a | |
322 pool member will reduce the count, and eventually also call free(3) on the | |
323 pool memory. | |
324 | |
325 The object count is not put into "struct talloc_chunk" because it is only | |
326 relevant for talloc pools and the alignment to 16 bytes would increase the | |
327 memory footprint of each talloc chunk by those 16 bytes. | |
328 */ | |
329 | |
330 #define TALLOC_POOL_HDR_SIZE 16 | |
331 | |
332 static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc) | |
333 { | |
334 return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk)); | |
335 } | |
336 | |
337 /* | |
338 Allocate from a pool | |
339 */ | |
340 | |
341 static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent, | |
342 size_t size) | |
343 { | |
344 struct talloc_chunk *pool_ctx = NULL; | |
345 size_t space_left; | |
346 struct talloc_chunk *result; | |
347 size_t chunk_size; | |
348 | |
349 if (parent == NULL) { | |
350 return NULL; | |
351 } | |
352 | |
353 if (parent->flags & TALLOC_FLAG_POOL) { | |
354 pool_ctx = parent; | |
355 } | |
356 else if (parent->flags & TALLOC_FLAG_POOLMEM) { | |
357 pool_ctx = (struct talloc_chunk *)parent->pool; | |
358 } | |
359 | |
360 if (pool_ctx == NULL) { | |
361 return NULL; | |
362 } | |
363 | |
364 space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size) | |
365 - ((char *)pool_ctx->pool); | |
366 | |
367 /* | |
368 * Align size to 16 bytes | |
369 */ | |
370 chunk_size = ((size + 15) & ~15); | |
371 | |
372 if (space_left < chunk_size) { | |
373 return NULL; | |
374 } | |
375 | |
376 result = (struct talloc_chunk *)pool_ctx->pool; | |
377 | |
378 #if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) | |
379 VALGRIND_MAKE_MEM_UNDEFINED(result, size); | |
380 #endif | |
381 | |
382 pool_ctx->pool = (void *)((char *)result + chunk_size); | |
383 | |
384 result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM; | |
385 result->pool = pool_ctx; | |
386 | |
387 *talloc_pool_objectcount(pool_ctx) += 1; | |
388 | |
389 return result; | |
390 } | |
391 | |
392 /* | |
393 Allocate a bit of memory as a child of an existing pointer | |
394 */ | |
395 static INLINE void *__talloc(const void *context, size_t size) | |
396 { | |
397 struct talloc_chunk *tc = NULL; | |
398 | |
399 if (unlikely(context == NULL)) { | |
400 context = null_context; | |
401 } | |
402 | |
403 if (unlikely(size >= MAX_TALLOC_SIZE)) { | |
404 return NULL; | |
405 } | |
406 | |
407 if (context != NULL) { | |
408 tc = talloc_alloc_pool(talloc_chunk_from_ptr(context), | |
409 TC_HDR_SIZE+size); | |
410 } | |
411 | |
412 if (tc == NULL) { | |
413 tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); | |
414 if (unlikely(tc == NULL)) return NULL; | |
415 tc->flags = TALLOC_MAGIC; | |
416 tc->pool = NULL; | |
417 } | |
418 | |
419 tc->size = size; | |
420 tc->destructor = NULL; | |
421 tc->child = NULL; | |
422 tc->name = NULL; | |
423 tc->refs = NULL; | |
424 | |
425 if (likely(context)) { | |
426 struct talloc_chunk *parent = talloc_chunk_from_ptr(context); | |
427 | |
428 if (parent->child) { | |
429 parent->child->parent = NULL; | |
430 tc->next = parent->child; | |
431 tc->next->prev = tc; | |
432 } else { | |
433 tc->next = NULL; | |
434 } | |
435 tc->parent = parent; | |
436 tc->prev = NULL; | |
437 parent->child = tc; | |
438 } else { | |
439 tc->next = tc->prev = tc->parent = NULL; | |
440 } | |
441 | |
442 return TC_PTR_FROM_CHUNK(tc); | |
443 } | |
444 | |
445 /* | |
446 * Create a talloc pool | |
447 */ | |
448 | |
449 void *talloc_pool(const void *context, size_t size) | |
450 { | |
451 void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE); | |
452 struct talloc_chunk *tc; | |
453 | |
454 if (unlikely(result == NULL)) { | |
455 return NULL; | |
456 } | |
457 | |
458 tc = talloc_chunk_from_ptr(result); | |
459 | |
460 tc->flags |= TALLOC_FLAG_POOL; | |
461 tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE; | |
462 | |
463 *talloc_pool_objectcount(tc) = 1; | |
464 | |
465 #if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) | |
466 VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size); | |
467 #endif | |
468 | |
469 return result; | |
470 } | |
471 | |
472 /* | |
473 setup a destructor to be called on free of a pointer | |
474 the destructor should return 0 on success, or -1 on failure. | |
475 if the destructor fails then the free is failed, and the memory can | |
476 be continued to be used | |
477 */ | |
478 void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) | |
479 { | |
480 struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); | |
481 tc->destructor = destructor; | |
482 } | |
483 | |
484 /* | |
485 increase the reference count on a piece of memory. | |
486 */ | |
487 int talloc_increase_ref_count(const void *ptr) | |
488 { | |
489 if (unlikely(!talloc_reference(null_context, ptr))) { | |
490 return -1; | |
491 } | |
492 return 0; | |
493 } | |
494 | |
495 /* | |
496 helper for talloc_reference() | |
497 | |
498 this is referenced by a function pointer and should not be inline | |
499 */ | |
500 static int talloc_reference_destructor(struct talloc_reference_handle *handle) | |
501 { | |
502 struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); | |
503 _TLIST_REMOVE(ptr_tc->refs, handle); | |
504 return 0; | |
505 } | |
506 | |
507 /* | |
508 more efficient way to add a name to a pointer - the name must point to a | |
509 true string constant | |
510 */ | |
511 static INLINE void _talloc_set_name_const(const void *ptr, const char *name) | |
512 { | |
513 struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); | |
514 tc->name = name; | |
515 } | |
516 | |
517 /* | |
518 internal talloc_named_const() | |
519 */ | |
520 static INLINE void *_talloc_named_const(const void *context, size_t size, const
char *name) | |
521 { | |
522 void *ptr; | |
523 | |
524 ptr = __talloc(context, size); | |
525 if (unlikely(ptr == NULL)) { | |
526 return NULL; | |
527 } | |
528 | |
529 _talloc_set_name_const(ptr, name); | |
530 | |
531 return ptr; | |
532 } | |
533 | |
534 /* | |
535 make a secondary reference to a pointer, hanging off the given context. | |
536 the pointer remains valid until both the original caller and this given | |
537 context are freed. | |
538 | |
539 the major use for this is when two different structures need to reference the | |
540 same underlying data, and you want to be able to free the two instances separa
tely, | |
541 and in either order | |
542 */ | |
543 void *_talloc_reference_loc(const void *context, const void *ptr, const char *lo
cation) | |
544 { | |
545 struct talloc_chunk *tc; | |
546 struct talloc_reference_handle *handle; | |
547 if (unlikely(ptr == NULL)) return NULL; | |
548 | |
549 tc = talloc_chunk_from_ptr(ptr); | |
550 handle = (struct talloc_reference_handle *)_talloc_named_const(context, | |
551 sizeof(struct talloc_referenc
e_handle), | |
552 TALLOC_MAGIC_REFERENCE); | |
553 if (unlikely(handle == NULL)) return NULL; | |
554 | |
555 /* note that we hang the destructor off the handle, not the | |
556 main context as that allows the caller to still setup their | |
557 own destructor on the context if they want to */ | |
558 talloc_set_destructor(handle, talloc_reference_destructor); | |
559 handle->ptr = discard_const_p(void, ptr); | |
560 handle->location = location; | |
561 _TLIST_ADD(tc->refs, handle); | |
562 return handle->ptr; | |
563 } | |
564 | |
565 static void *_talloc_steal_internal(const void *new_ctx, const void *ptr); | |
566 | |
567 /* | |
568 internal talloc_free call | |
569 */ | |
570 static INLINE int _talloc_free_internal(void *ptr, const char *location) | |
571 { | |
572 struct talloc_chunk *tc; | |
573 | |
574 if (unlikely(ptr == NULL)) { | |
575 return -1; | |
576 } | |
577 | |
578 tc = talloc_chunk_from_ptr(ptr); | |
579 | |
580 if (unlikely(tc->refs)) { | |
581 int is_child; | |
582 /* check this is a reference from a child or grantchild | |
583 * back to it's parent or grantparent | |
584 * | |
585 * in that case we need to remove the reference and | |
586 * call another instance of talloc_free() on the current | |
587 * pointer. | |
588 */ | |
589 is_child = talloc_is_parent(tc->refs, ptr); | |
590 _talloc_free_internal(tc->refs, location); | |
591 if (is_child) { | |
592 return _talloc_free_internal(ptr, location); | |
593 } | |
594 return -1; | |
595 } | |
596 | |
597 if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { | |
598 /* we have a free loop - stop looping */ | |
599 return 0; | |
600 } | |
601 | |
602 if (unlikely(tc->destructor)) { | |
603 talloc_destructor_t d = tc->destructor; | |
604 if (d == (talloc_destructor_t)-1) { | |
605 return -1; | |
606 } | |
607 tc->destructor = (talloc_destructor_t)-1; | |
608 if (d(ptr) == -1) { | |
609 tc->destructor = d; | |
610 return -1; | |
611 } | |
612 tc->destructor = NULL; | |
613 } | |
614 | |
615 if (tc->parent) { | |
616 _TLIST_REMOVE(tc->parent->child, tc); | |
617 if (tc->parent->child) { | |
618 tc->parent->child->parent = tc->parent; | |
619 } | |
620 } else { | |
621 if (tc->prev) tc->prev->next = tc->next; | |
622 if (tc->next) tc->next->prev = tc->prev; | |
623 } | |
624 | |
625 tc->flags |= TALLOC_FLAG_LOOP; | |
626 | |
627 while (tc->child) { | |
628 /* we need to work out who will own an abandoned child | |
629 if it cannot be freed. In priority order, the first | |
630 choice is owner of any remaining reference to this | |
631 pointer, the second choice is our parent, and the | |
632 final choice is the null context. */ | |
633 void *child = TC_PTR_FROM_CHUNK(tc->child); | |
634 const void *new_parent = null_context; | |
635 if (unlikely(tc->child->refs)) { | |
636 struct talloc_chunk *p = talloc_parent_chunk(tc->child->
refs); | |
637 if (p) new_parent = TC_PTR_FROM_CHUNK(p); | |
638 } | |
639 if (unlikely(_talloc_free_internal(child, location) == -1)) { | |
640 if (new_parent == null_context) { | |
641 struct talloc_chunk *p = talloc_parent_chunk(ptr
); | |
642 if (p) new_parent = TC_PTR_FROM_CHUNK(p); | |
643 } | |
644 _talloc_steal_internal(new_parent, child); | |
645 } | |
646 } | |
647 | |
648 tc->flags |= TALLOC_FLAG_FREE; | |
649 | |
650 /* we mark the freed memory with where we called the free | |
651 * from. This means on a double free error we can report where | |
652 * the first free came from | |
653 */ | |
654 tc->name = location; | |
655 | |
656 if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) { | |
657 struct talloc_chunk *pool; | |
658 unsigned int *pool_object_count; | |
659 | |
660 pool = (tc->flags & TALLOC_FLAG_POOL) | |
661 ? tc : (struct talloc_chunk *)tc->pool; | |
662 | |
663 pool_object_count = talloc_pool_objectcount(pool); | |
664 | |
665 if (*pool_object_count == 0) { | |
666 talloc_abort("Pool object count zero!"); | |
667 return 0; | |
668 } | |
669 | |
670 *pool_object_count -= 1; | |
671 | |
672 if (*pool_object_count == 0) { | |
673 free(pool); | |
674 } | |
675 } | |
676 else { | |
677 free(tc); | |
678 } | |
679 return 0; | |
680 } | |
681 | |
682 /* | |
683 move a lump of memory from one talloc context to another return the | |
684 ptr on success, or NULL if it could not be transferred. | |
685 passing NULL as ptr will always return NULL with no side effects. | |
686 */ | |
687 static void *_talloc_steal_internal(const void *new_ctx, const void *ptr) | |
688 { | |
689 struct talloc_chunk *tc, *new_tc; | |
690 | |
691 if (unlikely(!ptr)) { | |
692 return NULL; | |
693 } | |
694 | |
695 if (unlikely(new_ctx == NULL)) { | |
696 new_ctx = null_context; | |
697 } | |
698 | |
699 tc = talloc_chunk_from_ptr(ptr); | |
700 | |
701 if (unlikely(new_ctx == NULL)) { | |
702 if (tc->parent) { | |
703 _TLIST_REMOVE(tc->parent->child, tc); | |
704 if (tc->parent->child) { | |
705 tc->parent->child->parent = tc->parent; | |
706 } | |
707 } else { | |
708 if (tc->prev) tc->prev->next = tc->next; | |
709 if (tc->next) tc->next->prev = tc->prev; | |
710 } | |
711 | |
712 tc->parent = tc->next = tc->prev = NULL; | |
713 return discard_const_p(void, ptr); | |
714 } | |
715 | |
716 new_tc = talloc_chunk_from_ptr(new_ctx); | |
717 | |
718 if (unlikely(tc == new_tc || tc->parent == new_tc)) { | |
719 return discard_const_p(void, ptr); | |
720 } | |
721 | |
722 if (tc->parent) { | |
723 _TLIST_REMOVE(tc->parent->child, tc); | |
724 if (tc->parent->child) { | |
725 tc->parent->child->parent = tc->parent; | |
726 } | |
727 } else { | |
728 if (tc->prev) tc->prev->next = tc->next; | |
729 if (tc->next) tc->next->prev = tc->prev; | |
730 } | |
731 | |
732 tc->parent = new_tc; | |
733 if (new_tc->child) new_tc->child->parent = NULL; | |
734 _TLIST_ADD(new_tc->child, tc); | |
735 | |
736 return discard_const_p(void, ptr); | |
737 } | |
738 | |
739 /* | |
740 move a lump of memory from one talloc context to another return the | |
741 ptr on success, or NULL if it could not be transferred. | |
742 passing NULL as ptr will always return NULL with no side effects. | |
743 */ | |
744 void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *locati
on) | |
745 { | |
746 struct talloc_chunk *tc; | |
747 | |
748 if (unlikely(ptr == NULL)) { | |
749 return NULL; | |
750 } | |
751 | |
752 tc = talloc_chunk_from_ptr(ptr); | |
753 | |
754 if (unlikely(tc->refs != NULL) && talloc_parent(ptr) != new_ctx) { | |
755 struct talloc_reference_handle *h; | |
756 | |
757 talloc_log("WARNING: talloc_steal with references at %s\n", | |
758 location); | |
759 | |
760 for (h=tc->refs; h; h=h->next) { | |
761 talloc_log("\treference at %s\n", | |
762 h->location); | |
763 } | |
764 } | |
765 | |
766 return _talloc_steal_internal(new_ctx, ptr); | |
767 } | |
768 | |
769 /* | |
770 this is like a talloc_steal(), but you must supply the old | |
771 parent. This resolves the ambiguity in a talloc_steal() which is | |
772 called on a context that has more than one parent (via references) | |
773 | |
774 The old parent can be either a reference or a parent | |
775 */ | |
776 void *talloc_reparent(const void *old_parent, const void *new_parent, const void
*ptr) | |
777 { | |
778 struct talloc_chunk *tc; | |
779 struct talloc_reference_handle *h; | |
780 | |
781 if (unlikely(ptr == NULL)) { | |
782 return NULL; | |
783 } | |
784 | |
785 if (old_parent == talloc_parent(ptr)) { | |
786 return _talloc_steal_internal(new_parent, ptr); | |
787 } | |
788 | |
789 tc = talloc_chunk_from_ptr(ptr); | |
790 for (h=tc->refs;h;h=h->next) { | |
791 if (talloc_parent(h) == old_parent) { | |
792 if (_talloc_steal_internal(new_parent, h) != h) { | |
793 return NULL; | |
794 } | |
795 return discard_const_p(void, ptr); | |
796 } | |
797 } | |
798 | |
799 /* it wasn't a parent */ | |
800 return NULL; | |
801 } | |
802 | |
803 /* | |
804 remove a secondary reference to a pointer. This undo's what | |
805 talloc_reference() has done. The context and pointer arguments | |
806 must match those given to a talloc_reference() | |
807 */ | |
808 static INLINE int talloc_unreference(const void *context, const void *ptr) | |
809 { | |
810 struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); | |
811 struct talloc_reference_handle *h; | |
812 | |
813 if (unlikely(context == NULL)) { | |
814 context = null_context; | |
815 } | |
816 | |
817 for (h=tc->refs;h;h=h->next) { | |
818 struct talloc_chunk *p = talloc_parent_chunk(h); | |
819 if (p == NULL) { | |
820 if (context == NULL) break; | |
821 } else if (TC_PTR_FROM_CHUNK(p) == context) { | |
822 break; | |
823 } | |
824 } | |
825 if (h == NULL) { | |
826 return -1; | |
827 } | |
828 | |
829 return _talloc_free_internal(h, __location__); | |
830 } | |
831 | |
832 /* | |
833 remove a specific parent context from a pointer. This is a more | |
834 controlled varient of talloc_free() | |
835 */ | |
836 int talloc_unlink(const void *context, void *ptr) | |
837 { | |
838 struct talloc_chunk *tc_p, *new_p; | |
839 void *new_parent; | |
840 | |
841 if (ptr == NULL) { | |
842 return -1; | |
843 } | |
844 | |
845 if (context == NULL) { | |
846 context = null_context; | |
847 } | |
848 | |
849 if (talloc_unreference(context, ptr) == 0) { | |
850 return 0; | |
851 } | |
852 | |
853 if (context == NULL) { | |
854 if (talloc_parent_chunk(ptr) != NULL) { | |
855 return -1; | |
856 } | |
857 } else { | |
858 if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr))
{ | |
859 return -1; | |
860 } | |
861 } | |
862 | |
863 tc_p = talloc_chunk_from_ptr(ptr); | |
864 | |
865 if (tc_p->refs == NULL) { | |
866 return _talloc_free_internal(ptr, __location__); | |
867 } | |
868 | |
869 new_p = talloc_parent_chunk(tc_p->refs); | |
870 if (new_p) { | |
871 new_parent = TC_PTR_FROM_CHUNK(new_p); | |
872 } else { | |
873 new_parent = NULL; | |
874 } | |
875 | |
876 if (talloc_unreference(new_parent, ptr) != 0) { | |
877 return -1; | |
878 } | |
879 | |
880 _talloc_steal_internal(new_parent, ptr); | |
881 | |
882 return 0; | |
883 } | |
884 | |
885 /* | |
886 add a name to an existing pointer - va_list version | |
887 */ | |
888 static INLINE const char *talloc_set_name_v(const void *ptr, const char *fmt, va
_list ap) PRINTF_ATTRIBUTE(2,0); | |
889 | |
890 static INLINE const char *talloc_set_name_v(const void *ptr, const char *fmt, va
_list ap) | |
891 { | |
892 struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); | |
893 tc->name = talloc_vasprintf(ptr, fmt, ap); | |
894 if (likely(tc->name)) { | |
895 _talloc_set_name_const(tc->name, ".name"); | |
896 } | |
897 return tc->name; | |
898 } | |
899 | |
900 /* | |
901 add a name to an existing pointer | |
902 */ | |
903 const char *talloc_set_name(const void *ptr, const char *fmt, ...) | |
904 { | |
905 const char *name; | |
906 va_list ap; | |
907 va_start(ap, fmt); | |
908 name = talloc_set_name_v(ptr, fmt, ap); | |
909 va_end(ap); | |
910 return name; | |
911 } | |
912 | |
913 | |
914 /* | |
915 create a named talloc pointer. Any talloc pointer can be named, and | |
916 talloc_named() operates just like talloc() except that it allows you | |
917 to name the pointer. | |
918 */ | |
919 void *talloc_named(const void *context, size_t size, const char *fmt, ...) | |
920 { | |
921 va_list ap; | |
922 void *ptr; | |
923 const char *name; | |
924 | |
925 ptr = __talloc(context, size); | |
926 if (unlikely(ptr == NULL)) return NULL; | |
927 | |
928 va_start(ap, fmt); | |
929 name = talloc_set_name_v(ptr, fmt, ap); | |
930 va_end(ap); | |
931 | |
932 if (unlikely(name == NULL)) { | |
933 _talloc_free_internal(ptr, __location__); | |
934 return NULL; | |
935 } | |
936 | |
937 return ptr; | |
938 } | |
939 | |
940 /* | |
941 return the name of a talloc ptr, or "UNNAMED" | |
942 */ | |
943 const char *talloc_get_name(const void *ptr) | |
944 { | |
945 struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); | |
946 if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { | |
947 return ".reference"; | |
948 } | |
949 if (likely(tc->name)) { | |
950 return tc->name; | |
951 } | |
952 return "UNNAMED"; | |
953 } | |
954 | |
955 | |
956 /* | |
957 check if a pointer has the given name. If it does, return the pointer, | |
958 otherwise return NULL | |
959 */ | |
960 void *talloc_check_name(const void *ptr, const char *name) | |
961 { | |
962 const char *pname; | |
963 if (unlikely(ptr == NULL)) return NULL; | |
964 pname = talloc_get_name(ptr); | |
965 if (likely(pname == name || strcmp(pname, name) == 0)) { | |
966 return discard_const_p(void, ptr); | |
967 } | |
968 return NULL; | |
969 } | |
970 | |
971 static void talloc_abort_type_missmatch(const char *location, | |
972 const char *name, | |
973 const char *expected) | |
974 { | |
975 const char *reason; | |
976 | |
977 reason = talloc_asprintf(NULL, | |
978 "%s: Type mismatch: name[%s] expected[%s]", | |
979 location, | |
980 name?name:"NULL", | |
981 expected); | |
982 if (!reason) { | |
983 reason = "Type mismatch"; | |
984 } | |
985 | |
986 talloc_abort(reason); | |
987 } | |
988 | |
989 void *_talloc_get_type_abort(const void *ptr, const char *name, const char *loca
tion) | |
990 { | |
991 const char *pname; | |
992 | |
993 if (unlikely(ptr == NULL)) { | |
994 talloc_abort_type_missmatch(location, NULL, name); | |
995 return NULL; | |
996 } | |
997 | |
998 pname = talloc_get_name(ptr); | |
999 if (likely(pname == name || strcmp(pname, name) == 0)) { | |
1000 return discard_const_p(void, ptr); | |
1001 } | |
1002 | |
1003 talloc_abort_type_missmatch(location, pname, name); | |
1004 return NULL; | |
1005 } | |
1006 | |
1007 /* | |
1008 this is for compatibility with older versions of talloc | |
1009 */ | |
1010 void *talloc_init(const char *fmt, ...) | |
1011 { | |
1012 va_list ap; | |
1013 void *ptr; | |
1014 const char *name; | |
1015 | |
1016 /* | |
1017 * samba3 expects talloc_report_depth_cb(NULL, ...) | |
1018 * reports all talloc'ed memory, so we need to enable | |
1019 * null_tracking | |
1020 */ | |
1021 talloc_enable_null_tracking(); | |
1022 | |
1023 ptr = __talloc(NULL, 0); | |
1024 if (unlikely(ptr == NULL)) return NULL; | |
1025 | |
1026 va_start(ap, fmt); | |
1027 name = talloc_set_name_v(ptr, fmt, ap); | |
1028 va_end(ap); | |
1029 | |
1030 if (unlikely(name == NULL)) { | |
1031 _talloc_free_internal(ptr, __location__); | |
1032 return NULL; | |
1033 } | |
1034 | |
1035 return ptr; | |
1036 } | |
1037 | |
1038 /* | |
1039 this is a replacement for the Samba3 talloc_destroy_pool functionality. It | |
1040 should probably not be used in new code. It's in here to keep the talloc | |
1041 code consistent across Samba 3 and 4. | |
1042 */ | |
1043 void talloc_free_children(void *ptr) | |
1044 { | |
1045 struct talloc_chunk *tc; | |
1046 | |
1047 if (unlikely(ptr == NULL)) { | |
1048 return; | |
1049 } | |
1050 | |
1051 tc = talloc_chunk_from_ptr(ptr); | |
1052 | |
1053 while (tc->child) { | |
1054 /* we need to work out who will own an abandoned child | |
1055 if it cannot be freed. In priority order, the first | |
1056 choice is owner of any remaining reference to this | |
1057 pointer, the second choice is our parent, and the | |
1058 final choice is the null context. */ | |
1059 void *child = TC_PTR_FROM_CHUNK(tc->child); | |
1060 const void *new_parent = null_context; | |
1061 if (unlikely(tc->child->refs)) { | |
1062 struct talloc_chunk *p = talloc_parent_chunk(tc->child->
refs); | |
1063 if (p) new_parent = TC_PTR_FROM_CHUNK(p); | |
1064 } | |
1065 if (unlikely(talloc_free(child) == -1)) { | |
1066 if (new_parent == null_context) { | |
1067 struct talloc_chunk *p = talloc_parent_chunk(ptr
); | |
1068 if (p) new_parent = TC_PTR_FROM_CHUNK(p); | |
1069 } | |
1070 _talloc_steal_internal(new_parent, child); | |
1071 } | |
1072 } | |
1073 | |
1074 if ((tc->flags & TALLOC_FLAG_POOL) | |
1075 && (*talloc_pool_objectcount(tc) == 1)) { | |
1076 tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE); | |
1077 #if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) | |
1078 VALGRIND_MAKE_MEM_NOACCESS( | |
1079 tc->pool, tc->size - TALLOC_POOL_HDR_SIZE); | |
1080 #endif | |
1081 } | |
1082 } | |
1083 | |
1084 /* | |
1085 Allocate a bit of memory as a child of an existing pointer | |
1086 */ | |
1087 void *_talloc(const void *context, size_t size) | |
1088 { | |
1089 return __talloc(context, size); | |
1090 } | |
1091 | |
1092 /* | |
1093 externally callable talloc_set_name_const() | |
1094 */ | |
1095 void talloc_set_name_const(const void *ptr, const char *name) | |
1096 { | |
1097 _talloc_set_name_const(ptr, name); | |
1098 } | |
1099 | |
1100 /* | |
1101 create a named talloc pointer. Any talloc pointer can be named, and | |
1102 talloc_named() operates just like talloc() except that it allows you | |
1103 to name the pointer. | |
1104 */ | |
1105 void *talloc_named_const(const void *context, size_t size, const char *name) | |
1106 { | |
1107 return _talloc_named_const(context, size, name); | |
1108 } | |
1109 | |
1110 /* | |
1111 free a talloc pointer. This also frees all child pointers of this | |
1112 pointer recursively | |
1113 | |
1114 return 0 if the memory is actually freed, otherwise -1. The memory | |
1115 will not be freed if the ref_count is > 1 or the destructor (if | |
1116 any) returns non-zero | |
1117 */ | |
1118 int _talloc_free(void *ptr, const char *location) | |
1119 { | |
1120 struct talloc_chunk *tc; | |
1121 | |
1122 if (unlikely(ptr == NULL)) { | |
1123 return -1; | |
1124 } | |
1125 | |
1126 tc = talloc_chunk_from_ptr(ptr); | |
1127 | |
1128 if (unlikely(tc->refs != NULL)) { | |
1129 struct talloc_reference_handle *h; | |
1130 | |
1131 talloc_log("ERROR: talloc_free with references at %s\n", | |
1132 location); | |
1133 | |
1134 for (h=tc->refs; h; h=h->next) { | |
1135 talloc_log("\treference at %s\n", | |
1136 h->location); | |
1137 } | |
1138 return -1; | |
1139 } | |
1140 | |
1141 return _talloc_free_internal(ptr, location); | |
1142 } | |
1143 | |
1144 | |
1145 static INLINE size_t min_size(size_t a, size_t b) | |
1146 { | |
1147 return a > b ? b : a; | |
1148 } | |
1149 | |
1150 /* | |
1151 A talloc version of realloc. The context argument is only used if | |
1152 ptr is NULL | |
1153 */ | |
1154 void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n
ame) | |
1155 { | |
1156 struct talloc_chunk *tc; | |
1157 void *new_ptr; | |
1158 int malloced = 0; | |
1159 | |
1160 /* size zero is equivalent to free() */ | |
1161 if (unlikely(size == 0)) { | |
1162 talloc_unlink(context, ptr); | |
1163 return NULL; | |
1164 } | |
1165 | |
1166 if (unlikely(size >= MAX_TALLOC_SIZE)) { | |
1167 return NULL; | |
1168 } | |
1169 | |
1170 /* realloc(NULL) is equivalent to malloc() */ | |
1171 if (ptr == NULL) { | |
1172 return _talloc_named_const(context, size, name); | |
1173 } | |
1174 | |
1175 tc = talloc_chunk_from_ptr(ptr); | |
1176 | |
1177 /* don't allow realloc on referenced pointers */ | |
1178 if (unlikely(tc->refs)) { | |
1179 return NULL; | |
1180 } | |
1181 | |
1182 /* don't let anybody try to realloc a talloc_pool */ | |
1183 if (unlikely(tc->flags & TALLOC_FLAG_POOL)) { | |
1184 return NULL; | |
1185 } | |
1186 | |
1187 /* don't shrink if we have less than 1k to gain */ | |
1188 if ((size < tc->size) && ((tc->size - size) < 1024)) { | |
1189 tc->size = size; | |
1190 return ptr; | |
1191 } | |
1192 | |
1193 /* by resetting magic we catch users of the old memory */ | |
1194 tc->flags |= TALLOC_FLAG_FREE; | |
1195 | |
1196 #if ALWAYS_REALLOC | |
1197 new_ptr = malloc(size + TC_HDR_SIZE); | |
1198 if (new_ptr) { | |
1199 memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); | |
1200 free(tc); | |
1201 } | |
1202 #else | |
1203 if (tc->flags & TALLOC_FLAG_POOLMEM) { | |
1204 | |
1205 new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE); | |
1206 *talloc_pool_objectcount((struct talloc_chunk *) | |
1207 (tc->pool)) -= 1; | |
1208 | |
1209 if (new_ptr == NULL) { | |
1210 new_ptr = malloc(TC_HDR_SIZE+size); | |
1211 malloced = 1; | |
1212 } | |
1213 | |
1214 if (new_ptr) { | |
1215 memcpy(new_ptr, tc, min_size(tc->size,size) + TC_HDR_SIZ
E); | |
1216 } | |
1217 } | |
1218 else { | |
1219 new_ptr = realloc(tc, size + TC_HDR_SIZE); | |
1220 } | |
1221 #endif | |
1222 if (unlikely(!new_ptr)) { | |
1223 tc->flags &= ~TALLOC_FLAG_FREE; | |
1224 return NULL; | |
1225 } | |
1226 | |
1227 tc = (struct talloc_chunk *)new_ptr; | |
1228 tc->flags &= ~TALLOC_FLAG_FREE; | |
1229 if (malloced) { | |
1230 tc->flags &= ~TALLOC_FLAG_POOLMEM; | |
1231 } | |
1232 if (tc->parent) { | |
1233 tc->parent->child = tc; | |
1234 } | |
1235 if (tc->child) { | |
1236 tc->child->parent = tc; | |
1237 } | |
1238 | |
1239 if (tc->prev) { | |
1240 tc->prev->next = tc; | |
1241 } | |
1242 if (tc->next) { | |
1243 tc->next->prev = tc; | |
1244 } | |
1245 | |
1246 tc->size = size; | |
1247 _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); | |
1248 | |
1249 return TC_PTR_FROM_CHUNK(tc); | |
1250 } | |
1251 | |
1252 /* | |
1253 a wrapper around talloc_steal() for situations where you are moving a pointer | |
1254 between two structures, and want the old pointer to be set to NULL | |
1255 */ | |
1256 void *_talloc_move(const void *new_ctx, const void *_pptr) | |
1257 { | |
1258 const void **pptr = discard_const_p(const void *,_pptr); | |
1259 void *ret = talloc_steal(new_ctx, discard_const_p(void, *pptr)); | |
1260 (*pptr) = NULL; | |
1261 return ret; | |
1262 } | |
1263 | |
1264 /* | |
1265 return the total size of a talloc pool (subtree) | |
1266 */ | |
1267 size_t talloc_total_size(const void *ptr) | |
1268 { | |
1269 size_t total = 0; | |
1270 struct talloc_chunk *c, *tc; | |
1271 | |
1272 if (ptr == NULL) { | |
1273 ptr = null_context; | |
1274 } | |
1275 if (ptr == NULL) { | |
1276 return 0; | |
1277 } | |
1278 | |
1279 tc = talloc_chunk_from_ptr(ptr); | |
1280 | |
1281 if (tc->flags & TALLOC_FLAG_LOOP) { | |
1282 return 0; | |
1283 } | |
1284 | |
1285 tc->flags |= TALLOC_FLAG_LOOP; | |
1286 | |
1287 if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) { | |
1288 total = tc->size; | |
1289 } | |
1290 for (c=tc->child;c;c=c->next) { | |
1291 total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); | |
1292 } | |
1293 | |
1294 tc->flags &= ~TALLOC_FLAG_LOOP; | |
1295 | |
1296 return total; | |
1297 } | |
1298 | |
1299 /* | |
1300 return the total number of blocks in a talloc pool (subtree) | |
1301 */ | |
1302 size_t talloc_total_blocks(const void *ptr) | |
1303 { | |
1304 size_t total = 0; | |
1305 struct talloc_chunk *c, *tc; | |
1306 | |
1307 if (ptr == NULL) { | |
1308 ptr = null_context; | |
1309 } | |
1310 if (ptr == NULL) { | |
1311 return 0; | |
1312 } | |
1313 | |
1314 tc = talloc_chunk_from_ptr(ptr); | |
1315 | |
1316 if (tc->flags & TALLOC_FLAG_LOOP) { | |
1317 return 0; | |
1318 } | |
1319 | |
1320 tc->flags |= TALLOC_FLAG_LOOP; | |
1321 | |
1322 total++; | |
1323 for (c=tc->child;c;c=c->next) { | |
1324 total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); | |
1325 } | |
1326 | |
1327 tc->flags &= ~TALLOC_FLAG_LOOP; | |
1328 | |
1329 return total; | |
1330 } | |
1331 | |
1332 /* | |
1333 return the number of external references to a pointer | |
1334 */ | |
1335 size_t talloc_reference_count(const void *ptr) | |
1336 { | |
1337 struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); | |
1338 struct talloc_reference_handle *h; | |
1339 size_t ret = 0; | |
1340 | |
1341 for (h=tc->refs;h;h=h->next) { | |
1342 ret++; | |
1343 } | |
1344 return ret; | |
1345 } | |
1346 | |
1347 /* | |
1348 report on memory usage by all children of a pointer, giving a full tree view | |
1349 */ | |
1350 void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, | |
1351 void (*callback)(const void *ptr, | |
1352 int depth, int max_depth, | |
1353 int is_ref, | |
1354 void *private_data), | |
1355 void *private_data) | |
1356 { | |
1357 struct talloc_chunk *c, *tc; | |
1358 | |
1359 if (ptr == NULL) { | |
1360 ptr = null_context; | |
1361 } | |
1362 if (ptr == NULL) return; | |
1363 | |
1364 tc = talloc_chunk_from_ptr(ptr); | |
1365 | |
1366 if (tc->flags & TALLOC_FLAG_LOOP) { | |
1367 return; | |
1368 } | |
1369 | |
1370 callback(ptr, depth, max_depth, 0, private_data); | |
1371 | |
1372 if (max_depth >= 0 && depth >= max_depth) { | |
1373 return; | |
1374 } | |
1375 | |
1376 tc->flags |= TALLOC_FLAG_LOOP; | |
1377 for (c=tc->child;c;c=c->next) { | |
1378 if (c->name == TALLOC_MAGIC_REFERENCE) { | |
1379 struct talloc_reference_handle *h = (struct talloc_refer
ence_handle *)TC_PTR_FROM_CHUNK(c); | |
1380 callback(h->ptr, depth + 1, max_depth, 1, private_data); | |
1381 } else { | |
1382 talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1,
max_depth, callback, private_data); | |
1383 } | |
1384 } | |
1385 tc->flags &= ~TALLOC_FLAG_LOOP; | |
1386 } | |
1387 | |
1388 static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_
depth, int is_ref, void *_f) | |
1389 { | |
1390 const char *name = talloc_get_name(ptr); | |
1391 FILE *f = (FILE *)_f; | |
1392 | |
1393 if (is_ref) { | |
1394 fprintf(f, "%*sreference to: %s\n", depth*4, "", name); | |
1395 return; | |
1396 } | |
1397 | |
1398 if (depth == 0) { | |
1399 fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blo
cks)\n", | |
1400 (max_depth < 0 ? "full " :""), name, | |
1401 (unsigned long)talloc_total_size(ptr), | |
1402 (unsigned long)talloc_total_blocks(ptr)); | |
1403 return; | |
1404 } | |
1405 | |
1406 fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", | |
1407 depth*4, "", | |
1408 name, | |
1409 (unsigned long)talloc_total_size(ptr), | |
1410 (unsigned long)talloc_total_blocks(ptr), | |
1411 (int)talloc_reference_count(ptr), ptr); | |
1412 | |
1413 #if 0 | |
1414 fprintf(f, "content: "); | |
1415 if (talloc_total_size(ptr)) { | |
1416 int tot = talloc_total_size(ptr); | |
1417 int i; | |
1418 | |
1419 for (i = 0; i < tot; i++) { | |
1420 if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126))
{ | |
1421 fprintf(f, "%c", ((char *)ptr)[i]); | |
1422 } else { | |
1423 fprintf(f, "~%02x", ((char *)ptr)[i]); | |
1424 } | |
1425 } | |
1426 } | |
1427 fprintf(f, "\n"); | |
1428 #endif | |
1429 } | |
1430 | |
1431 /* | |
1432 report on memory usage by all children of a pointer, giving a full tree view | |
1433 */ | |
1434 void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f
) | |
1435 { | |
1436 if (f) { | |
1437 talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_dept
h_FILE_helper, f); | |
1438 fflush(f); | |
1439 } | |
1440 } | |
1441 | |
1442 /* | |
1443 report on memory usage by all children of a pointer, giving a full tree view | |
1444 */ | |
1445 void talloc_report_full(const void *ptr, FILE *f) | |
1446 { | |
1447 talloc_report_depth_file(ptr, 0, -1, f); | |
1448 } | |
1449 | |
1450 /* | |
1451 report on memory usage by all children of a pointer | |
1452 */ | |
1453 void talloc_report(const void *ptr, FILE *f) | |
1454 { | |
1455 talloc_report_depth_file(ptr, 0, 1, f); | |
1456 } | |
1457 | |
1458 /* | |
1459 report on any memory hanging off the null context | |
1460 */ | |
1461 static void talloc_report_null(void) | |
1462 { | |
1463 if (talloc_total_size(null_context) != 0) { | |
1464 talloc_report(null_context, stderr); | |
1465 } | |
1466 } | |
1467 | |
1468 /* | |
1469 report on any memory hanging off the null context | |
1470 */ | |
1471 static void talloc_report_null_full(void) | |
1472 { | |
1473 if (talloc_total_size(null_context) != 0) { | |
1474 talloc_report_full(null_context, stderr); | |
1475 } | |
1476 } | |
1477 | |
1478 /* | |
1479 enable tracking of the NULL context | |
1480 */ | |
1481 void talloc_enable_null_tracking(void) | |
1482 { | |
1483 if (null_context == NULL) { | |
1484 null_context = _talloc_named_const(NULL, 0, "null_context"); | |
1485 if (autofree_context != NULL) { | |
1486 talloc_reparent(NULL, null_context, autofree_context); | |
1487 } | |
1488 } | |
1489 } | |
1490 | |
1491 /* | |
1492 enable tracking of the NULL context, not moving the autofree context | |
1493 into the NULL context. This is needed for the talloc testsuite | |
1494 */ | |
1495 void talloc_enable_null_tracking_no_autofree(void) | |
1496 { | |
1497 if (null_context == NULL) { | |
1498 null_context = _talloc_named_const(NULL, 0, "null_context"); | |
1499 } | |
1500 } | |
1501 | |
1502 /* | |
1503 disable tracking of the NULL context | |
1504 */ | |
1505 void talloc_disable_null_tracking(void) | |
1506 { | |
1507 if (null_context != NULL) { | |
1508 /* we have to move any children onto the real NULL | |
1509 context */ | |
1510 struct talloc_chunk *tc, *tc2; | |
1511 tc = talloc_chunk_from_ptr(null_context); | |
1512 for (tc2 = tc->child; tc2; tc2=tc2->next) { | |
1513 if (tc2->parent == tc) tc2->parent = NULL; | |
1514 if (tc2->prev == tc) tc2->prev = NULL; | |
1515 } | |
1516 for (tc2 = tc->next; tc2; tc2=tc2->next) { | |
1517 if (tc2->parent == tc) tc2->parent = NULL; | |
1518 if (tc2->prev == tc) tc2->prev = NULL; | |
1519 } | |
1520 tc->child = NULL; | |
1521 tc->next = NULL; | |
1522 } | |
1523 talloc_free(null_context); | |
1524 null_context = NULL; | |
1525 } | |
1526 | |
1527 /* | |
1528 enable leak reporting on exit | |
1529 */ | |
1530 void talloc_enable_leak_report(void) | |
1531 { | |
1532 talloc_enable_null_tracking(); | |
1533 atexit(talloc_report_null); | |
1534 } | |
1535 | |
1536 /* | |
1537 enable full leak reporting on exit | |
1538 */ | |
1539 void talloc_enable_leak_report_full(void) | |
1540 { | |
1541 talloc_enable_null_tracking(); | |
1542 atexit(talloc_report_null_full); | |
1543 } | |
1544 | |
1545 /* | |
1546 talloc and zero memory. | |
1547 */ | |
1548 void *_talloc_zero(const void *ctx, size_t size, const char *name) | |
1549 { | |
1550 void *p = _talloc_named_const(ctx, size, name); | |
1551 | |
1552 if (p) { | |
1553 memset(p, '\0', size); | |
1554 } | |
1555 | |
1556 return p; | |
1557 } | |
1558 | |
1559 /* | |
1560 memdup with a talloc. | |
1561 */ | |
1562 void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name
) | |
1563 { | |
1564 void *newp = _talloc_named_const(t, size, name); | |
1565 | |
1566 if (likely(newp)) { | |
1567 memcpy(newp, p, size); | |
1568 } | |
1569 | |
1570 return newp; | |
1571 } | |
1572 | |
1573 static INLINE char *__talloc_strlendup(const void *t, const char *p, size_t len) | |
1574 { | |
1575 char *ret; | |
1576 | |
1577 ret = (char *)__talloc(t, len + 1); | |
1578 if (unlikely(!ret)) return NULL; | |
1579 | |
1580 memcpy(ret, p, len); | |
1581 ret[len] = 0; | |
1582 | |
1583 _talloc_set_name_const(ret, ret); | |
1584 return ret; | |
1585 } | |
1586 | |
1587 /* | |
1588 strdup with a talloc | |
1589 */ | |
1590 char *talloc_strdup(const void *t, const char *p) | |
1591 { | |
1592 if (unlikely(!p)) return NULL; | |
1593 return __talloc_strlendup(t, p, strlen(p)); | |
1594 } | |
1595 | |
1596 #ifndef HAVE_STRNLEN | |
1597 #define strnlen rep_strnlen | |
1598 static size_t rep_strnlen(const char* s, size_t n) | |
1599 { | |
1600 if (unlikely(!s)) return 0; | |
1601 int i = 0; | |
1602 while (i < n && *s++ != '\0') | |
1603 ++i; | |
1604 return i; | |
1605 } | |
1606 #endif | |
1607 | |
1608 /* | |
1609 strndup with a talloc | |
1610 */ | |
1611 char *talloc_strndup(const void *t, const char *p, size_t n) | |
1612 { | |
1613 if (unlikely(!p)) return NULL; | |
1614 return __talloc_strlendup(t, p, strnlen(p, n)); | |
1615 } | |
1616 | |
1617 static INLINE char *__talloc_strlendup_append(char *s, size_t slen, | |
1618 const char *a, size_t alen) | |
1619 { | |
1620 char *ret; | |
1621 | |
1622 ret = talloc_realloc(NULL, s, char, slen + alen + 1); | |
1623 if (unlikely(!ret)) return NULL; | |
1624 | |
1625 /* append the string and the trailing \0 */ | |
1626 memcpy(&ret[slen], a, alen); | |
1627 ret[slen+alen] = 0; | |
1628 | |
1629 _talloc_set_name_const(ret, ret); | |
1630 return ret; | |
1631 } | |
1632 | |
1633 /* | |
1634 * Appends at the end of the string. | |
1635 */ | |
1636 char *talloc_strdup_append(char *s, const char *a) | |
1637 { | |
1638 if (unlikely(!s)) { | |
1639 return talloc_strdup(NULL, a); | |
1640 } | |
1641 | |
1642 if (unlikely(!a)) { | |
1643 return s; | |
1644 } | |
1645 | |
1646 return __talloc_strlendup_append(s, strlen(s), a, strlen(a)); | |
1647 } | |
1648 | |
1649 /* | |
1650 * Appends at the end of the talloc'ed buffer, | |
1651 * not the end of the string. | |
1652 */ | |
1653 char *talloc_strdup_append_buffer(char *s, const char *a) | |
1654 { | |
1655 size_t slen; | |
1656 | |
1657 if (unlikely(!s)) { | |
1658 return talloc_strdup(NULL, a); | |
1659 } | |
1660 | |
1661 if (unlikely(!a)) { | |
1662 return s; | |
1663 } | |
1664 | |
1665 slen = talloc_get_size(s); | |
1666 if (likely(slen > 0)) { | |
1667 slen--; | |
1668 } | |
1669 | |
1670 return __talloc_strlendup_append(s, slen, a, strlen(a)); | |
1671 } | |
1672 | |
1673 /* | |
1674 * Appends at the end of the string. | |
1675 */ | |
1676 char *talloc_strndup_append(char *s, const char *a, size_t n) | |
1677 { | |
1678 if (unlikely(!s)) { | |
1679 return talloc_strdup(NULL, a); | |
1680 } | |
1681 | |
1682 if (unlikely(!a)) { | |
1683 return s; | |
1684 } | |
1685 | |
1686 return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n)); | |
1687 } | |
1688 | |
1689 /* | |
1690 * Appends at the end of the talloc'ed buffer, | |
1691 * not the end of the string. | |
1692 */ | |
1693 char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) | |
1694 { | |
1695 size_t slen; | |
1696 | |
1697 if (unlikely(!s)) { | |
1698 return talloc_strdup(NULL, a); | |
1699 } | |
1700 | |
1701 if (unlikely(!a)) { | |
1702 return s; | |
1703 } | |
1704 | |
1705 slen = talloc_get_size(s); | |
1706 if (likely(slen > 0)) { | |
1707 slen--; | |
1708 } | |
1709 | |
1710 return __talloc_strlendup_append(s, slen, a, strnlen(a, n)); | |
1711 } | |
1712 | |
1713 #ifndef HAVE_VA_COPY | |
1714 #ifdef HAVE___VA_COPY | |
1715 #define va_copy(dest, src) __va_copy(dest, src) | |
1716 #else | |
1717 #define va_copy(dest, src) (dest) = (src) | |
1718 #endif | |
1719 #endif | |
1720 | |
1721 char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) | |
1722 { | |
1723 int len; | |
1724 char *ret; | |
1725 va_list ap2; | |
1726 | |
1727 va_copy(ap2, ap); | |
1728 len = vsnprintf(NULL, 0, fmt, ap2); | |
1729 va_end(ap2); | |
1730 if (unlikely(len < 0)) { | |
1731 return NULL; | |
1732 } | |
1733 | |
1734 ret = (char *)__talloc(t, len+1); | |
1735 if (unlikely(!ret)) return NULL; | |
1736 | |
1737 va_copy(ap2, ap); | |
1738 vsnprintf(ret, len+1, fmt, ap2); | |
1739 va_end(ap2); | |
1740 | |
1741 _talloc_set_name_const(ret, ret); | |
1742 return ret; | |
1743 } | |
1744 | |
1745 | |
1746 /* | |
1747 Perform string formatting, and return a pointer to newly allocated | |
1748 memory holding the result, inside a memory pool. | |
1749 */ | |
1750 char *talloc_asprintf(const void *t, const char *fmt, ...) | |
1751 { | |
1752 va_list ap; | |
1753 char *ret; | |
1754 | |
1755 va_start(ap, fmt); | |
1756 ret = talloc_vasprintf(t, fmt, ap); | |
1757 va_end(ap); | |
1758 return ret; | |
1759 } | |
1760 | |
1761 static INLINE char *__talloc_vaslenprintf_append(char *s, size_t slen, | |
1762 const char *fmt, va_list ap) | |
1763 PRINTF_ATTRIBUTE(3,0); | |
1764 | |
1765 static INLINE char *__talloc_vaslenprintf_append(char *s, size_t slen, | |
1766 const char *fmt, va_list ap) | |
1767 { | |
1768 /* ssize_t isn't present on Windows. */ | |
1769 #ifndef _MSC_VER | |
1770 ssize_t alen; | |
1771 #else | |
1772 size_t alen; | |
1773 #endif | |
1774 va_list ap2; | |
1775 | |
1776 va_copy(ap2, ap); | |
1777 alen = vsnprintf(NULL, 0, fmt, ap2); | |
1778 va_end(ap2); | |
1779 | |
1780 if (alen <= 0) { | |
1781 /* Either the vsnprintf failed or the format resulted in | |
1782 * no characters being formatted. In the former case, we | |
1783 * ought to return NULL, in the latter we ought to return | |
1784 * the original string. Most current callers of this | |
1785 * function expect it to never return NULL. | |
1786 */ | |
1787 return s; | |
1788 } | |
1789 | |
1790 s = talloc_realloc(NULL, s, char, slen + alen + 1); | |
1791 if (!s) return NULL; | |
1792 | |
1793 va_copy(ap2, ap); | |
1794 vsnprintf(s + slen, alen + 1, fmt, ap2); | |
1795 va_end(ap2); | |
1796 | |
1797 _talloc_set_name_const(s, s); | |
1798 return s; | |
1799 } | |
1800 | |
1801 /** | |
1802 * Realloc @p s to append the formatted result of @p fmt and @p ap, | |
1803 * and return @p s, which may have moved. Good for gradually | |
1804 * accumulating output into a string buffer. Appends at the end | |
1805 * of the string. | |
1806 **/ | |
1807 char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) | |
1808 { | |
1809 if (unlikely(!s)) { | |
1810 return talloc_vasprintf(NULL, fmt, ap); | |
1811 } | |
1812 | |
1813 return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap); | |
1814 } | |
1815 | |
1816 /** | |
1817 * Realloc @p s to append the formatted result of @p fmt and @p ap, | |
1818 * and return @p s, which may have moved. Always appends at the | |
1819 * end of the talloc'ed buffer, not the end of the string. | |
1820 **/ | |
1821 char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) | |
1822 { | |
1823 size_t slen; | |
1824 | |
1825 if (unlikely(!s)) { | |
1826 return talloc_vasprintf(NULL, fmt, ap); | |
1827 } | |
1828 | |
1829 slen = talloc_get_size(s); | |
1830 if (likely(slen > 0)) { | |
1831 slen--; | |
1832 } | |
1833 | |
1834 return __talloc_vaslenprintf_append(s, slen, fmt, ap); | |
1835 } | |
1836 | |
1837 /* | |
1838 Realloc @p s to append the formatted result of @p fmt and return @p | |
1839 s, which may have moved. Good for gradually accumulating output | |
1840 into a string buffer. | |
1841 */ | |
1842 char *talloc_asprintf_append(char *s, const char *fmt, ...) | |
1843 { | |
1844 va_list ap; | |
1845 | |
1846 va_start(ap, fmt); | |
1847 s = talloc_vasprintf_append(s, fmt, ap); | |
1848 va_end(ap); | |
1849 return s; | |
1850 } | |
1851 | |
1852 /* | |
1853 Realloc @p s to append the formatted result of @p fmt and return @p | |
1854 s, which may have moved. Good for gradually accumulating output | |
1855 into a buffer. | |
1856 */ | |
1857 char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) | |
1858 { | |
1859 va_list ap; | |
1860 | |
1861 va_start(ap, fmt); | |
1862 s = talloc_vasprintf_append_buffer(s, fmt, ap); | |
1863 va_end(ap); | |
1864 return s; | |
1865 } | |
1866 | |
1867 /* | |
1868 alloc an array, checking for integer overflow in the array size | |
1869 */ | |
1870 void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char
*name) | |
1871 { | |
1872 if (count >= MAX_TALLOC_SIZE/el_size) { | |
1873 return NULL; | |
1874 } | |
1875 return _talloc_named_const(ctx, el_size * count, name); | |
1876 } | |
1877 | |
1878 /* | |
1879 alloc an zero array, checking for integer overflow in the array size | |
1880 */ | |
1881 void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const
char *name) | |
1882 { | |
1883 if (count >= MAX_TALLOC_SIZE/el_size) { | |
1884 return NULL; | |
1885 } | |
1886 return _talloc_zero(ctx, el_size * count, name); | |
1887 } | |
1888 | |
1889 /* | |
1890 realloc an array, checking for integer overflow in the array size | |
1891 */ | |
1892 void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned
count, const char *name) | |
1893 { | |
1894 if (count >= MAX_TALLOC_SIZE/el_size) { | |
1895 return NULL; | |
1896 } | |
1897 return _talloc_realloc(ctx, ptr, el_size * count, name); | |
1898 } | |
1899 | |
1900 /* | |
1901 a function version of talloc_realloc(), so it can be passed as a function poin
ter | |
1902 to libraries that want a realloc function (a realloc function encapsulates | |
1903 all the basic capabilities of an allocation library, which is why this is usef
ul) | |
1904 */ | |
1905 void *talloc_realloc_fn(const void *context, void *ptr, size_t size) | |
1906 { | |
1907 return _talloc_realloc(context, ptr, size, NULL); | |
1908 } | |
1909 | |
1910 | |
1911 static int talloc_autofree_destructor(void *ptr) | |
1912 { | |
1913 autofree_context = NULL; | |
1914 return 0; | |
1915 } | |
1916 | |
1917 static void talloc_autofree(void) | |
1918 { | |
1919 talloc_free(autofree_context); | |
1920 } | |
1921 | |
1922 /* | |
1923 return a context which will be auto-freed on exit | |
1924 this is useful for reducing the noise in leak reports | |
1925 */ | |
1926 void *talloc_autofree_context(void) | |
1927 { | |
1928 if (autofree_context == NULL) { | |
1929 autofree_context = _talloc_named_const(NULL, 0, "autofree_contex
t"); | |
1930 talloc_set_destructor(autofree_context, talloc_autofree_destruct
or); | |
1931 atexit(talloc_autofree); | |
1932 } | |
1933 return autofree_context; | |
1934 } | |
1935 | |
1936 size_t talloc_get_size(const void *context) | |
1937 { | |
1938 struct talloc_chunk *tc; | |
1939 | |
1940 if (context == NULL) { | |
1941 context = null_context; | |
1942 } | |
1943 if (context == NULL) { | |
1944 return 0; | |
1945 } | |
1946 | |
1947 tc = talloc_chunk_from_ptr(context); | |
1948 | |
1949 return tc->size; | |
1950 } | |
1951 | |
1952 /* | |
1953 find a parent of this context that has the given name, if any | |
1954 */ | |
1955 void *talloc_find_parent_byname(const void *context, const char *name) | |
1956 { | |
1957 struct talloc_chunk *tc; | |
1958 | |
1959 if (context == NULL) { | |
1960 return NULL; | |
1961 } | |
1962 | |
1963 tc = talloc_chunk_from_ptr(context); | |
1964 while (tc) { | |
1965 if (tc->name && strcmp(tc->name, name) == 0) { | |
1966 return TC_PTR_FROM_CHUNK(tc); | |
1967 } | |
1968 while (tc && tc->prev) tc = tc->prev; | |
1969 if (tc) { | |
1970 tc = tc->parent; | |
1971 } | |
1972 } | |
1973 return NULL; | |
1974 } | |
1975 | |
1976 /* | |
1977 show the parentage of a context | |
1978 */ | |
1979 void talloc_show_parents(const void *context, FILE *file) | |
1980 { | |
1981 struct talloc_chunk *tc; | |
1982 | |
1983 if (context == NULL) { | |
1984 fprintf(file, "talloc no parents for NULL\n"); | |
1985 return; | |
1986 } | |
1987 | |
1988 tc = talloc_chunk_from_ptr(context); | |
1989 fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); | |
1990 while (tc) { | |
1991 fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))
); | |
1992 while (tc && tc->prev) tc = tc->prev; | |
1993 if (tc) { | |
1994 tc = tc->parent; | |
1995 } | |
1996 } | |
1997 fflush(file); | |
1998 } | |
1999 | |
2000 /* | |
2001 return 1 if ptr is a parent of context | |
2002 */ | |
2003 int talloc_is_parent(const void *context, const void *ptr) | |
2004 { | |
2005 struct talloc_chunk *tc; | |
2006 | |
2007 if (context == NULL) { | |
2008 return 0; | |
2009 } | |
2010 | |
2011 tc = talloc_chunk_from_ptr(context); | |
2012 while (tc) { | |
2013 if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; | |
2014 while (tc && tc->prev) tc = tc->prev; | |
2015 if (tc) { | |
2016 tc = tc->parent; | |
2017 } | |
2018 } | |
2019 return 0; | |
2020 } | |
OLD | NEW |