r2718: - added a talloc_unreference() function as requested by metze.
[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 static const void *null_context;
34
35 struct talloc_reference_handle {
36         struct talloc_reference_handle *next, *prev;
37         void *ptr;
38 };
39
40 typedef int (*talloc_destructor_t)(void *);
41
42 struct talloc_chunk {
43         struct talloc_chunk *next, *prev;
44         struct talloc_chunk *parent, *child;
45         struct talloc_reference_handle *refs;
46         size_t size;
47         uint_t magic;
48         talloc_destructor_t destructor;
49         const char *name;
50 };
51
52 /* panic if we get a bad magic value */
53 static struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
54 {
55         struct talloc_chunk *tc = ((struct talloc_chunk *)discard_const(ptr))-1;
56         if (tc->magic != TALLOC_MAGIC) {
57                 if (tc->magic == TALLOC_MAGIC_FREE) {
58                         smb_panic("Bad talloc magic value - double free\n");
59                 } else {
60                         smb_panic("Bad talloc magic value\n");
61                 }
62         }
63         return tc;
64 }
65
66 /* 
67    Allocate a bit of memory as a child of an existing pointer
68 */
69 void *_talloc(const void *context, size_t size)
70 {
71         struct talloc_chunk *tc;
72
73         if (context == NULL) {
74                 context = null_context;
75         }
76
77         if (size >= MAX_TALLOC_SIZE) {
78                 return NULL;
79         }
80
81         tc = malloc(sizeof(*tc)+size);
82         if (tc == NULL) {
83                 return NULL;
84         }
85
86         tc->size = size;
87         tc->magic = TALLOC_MAGIC;
88         tc->destructor = NULL;
89         tc->child = NULL;
90         tc->name = NULL;
91         tc->refs = NULL;
92
93         if (context) {
94                 struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
95
96                 tc->parent = parent;
97
98                 if (parent->child) {
99                         parent->child->parent = NULL;
100                 }
101
102                 DLIST_ADD(parent->child, tc);
103         } else {
104                 tc->next = tc->prev = tc->parent = NULL;
105         }
106
107         return (void *)(tc+1);
108 }
109
110
111 /*
112   setup a destructor to be called on free of a pointer
113   the destructor should return 0 on success, or -1 on failure.
114   if the destructor fails then the free is failed, and the memory can
115   be continued to be used
116 */
117 void talloc_set_destructor(const void *ptr, int (*destructor)(void *))
118 {
119         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
120         tc->destructor = destructor;
121 }
122
123 /*
124   increase the reference count on a piece of memory. 
125 */
126 void talloc_increase_ref_count(const void *ptr)
127 {
128         talloc_reference(null_context, ptr);
129 }
130
131 /*
132   helper for talloc_reference()
133 */
134 static int talloc_reference_destructor(void *ptr)
135 {
136         struct talloc_reference_handle *handle = ptr;
137         struct talloc_chunk *tc1 = talloc_chunk_from_ptr(ptr);
138         struct talloc_chunk *tc2 = talloc_chunk_from_ptr(handle->ptr);
139         if (tc1->destructor != (talloc_destructor_t)-1) {
140                 tc1->destructor = NULL;
141         }
142         DLIST_REMOVE(tc2->refs, handle);
143         talloc_free(handle);
144         return 0;
145 }
146
147 /*
148   make a secondary reference to a pointer, hanging off the given context.
149   the pointer remains valid until both the original caller and this given
150   context are freed.
151   
152   the major use for this is when two different structures need to reference the 
153   same underlying data, and you want to be able to free the two instances separately,
154   and in either order
155 */
156 void *talloc_reference(const void *context, const void *ptr)
157 {
158         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
159         struct talloc_reference_handle *handle;
160         handle = talloc_named_const(context, sizeof(*handle), ".reference");
161         if (handle == NULL) {
162                 return NULL;
163         }
164         /* note that we hang the destructor off the handle, not the
165            main context as that allows the caller to still setup their
166            own destructor on the context if they want to */
167         talloc_set_destructor(handle, talloc_reference_destructor);
168         handle->ptr = discard_const(ptr);
169         DLIST_ADD(tc->refs, handle);
170         return handle->ptr;
171 }
172
173 /*
174   remove a secondary reference to a pointer. This undo's what
175   talloc_reference() has done. The context and pointer arguments
176   must match those given to a talloc_reference()
177 */
178 void *talloc_unreference(const void *context, const void *ptr)
179 {
180         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
181         struct talloc_reference_handle *h;
182
183         if (context == NULL) {
184                 context = null_context;
185         }
186
187         for (h=tc->refs;h;h=h->next) {
188                 struct talloc_chunk *tc2 = talloc_chunk_from_ptr(h);
189                 const void *parent = tc2->parent?tc2->parent+1:null_context;
190                 if (parent == context) break;
191         }
192         if (h == NULL) {
193                 return NULL;
194         }
195
196         talloc_set_destructor(h, NULL);
197         DLIST_REMOVE(tc->refs, h);
198         talloc_free(h);
199         return discard_const(ptr);
200 }
201
202 /*
203   add a name to an existing pointer - va_list version
204 */
205 static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
206
207 static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
208 {
209         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
210         tc->name = talloc_vasprintf(ptr, fmt, ap);
211         if (tc->name) {
212                 talloc_set_name_const(tc->name, ".name");
213         }
214 }
215
216 /*
217   add a name to an existing pointer
218 */
219 void talloc_set_name(const void *ptr, const char *fmt, ...) _PRINTF_ATTRIBUTE(2,3)
220 {
221         va_list ap;
222         va_start(ap, fmt);
223         talloc_set_name_v(ptr, fmt, ap);
224         va_end(ap);
225 }
226
227 /*
228    more efficient way to add a name to a pointer - the name must point to a 
229    true string constant
230 */
231 void talloc_set_name_const(const void *ptr, const char *name)
232 {
233         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
234         tc->name = name;
235 }
236
237 /*
238   create a named talloc pointer. Any talloc pointer can be named, and
239   talloc_named() operates just like talloc() except that it allows you
240   to name the pointer.
241 */
242 void *talloc_named(const void *context, size_t size, 
243                    const char *fmt, ...) _PRINTF_ATTRIBUTE(3,4)
244 {
245         va_list ap;
246         void *ptr;
247
248         ptr = _talloc(context, size);
249         if (ptr == NULL) {
250                 return NULL;
251         }
252
253         va_start(ap, fmt);
254         talloc_set_name_v(ptr, fmt, ap);
255         va_end(ap);
256
257         return ptr;
258 }
259
260 /*
261   create a named talloc pointer. Any talloc pointer can be named, and
262   talloc_named() operates just like talloc() except that it allows you
263   to name the pointer.
264 */
265 void *talloc_named_const(const void *context, size_t size, const char *name)
266 {
267         void *ptr;
268
269         ptr = _talloc(context, size);
270         if (ptr == NULL) {
271                 return NULL;
272         }
273
274         talloc_set_name_const(ptr, name);
275
276         return ptr;
277 }
278
279 /*
280   return the name of a talloc ptr, or "UNNAMED"
281 */
282 const char *talloc_get_name(const void *ptr)
283 {
284         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
285         if (tc->name) {
286                 return tc->name;
287         }
288         return "UNNAMED";
289 }
290
291 /*
292   this is for compatibility with older versions of talloc
293 */
294 void *talloc_init(const char *fmt, ...) _PRINTF_ATTRIBUTE(1,2)
295 {
296         va_list ap;
297         void *ptr;
298
299         ptr = _talloc(NULL, 0);
300         if (ptr == NULL) {
301                 return NULL;
302         }
303
304         va_start(ap, fmt);
305         talloc_set_name_v(ptr, fmt, ap);
306         va_end(ap);
307
308         return ptr;
309 }
310
311
312 /* 
313    free a talloc pointer. This also frees all child pointers of this 
314    pointer recursively
315
316    return 0 if the memory is actually freed, otherwise -1. The memory
317    will not be freed if the ref_count is > 1 or the destructor (if
318    any) returns non-zero
319 */
320 int talloc_free(void *ptr)
321 {
322         struct talloc_chunk *tc;
323
324         if (ptr == NULL) {
325                 return -1;
326         }
327
328         tc = talloc_chunk_from_ptr(ptr);
329
330         if (tc->refs) {
331                 talloc_reference_destructor(tc->refs);
332                 return 0;
333         }
334
335         if (tc->destructor) {
336                 talloc_destructor_t d = tc->destructor;
337                 if (d == (talloc_destructor_t)-1) {
338                         return -1;
339                 }
340                 tc->destructor = (talloc_destructor_t)-1;
341                 if (d(ptr) == -1) {
342                         tc->destructor = d;
343                         return -1;
344                 }
345                 tc->destructor = NULL;
346         }
347
348         while (tc->child) {
349                 /* we need to work out who will own an abandoned child
350                    if it cannot be freed. In priority order, the first
351                    choice is owner of any remaining reference to this
352                    pointer, the second choice is our parent, and the
353                    final choice is the null context. */
354                 void *child = tc->child+1;
355                 const void *new_parent = null_context;
356                 if (tc->child->refs) {
357                         struct talloc_chunk *ref = talloc_chunk_from_ptr(tc->child->refs);
358                         if (ref->parent) new_parent = ref->parent+1;
359                 }
360                 if (new_parent == null_context && tc->parent) {
361                         new_parent = tc->parent+1;
362                 }
363                 talloc_free(talloc_steal(new_parent, child));
364         }
365
366         if (tc->parent) {
367                 DLIST_REMOVE(tc->parent->child, tc);
368                 if (tc->parent->child) {
369                         tc->parent->child->parent = tc->parent;
370                 }
371         } else {
372                 if (tc->prev) tc->prev->next = tc->next;
373                 if (tc->next) tc->next->prev = tc->prev;
374         }
375
376         tc->magic = TALLOC_MAGIC_FREE;
377
378         free(tc);
379         return 0;
380 }
381
382
383
384 /*
385   A talloc version of realloc. The context argument is only used if
386   ptr is NULL
387 */
388 void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
389 {
390         struct talloc_chunk *tc;
391         void *new_ptr;
392
393         /* size zero is equivalent to free() */
394         if (size == 0) {
395                 talloc_free(ptr);
396                 return NULL;
397         }
398
399         /* realloc(NULL) is equavalent to malloc() */
400         if (ptr == NULL) {
401                 return talloc_named_const(context, size, name);
402         }
403
404         tc = talloc_chunk_from_ptr(ptr);
405
406         /* don't allow realloc on referenced pointers */
407         if (tc->refs) {
408                 return NULL;
409         }
410
411         /* by resetting magic we catch users of the old memory */
412         tc->magic = TALLOC_MAGIC_FREE;
413
414         new_ptr = realloc(tc, size + sizeof(*tc));
415         if (!new_ptr) {
416                 tc->magic = TALLOC_MAGIC;
417                 return NULL;
418         }
419
420         tc = new_ptr;
421         tc->magic = TALLOC_MAGIC;
422         if (tc->parent) {
423                 tc->parent->child = new_ptr;
424         }
425
426         if (tc->prev) {
427                 tc->prev->next = tc;
428         }
429         if (tc->next) {
430                 tc->next->prev = tc;
431         }
432
433         tc->size = size;
434         talloc_set_name_const(tc+1, name);
435
436         return (void *)(tc+1);
437 }
438
439 /* 
440    move a lump of memory from one talloc context to another return the
441    ptr on success, or NULL if it could not be transferred
442 */
443 void *talloc_steal(const void *new_ctx, const void *ptr)
444 {
445         struct talloc_chunk *tc, *new_tc;
446
447         if (!ptr) {
448                 return NULL;
449         }
450
451         tc = talloc_chunk_from_ptr(ptr);
452
453         if (new_ctx == NULL) {
454                 if (tc->parent) {
455                         DLIST_REMOVE(tc->parent->child, tc);
456                         if (tc->parent->child) {
457                                 tc->parent->child->parent = tc->parent;
458                         }
459                 } else {
460                         if (tc->prev) tc->prev->next = tc->next;
461                         if (tc->next) tc->next->prev = tc->prev;
462                 }
463                 
464                 tc->parent = tc->next = tc->prev = NULL;
465                 return discard_const(ptr);
466         }
467
468         new_tc = talloc_chunk_from_ptr(new_ctx);
469
470         if (tc == new_tc) {
471                 return discard_const(ptr);
472         }
473
474         if (tc->parent) {
475                 DLIST_REMOVE(tc->parent->child, tc);
476                 if (tc->parent->child) {
477                         tc->parent->child->parent = tc->parent;
478                 }
479         } else {
480                 if (tc->prev) tc->prev->next = tc->next;
481                 if (tc->next) tc->next->prev = tc->prev;
482         }
483
484         tc->parent = new_tc;
485         if (new_tc->child) new_tc->child->parent = NULL;
486         DLIST_ADD(new_tc->child, tc);
487
488         return discard_const(ptr);
489 }
490
491 /*
492   return the total size of a talloc pool (subtree)
493 */
494 off_t talloc_total_size(const void *ptr)
495 {
496         off_t total = 0;
497         struct talloc_chunk *c, *tc;
498         
499         if (ptr == NULL) {
500                 ptr = null_context;
501         }
502         if (ptr == NULL) {
503                 return 0;
504         }
505
506         tc = talloc_chunk_from_ptr(ptr);
507
508         total = tc->size;
509         for (c=tc->child;c;c=c->next) {
510                 total += talloc_total_size(c+1);
511         }
512         return total;
513 }
514
515 /*
516   return the total number of blocks in a talloc pool (subtree)
517 */
518 static off_t talloc_total_blocks(const void *ptr)
519 {
520         off_t total = 0;
521         struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
522
523         total++;
524         for (c=tc->child;c;c=c->next) {
525                 total += talloc_total_blocks(c+1);
526         }
527         return total;
528 }
529
530 /*
531   return the number of external references to a pointer
532 */
533 static int talloc_reference_count(const void *ptr)
534 {
535         struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
536         struct talloc_reference_handle *h;
537         int ret = 0;
538
539         for (h=tc->refs;h;h=h->next) {
540                 ret++;
541         }
542         return ret;
543 }
544
545 /*
546   report on memory usage by all children of a pointer, giving a full tree view
547 */
548 static void talloc_report_depth(const void *ptr, FILE *f, int depth)
549 {
550         struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
551
552         for (c=tc->child;c;c=c->next) {
553                 const char *name = talloc_get_name(c+1);
554                 if (strcmp(name, ".reference") == 0) {
555                         struct talloc_reference_handle *handle = (void *)(c+1);
556                         const char *name2 = talloc_get_name(handle->ptr);
557                         fprintf(f, "%*sreference to: %s\n", depth*4, "", name2);
558                 } else {
559                         fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d)\n", 
560                                 depth*4, "",
561                                 name,
562                                 (unsigned long)talloc_total_size(c+1),
563                                 (unsigned long)talloc_total_blocks(c+1),
564                                 talloc_reference_count(c+1));
565                         talloc_report_depth(c+1, f, depth+1);
566                 }
567         }
568
569 }
570
571 /*
572   report on memory usage by all children of a pointer, giving a full tree view
573 */
574 void talloc_report_full(const void *ptr, FILE *f)
575 {
576         if (ptr == NULL) {
577                 ptr = null_context;
578         }
579         if (ptr == NULL) return;
580
581         fprintf(f,"full talloc report on '%s' (total %lu bytes in %lu blocks)\n", 
582                 talloc_get_name(ptr), 
583                 (unsigned long)talloc_total_size(ptr),
584                 (unsigned long)talloc_total_blocks(ptr));
585
586         talloc_report_depth(ptr, f, 1);
587 }
588
589 /*
590   report on memory usage by all children of a pointer
591 */
592 void talloc_report(const void *ptr, FILE *f)
593 {
594         struct talloc_chunk *c, *tc;
595
596         if (ptr == NULL) {
597                 ptr = null_context;
598         }
599         if (ptr == NULL) return;
600        
601         fprintf(f,"talloc report on '%s' (total %lu bytes in %lu blocks)\n", 
602                 talloc_get_name(ptr), 
603                 (unsigned long)talloc_total_size(ptr),
604                 (unsigned long)talloc_total_blocks(ptr));
605
606         tc = talloc_chunk_from_ptr(ptr);
607
608         for (c=tc->child;c;c=c->next) {
609                 fprintf(f, "\t%-30s contains %6lu bytes in %3lu blocks\n", 
610                         talloc_get_name(c+1),
611                         (unsigned long)talloc_total_size(c+1),
612                         (unsigned long)talloc_total_blocks(c+1));
613         }
614
615 }
616
617 /*
618   report on any memory hanging off the null context
619 */
620 static void talloc_report_null(void)
621 {
622         if (talloc_total_size(null_context) == 0) {
623                 return;
624         }
625         talloc_report(null_context, stderr);
626 }
627
628 /*
629   report on any memory hanging off the null context
630 */
631 static void talloc_report_null_full(void)
632 {
633         if (talloc_total_size(null_context) == 0) {
634                 return;
635         }
636         talloc_report_full(null_context, stderr);
637 }
638
639 /*
640   enable leak reporting on exit
641 */
642 void talloc_enable_leak_report(void)
643 {
644         null_context = talloc_named_const(NULL, 0, "null_context");
645         atexit(talloc_report_null);
646 }
647
648 /*
649   enable full leak reporting on exit
650 */
651 void talloc_enable_leak_report_full(void)
652 {
653         null_context = talloc_named_const(NULL, 0, "null_context");
654         atexit(talloc_report_null_full);
655 }
656
657 /* 
658    talloc and zero memory. 
659 */
660 void *talloc_zero(const void *ctx, size_t size)
661 {
662         void *p = talloc(ctx, size);
663
664         if (p) {
665                 memset(p, '\0', size);
666         }
667
668         return p;
669 }
670
671
672 /*
673   memdup with a talloc. 
674 */
675 void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
676 {
677         void *newp = talloc_named_const(t, size, name);
678
679         if (newp) {
680                 memcpy(newp, p, size);
681         }
682
683         return newp;
684 }
685
686 /*
687   strdup with a talloc 
688 */
689 char *talloc_strdup(const void *t, const char *p)
690 {
691         char *ret;
692         if (!p) {
693                 return NULL;
694         }
695         ret = talloc_memdup(t, p, strlen(p) + 1);
696         if (ret) {
697                 talloc_set_name_const(ret, ret);
698         }
699         return ret;
700 }
701
702 /*
703   strndup with a talloc 
704 */
705 char *talloc_strndup(const void *t, const char *p, size_t n)
706 {
707         size_t len = strnlen(p, n);
708         char *ret;
709
710         ret = talloc(t, len + 1);
711         if (!ret) { return NULL; }
712         memcpy(ret, p, len);
713         ret[len] = 0;
714         return ret;
715 }
716
717 char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) _PRINTF_ATTRIBUTE(2,0)
718 {       
719         int len;
720         char *ret;
721         va_list ap2;
722         
723         VA_COPY(ap2, ap);
724
725         len = vsnprintf(NULL, 0, fmt, ap2);
726
727         ret = talloc(t, len+1);
728         if (ret) {
729                 VA_COPY(ap2, ap);
730                 vsnprintf(ret, len+1, fmt, ap2);
731                 talloc_set_name_const(ret, ret);
732         }
733
734         return ret;
735 }
736
737
738 /*
739   Perform string formatting, and return a pointer to newly allocated
740   memory holding the result, inside a memory pool.
741  */
742 char *talloc_asprintf(const void *t, const char *fmt, ...) _PRINTF_ATTRIBUTE(2,3)
743 {
744         va_list ap;
745         char *ret;
746
747         va_start(ap, fmt);
748         ret = talloc_vasprintf(t, fmt, ap);
749         va_end(ap);
750         return ret;
751 }
752
753
754 /**
755  * Realloc @p s to append the formatted result of @p fmt and @p ap,
756  * and return @p s, which may have moved.  Good for gradually
757  * accumulating output into a string buffer.
758  **/
759
760 static char *talloc_vasprintf_append(char *s,
761                                      const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
762
763 static char *talloc_vasprintf_append(char *s,
764                                      const char *fmt, va_list ap)
765 {       
766         int len, s_len;
767         va_list ap2;
768
769         VA_COPY(ap2, ap);
770
771         if (s) {
772                 s_len = strlen(s);
773         } else {
774                 s_len = 0;
775         }
776         len = vsnprintf(NULL, 0, fmt, ap2);
777
778         s = talloc_realloc(NULL, s, s_len + len+1);
779         if (!s) return NULL;
780
781         VA_COPY(ap2, ap);
782
783         vsnprintf(s+s_len, len+1, fmt, ap2);
784         talloc_set_name_const(s, s);
785
786         return s;
787 }
788
789 /*
790   Realloc @p s to append the formatted result of @p fmt and return @p
791   s, which may have moved.  Good for gradually accumulating output
792   into a string buffer.
793  */
794 char *talloc_asprintf_append(char *s,
795                              const char *fmt, ...) _PRINTF_ATTRIBUTE(2,3)
796 {
797         va_list ap;
798
799         va_start(ap, fmt);
800         s = talloc_vasprintf_append(s, fmt, ap);
801         va_end(ap);
802         return s;
803 }
804
805 /*
806   alloc an array, checking for integer overflow in the array size
807 */
808 void *talloc_array(const void *ctx, size_t el_size, uint_t count, const char *name)
809 {
810         if (count == 0 ||
811             count >= MAX_TALLOC_SIZE/el_size) {
812                 return NULL;
813         }
814         return talloc_named_const(ctx, el_size * count, name);
815 }
816
817
818 /*
819   realloc an array, checking for integer overflow in the array size
820 */
821 void *talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, uint_t count, const char *name)
822 {
823         if (count == 0 ||
824             count >= MAX_TALLOC_SIZE/el_size) {
825                 return NULL;
826         }
827         ptr = talloc_realloc(ctx, ptr, el_size * count);
828         if (ptr) {
829                 talloc_set_name_const(ptr, name);
830         }
831         return ptr;
832 }
833
834 /*
835   a alloc function for ldb that uses talloc
836 */
837 void *talloc_ldb_alloc(void *context, void *ptr, size_t size)
838 {
839         if (ptr == NULL) {
840                 return talloc(context, size);
841         }
842         if (size == 0) {
843                 talloc_free(ptr);
844                 return NULL;
845         }
846         return talloc_realloc(context, ptr, size);
847 }