39
39
* which points to the block header, marked as allocated and guaranteed
40
40
* to have enough space to hold the requested data size; or 0 meaning, that the
41
41
* heap has no more room to provide such a block (reasons for that:
42
* heap is corrupted, heap has no provision to be expanded, expansion failed,
42
* heap is corrupt, heap has no provision to be expanded, expansion failed,
43
43
* or the heap was attached read-only).
45
45
* An application program can then use the data field on its need,
58
58
* with 'HEAP_Free' call, supplying the address of the block header
61
* Prior the heap use, the initialization is required, which comprises
61
* Prior to the heap use, the initialization is required, which comprises
62
62
* call to either 'HEAP_Create' or 'HEAP_Attach' with the information about
63
* the base heap pointer. 'HEAP_Create' also requires the size of initial
63
* the base heap pointer. 'HEAP_Create' also takes the size of initial
64
64
* heap area (if there is one), and size of chunk (usually, a page size)
65
65
* to be used in heap expansions (defaults to alignment if provided as 0).
66
66
* Additionally (but not compulsory) the application program can provide
67
* heap manager with 'expand' routine, which is supposed to be called,
68
* when no more room is available in the heap, or the heap was not
67
* heap manager with 'resize' routine, which is supposed to be called,
68
* when no more room is available in the heap, or the heap has not been
69
69
* preallocated (base = 0 in 'HEAP_Create'), and given the arguments:
70
70
* - current heap base address (or 0 if this is the very first heap alloc),
71
* - new required heap size (or 0 if this is the very last call to deallocate
71
* - new required heap size (or 0 if this is the last call to deallocate
72
72
* the entire heap).
73
* If successful, the expand routine must return the new heap base
73
* If successful, the resize routine must return the new heap base
74
74
* address (if any) of expanded heap area, and where the exact copy of
75
75
* the current heap is made.
77
77
* Note that all heap base pointers must be aligned on a 'double' boundary.
78
78
* Please also be warned not to store pointers to the heap area, as a
79
* garbage collection can clobber them. Within a block, however,
79
* garbage collection can clobber them. Within a block, however,
80
80
* it is possible to use local pointers (offsets), which remain same
81
81
* regardless of garbage collections.
84
84
* the next block (either free, or used) from the heap. Given a NULL-pointer,
85
85
* this function returns the very first block, whereas all subsequent calls
86
86
* with the argument being the last observed block results in the next block
87
* returned. NULL comes back when no more blocks exist in the heap.
87
* returned. NULL comes back when no more blocks exist in the heap.
89
* Note that for proper heap operations, no allocations should happen between
89
* Note that for proper heap operations, no allocation(s) should happen between
90
90
* successive calls to 'HEAP_Walk', whereas deallocation of the seen block
93
93
* Explicit heap traversing should not overcome the heap limit,
94
94
* as any information above the limit is not maintained by the heap manager.
95
* Every heap operation guarantees, that there are no adjacent free blocks,
95
* Every heap operation guarantees that there are no adjacent free blocks,
96
96
* only used blocks can follow each other sequentially.
98
98
* To discontinue to use the heap, 'HEAP_Destroy' or 'HEAP_Detach' can be
99
* called. The former deallocates the heap (by means of a call to 'expand'),
99
* called. The former deallocates the heap (by means of a call to 'resize'),
100
100
* the latter just removes the heap handle, retaining the heap data intact.
101
101
* Later, such a heap could be used again if attached with 'HEAP_Attach'.
115
115
#include <stdlib.h>
116
116
#include <string.h>
118
#if defined(NCBI_OS_MSWIN) && defined(_WIN64)
119
/* Disable ptr->long conversion warning (even on explicit cast!) */
120
# pragma warning (disable : 4311)
121
#endif /*NCBI_OS_MSWIN && _WIN64*/
119
124
struct SHEAP_tag {
125
int/*bool*/ copy; /* (!=0) keeps user's serial number if provided */
125
void* base; /* Current base of heap extent: !base == !size */
126
TNCBI_Size size; /* Current size of heap extent: !base == !size */
127
TNCBI_Size chunk; /* == 0 when the heap is read-only */
128
FHEAP_Resize resize; /* != NULL when resizeable (only for non-RO heaps*/
129
void* arg; /* Aux argument to pass to "resize" */
130
unsigned int refc; /* Reference counter (copy heaps only,0=original)*/
131
int serial; /* Serial number as assigned by user(Attach|Copy)*/
134
140
#define HEAP_FREE 0
135
141
#define HEAP_ISFREE(b) (((b)->flag & ~HEAP_LAST) == HEAP_FREE)
136
142
#define HEAP_ISUSED(b) (((b)->flag & ~HEAP_LAST) == HEAP_USED)
137
#define HEAP_ISLAST(b) ((b)->flag & HEAP_LAST)
143
#define HEAP_ISLAST(b) ( (b)->flag & HEAP_LAST)
140
146
HEAP HEAP_Create(void* base, TNCBI_Size size,
141
TNCBI_Size chunk, FHEAP_Expand expand, void* arg)
147
TNCBI_Size chunk, FHEAP_Resize resize, void* arg)
155
min_size = size ? (TNCBI_Size) HEAP_ALIGN(sizeof(*b) + 1) : 0;
156
if (size < min_size) {
157
CORE_LOGF(eLOG_Error, ("Heap Create: Storage too small "
158
"(provided %u, required %u+)",
159
(unsigned) size, (unsigned) min_size));
162
if (!(heap = (HEAP) malloc(sizeof(*heap))))
166
heap->chunk = chunk ? (TNCBI_Size) HEAP_ALIGN(chunk) : 0;
167
heap->resize = heap->chunk ? resize : 0;
168
heap->arg = heap->resize ? arg : 0;
169
heap->refc = 0/*original*/;
172
/* Reformat the pre-allocated heap */
173
if (HEAP_ALIGN(base) != (unsigned long) base) {
174
CORE_LOGF(eLOG_Warning,
175
("Heap Create: Unaligned base (0x%08lX)", (long) base));
177
b = (SHEAP_Block*) base;
178
b->flag = HEAP_FREE | HEAP_LAST;
185
HEAP HEAP_AttachEx(const void* base, TNCBI_Size size, int serial)
146
189
if (!base != !size || !(heap = (HEAP) malloc(sizeof(*heap))))
148
chunk = (TNCBI_Size) HEAP_ALIGN(chunk);
150
size = (TNCBI_Size) _HEAP_ALIGN(sizeof(*b) + 1, chunk);
151
if (!size || !expand || !(base = (*expand)(0, size, arg))) {
152
CORE_LOGF(eLOG_Warning,
153
("Heap Create: Cannot create (size = %u)",
154
(unsigned)(size ? size : sizeof(*b))));
159
if ((void*) HEAP_ALIGN(base) != base) {
160
CORE_LOGF(eLOG_Warning,
161
("Heap Create: Unaligned base (0x%08lX)", (long) base));
163
if (size < (TNCBI_Size) HEAP_ALIGN(sizeof(*b) + 1)) {
164
CORE_LOGF(eLOG_Warning, ("Heap Create: Heap is too small (%u, %u)",
165
(unsigned) size, (unsigned) sizeof(*b)));
170
heap->expand = expand;
171
heap->arg = expand ? arg : 0;
172
heap->copy = 0/*original*/;
173
b = (SHEAP_Block*) heap->base;
174
b->flag = HEAP_FREE | HEAP_LAST;
180
HEAP HEAP_AttachEx(const void* base, TNCBI_Size size)
184
if (!base || !size || !(heap = (HEAP) malloc(sizeof(*heap))))
186
if ((void*) HEAP_ALIGN(base) != base) {
191
if (HEAP_ALIGN(base) != (unsigned long) base) {
187
192
CORE_LOGF(eLOG_Warning,
188
193
("Heap Attach: Unaligned base (0x%08lX)", (long) base));
190
195
heap->base = (void*) base;
191
196
heap->size = size;
192
197
heap->chunk = 0/*read-only*/;
195
heap->copy = 0/*original*/;
200
heap->refc = 0/*original*/;
201
heap->serial = serial;
200
HEAP HEAP_Attach(const void* base)
206
HEAP HEAP_Attach(const void* base, int serial)
208
for (b = (SHEAP_Block*) base; ; b = (SHEAP_Block*)((char*) b + b->size)) {
209
if (!HEAP_ISUSED(b) && !HEAP_ISFREE(b)) {
210
CORE_LOGF(eLOG_Warning,
211
("Heap Attach: Heap corrupted (0x%08X, %u)",
212
b->flag, (unsigned) b->size));
211
const SHEAP_Block* b = (const SHEAP_Block*) base;
214
if (!HEAP_ISUSED(b) && !HEAP_ISFREE(b)) {
215
CORE_LOGF(eLOG_Warning,
216
("Heap Attach: Heap corrupted (%u, 0x%08X, %u)",
217
n, b->flag, (unsigned) b->size));
223
b = (const SHEAP_Block*)((const char*) b + b->size);
219
return HEAP_AttachEx(base, size);
226
return HEAP_AttachEx(base, size, serial);
233
240
assert(HEAP_ISFREE(b));
234
241
if (!HEAP_ISLAST(b) && HEAP_ISFREE(n)) {
235
242
b->size += n->size;
237
244
n = (SHEAP_Block*)((char*) n + n->size);
246
assert(!p || (SHEAP_Block*)((char*) p + p->size) == b);
239
247
if (p && HEAP_ISFREE(p)) {
240
248
p->size += b->size;
247
255
/* Collect garbage in the heap, moving all contents to the
248
* top of the heap, and merging all free blocks at the end
249
* in one large free block. Return pointer to that free block.
256
* top of, and merging all free blocks at the end into one
257
* large free block. Return pointer to that free block, or
258
* NULL if there is no free space in the heap.
251
260
static SHEAP_Block* s_HEAP_Collect(HEAP heap)
276
285
/* Take the block 'b' (maybe split in two, if it's roomy enough)
277
* for use of by at most 'size' bytes (including block header).
278
* Return the block to use if taken okay; 0 otherwise.
286
* for use of by at most 'size' bytes (aligned, and block header included).
287
* Return the block to use.
280
289
static SHEAP_Block* s_HEAP_Take(SHEAP_Block* b, TNCBI_Size size)
282
291
unsigned int last = b->flag & HEAP_LAST;
284
if (b->size >= size + sizeof(*b)) {
293
assert(b->size >= size);
294
if (b->size > size + sizeof(*b)) {
285
295
SHEAP_Block* n = (SHEAP_Block*)((char*) b + size);
287
297
n->flag = HEAP_FREE | last;
308
static const char* s_HEAP_Id(char* buf, HEAP h)
312
sprintf(buf, "[%u]", h->refc);
298
317
SHEAP_Block* HEAP_Alloc(HEAP heap, TNCBI_Size size)
300
319
SHEAP_Block* b, *p = 0;
301
320
TNCBI_Size free = 0;
303
if (!heap || size < 1) {
305
CORE_LOG(eLOG_Warning, "Heap Alloc: Cannot alloc in NULL heap");
325
CORE_LOG(eLOG_Warning, "Heap Alloc: Cannot alloc in NULL heap");
328
assert(!heap->base == !heap->size);
309
332
if (!heap->chunk) {
310
CORE_LOG(eLOG_Warning, "Heap Alloc: Heap is read-only");
333
CORE_LOGF(eLOG_Warning, ("Heap Alloc%s: Heap is read-only",
334
s_HEAP_Id(_id, heap)));
314
338
size = (TNCBI_Size) HEAP_ALIGN(sizeof(*b) + size);
316
340
b = (SHEAP_Block*) heap->base;
317
while ((char*) b < (char*) heap->base + heap->size) {
341
for (n = 0; (char*) b < (char*) heap->base + heap->size; n++) {
318
342
if (HEAP_ISFREE(b)) {
319
343
/* if an empty, large enough block found, then take it! */
320
344
if (b->size >= size)
323
347
} else if (!HEAP_ISUSED(b)) {
324
348
CORE_LOGF(eLOG_Warning,
325
("Heap Alloc: Heap corrupted (0x%08X, %u)",
326
b->flag, (unsigned) b->size));
349
("Heap Alloc%s: Heap corrupted (%u, 0x%08X, %u)",
350
s_HEAP_Id(_id, heap), n, b->flag, (unsigned) b->size));
330
354
b = (SHEAP_Block*)((char*) b + b->size);
356
assert(!p || HEAP_ISLAST(p));
333
358
/* Heap exhausted, no free block found */
334
359
if (free >= size)
335
360
b = s_HEAP_Collect(heap);
336
else if (!heap->expand)
361
else if (!heap->resize)
339
364
TNCBI_Size hsize =
341
366
ptrdiff_t dp = (char*) p - (char*) heap->base;
344
if (!(base = (*heap->expand)(heap->base, hsize, heap->arg)))
369
if (!(base = (*heap->resize)(heap->base, hsize, heap->arg)))
346
371
p = (SHEAP_Block*)((char*) base + dp);
348
CORE_LOG(eLOG_Warning, "Heap Alloc: Last block lost");
349
if (HEAP_ISUSED(p)) {
350
p->flag &= ~HEAP_LAST;
351
/* New block is the very top on the heap */
352
b = (SHEAP_Block*)((char*) base + heap->size);
353
b->size = hsize - heap->size;
354
b->flag = HEAP_FREE | HEAP_LAST;
356
/* Extend last free block */
357
p->size += hsize - heap->size;
373
p->flag = HEAP_FREE | HEAP_LAST;
377
if (!HEAP_ISLAST(p)) {
378
CORE_LOGF(eLOG_Warning, ("Heap Alloc%s: Last block marker "
379
"lost", s_HEAP_Id(_id, heap)));
381
if (HEAP_ISUSED(p)) {
382
p->flag &= ~HEAP_LAST;
383
/* New block is the very top on the heap */
384
b = (SHEAP_Block*)((char*) base + heap->size);
385
b->size = hsize - heap->size;
386
b->flag = HEAP_FREE | HEAP_LAST;
388
/* Extend last free block */
389
p->size += hsize - heap->size;
360
393
heap->base = base;
361
394
heap->size = hsize;
363
assert(b && HEAP_ISFREE(b) && b->size >= size);
396
assert(b && HEAP_ISFREE(b));
364
397
return s_HEAP_Take(b, size);
368
401
void HEAP_Free(HEAP heap, SHEAP_Block* ptr)
370
SHEAP_Block* b, *p = 0;
374
CORE_LOG(eLOG_Warning, "Heap Free: Cannot free in NULL heap");
408
CORE_LOG(eLOG_Warning, "Heap Free: Cannot free in NULL heap");
411
assert(!heap->base == !heap->size);
378
415
if (!heap->chunk) {
379
CORE_LOG(eLOG_Warning, "Heap Free: Heap is read-only");
416
CORE_LOGF(eLOG_Warning, ("Heap Free%s: Heap is read-only",
417
s_HEAP_Id(_id, heap)));
383
421
b = (SHEAP_Block*) heap->base;
384
while ((char*) b < (char*) heap->base + heap->size) {
422
for (p = 0, n = 0; (char*) b < (char*) heap->base + heap->size; n++) {
385
423
if (HEAP_ISFREE(b)) {
387
CORE_LOG(eLOG_Warning, "Heap Free: Freeing free block");
425
CORE_LOGF(eLOG_Warning, ("Heap Free%s: Freeing free block",
426
s_HEAP_Id(_id, heap)));
390
429
} else if (HEAP_ISUSED(b)) {
397
436
CORE_LOGF(eLOG_Warning,
398
("Heap Free: Heap corrupted (0x%08X, %u)",
399
b->flag, (unsigned) b->size));
437
("Heap Free%s: Heap corrupted (%u, 0x%08X, %u)",
438
s_HEAP_Id(_id, heap), n, b->flag, (unsigned) b->size));
403
442
b = (SHEAP_Block*)((char*) p + p->size);
405
CORE_LOG(eLOG_Warning, "Heap Free: Block not found");
444
CORE_LOGF(eLOG_Warning, ("Heap Free%s: Block not found",
445
s_HEAP_Id(_id, heap)));
409
449
SHEAP_Block* HEAP_Walk(const HEAP heap, const SHEAP_Block* p)
414
455
CORE_LOG(eLOG_Warning, "Heap Walk: NULL heap");
418
((char*) p >= (char*) heap->base &&
419
(char*) p < (char*) heap->base + heap->size)) {
458
assert(!heap->base == !heap->size);
460
if (!p || ((char*) p >= (char*) heap->base &&
461
(char*) p < (char*) heap->base + heap->size)) {
420
462
b = (SHEAP_Block*)(p ? (char*) p + p->size : (char*) heap->base);
421
463
if ((char*) b < (char*) heap->base + heap->size) {
422
if (b->size >= sizeof(*b) &&
423
b->size == (TNCBI_Size) HEAP_ALIGN(b->size) &&
424
(char*) b + b->size <= (char*) heap->base + heap->size &&
425
(HEAP_ISFREE(b) || HEAP_ISUSED(b))) {
464
if (b->size >= sizeof(*b)/*compatibility: ">" should be here now*/
465
&& b->size == (TNCBI_Size) HEAP_ALIGN(b->size)
466
&& (char*) b + b->size <= (char*) heap->base + heap->size
467
&& (HEAP_ISFREE(b) || HEAP_ISUSED(b))) {
426
468
/* Block 'b' seems valid, but... */
430
CORE_LOG(eLOG_Warning, "Heap Walk: Misplaced last block");
431
else if (HEAP_ISFREE(b) && HEAP_ISFREE(p)) {
471
if (HEAP_ISLAST(p)) {
472
CORE_LOGF(eLOG_Warning, ("Heap Walk%s: Misplaced last "
473
"block", s_HEAP_Id(_id, heap)));
474
} else if (HEAP_ISFREE(p) && HEAP_ISFREE(b)) {
432
475
const SHEAP_Block* c = (const SHEAP_Block*) heap->base;
433
476
while ((char*) c < (char*) p) {
434
477
if (HEAP_ISFREE(c) &&
439
482
if ((char*) c < (char*) p)
441
CORE_LOG(eLOG_Warning, "Heap Walk: Adjacent free blocks");
484
CORE_LOGF(eLOG_Warning, ("Heap Walk%s: Adjacent free "
485
"blocks", s_HEAP_Id(_id, heap)));
445
489
CORE_LOGF(eLOG_Warning,
446
("Heap Walk: Heap corrupted (0x%08X, %u)",
447
b->flag, (unsigned) b->size));
448
} else if ((char*) b > (char*) heap->base + heap->size)
449
CORE_LOG(eLOG_Warning, "Heap Walk: Heap corrupted");
450
else if (!HEAP_ISLAST(p))
451
CORE_LOG(eLOG_Warning, "Heap Walk: Last block lost");
453
CORE_LOG(eLOG_Warning, "Heap Walk: Alien pointer passed");
490
("Heap Walk%s: Heap corrupted (0x%08X, %u)",
491
s_HEAP_Id(_id, heap), b->flag, (unsigned) b->size));
493
} else if ((char*) b > (char*) heap->base + heap->size) {
494
CORE_LOGF(eLOG_Warning, ("Heap Walk%s: Heap corrupted",
495
s_HEAP_Id(_id, heap)));
496
} else if (b && !HEAP_ISLAST(p)) {
497
CORE_LOGF(eLOG_Warning, ("Heap Walk%s: Last block lost",
498
s_HEAP_Id(_id, heap)));
501
CORE_LOGF(eLOG_Warning, ("Heap Walk%s: Alien pointer",
502
s_HEAP_Id(_id, heap)));
458
TNCBI_Size HEAP_Trim(HEAP heap)
508
HEAP HEAP_Trim(HEAP heap)
460
510
TNCBI_Size size, last_size;
465
516
if (!heap->chunk) {
466
517
CORE_LOG(eLOG_Error, "Heap Trim: Heap is read-only");
470
if (!(f =s_HEAP_Collect(heap)) || HEAP_ISUSED(f) || f->size < heap->chunk){
521
if (!(f = s_HEAP_Collect(heap)) || f->size < heap->chunk) {
522
assert(!f || HEAP_ISFREE(f));
473
} else if ((char*) f != heap->base) {
474
assert(f->size >= _HEAP_ALIGNMENT);
475
last_size = f->size % heap->chunk;
477
if (last_size < _HEAP_ALIGNMENT)
478
last_size += heap->chunk;
479
size = heap->size - f->size + last_size;
481
SHEAP_Block* b = (SHEAP_Block*) heap->base, *p = 0;
484
b = (SHEAP_Block*)((char*) b + b->size);
486
size = heap->size - f->size;
525
} else if (!(last_size = f->size % heap->chunk)) {
526
SHEAP_Block* b = (SHEAP_Block*) heap->base, *p = 0;
529
b = (SHEAP_Block*)((char*) b + b->size);
531
size = heap->size - f->size;
491
last_size = heap->chunk;
534
if (last_size <= sizeof(*f))
535
last_size = _HEAP_ALIGN(sizeof(*f) + 1, heap->chunk);
536
size = heap->size - f->size + last_size;
495
assert(size % heap->chunk == 0);
497
void* base = (*heap->expand)(heap->base, size, heap->arg);
540
void* base = (*heap->resize)(heap->base, size, heap->arg);
501
546
ptrdiff_t dp = (char*) f - (char*) heap->base;
508
553
heap->size = size;
509
554
} else if (size != heap->size)
510
555
CORE_LOG(eLOG_Error, "Heap Trim: Heap is not trimmable");
557
assert(!heap->base == !heap->size);
515
HEAP HEAP_CopySerial(const HEAP heap, size_t extra, int serial)
562
HEAP HEAP_Copy(const HEAP heap, size_t extra, int serial)
569
assert(!heap->base == !heap->size);
571
size = HEAP_ALIGN(heap->size);
520
572
extra = HEAP_ALIGN(extra);
522
!(newbase = malloc(HEAP_ALIGN(heap->size) + extra + sizeof(*newheap))))
573
if (!(newheap = (HEAP)malloc(HEAP_ALIGN(sizeof(*newheap)) + size + extra)))
524
memcpy(newbase, heap->base, heap->size);
525
newheap = (HEAP)((char*) newbase + HEAP_ALIGN(heap->size) + extra);
526
newheap->base = newbase;
575
newheap->base = (heap->base
576
? (char*) newheap + HEAP_ALIGN(sizeof(*newheap))
527
578
newheap->size = heap->size;
528
579
newheap->chunk = 0/*read-only*/;
530
581
newheap->arg = 0;
531
newheap->copy = serial ? serial : 1/*copy*/;
582
newheap->refc = 1/*copy*/;
583
newheap->serial = serial;
585
memcpy((char*) newheap->base, heap->base, heap->size);
586
memset((char*) newheap->base + heap->size, 0, size - heap->size);
592
HEAP HEAP_CopySerial(const HEAP heap, size_t extra, int serial)
594
return HEAP_Copy(heap, extra, serial);
598
void HEAP_AddRef(HEAP heap)
602
assert(!heap->base == !heap->size);
536
610
void HEAP_Detach(HEAP heap)
539
free(heap->copy ? heap->base : heap);
614
assert(!heap->base == !heap->size);
615
if (!heap->refc || !--heap->refc) {
616
memset(heap, 0, sizeof(*heap));
543
622
void HEAP_Destroy(HEAP heap)
546
if (!heap->chunk && !heap->copy)
547
CORE_LOG(eLOG_Warning, "Heap Destroy: Heap is read-only");
548
else if (heap->expand/*NB: false for heap copies*/)
549
(*heap->expand)(heap->base, 0, heap->arg);
626
assert(!heap->base == !heap->size);
627
if (!heap->chunk && !heap->refc)
628
CORE_LOG(eLOG_Warning, "Heap Destroy: Heap is read-only");
629
else if (heap->resize/*NB: NULL for heap copies*/)
630
verify((*heap->resize)(heap->base, 0, heap->arg) == 0);
555
635
void* HEAP_Base(const HEAP heap)
557
return heap ? heap->base : 0;
639
assert(!heap->base == !heap->size);
561
644
TNCBI_Size HEAP_Size(const HEAP heap)
563
return heap ? heap->size : 0;
648
assert(!heap->base == !heap->size);
567
653
int HEAP_Serial(const HEAP heap)
569
return heap ? heap->copy : 0;
657
assert(!heap->base == !heap->size);
574
663
* --------------------------------------------------------------------------
575
664
* $Log: ncbi_heapmgr.c,v $
665
* Revision 6.31 2006/04/28 16:19:44 lavr
666
* Disable W4311 for MSVC/W64
668
* Revision 6.30 2006/03/06 20:26:00 lavr
669
* Added a paranoid assert() to check ref.-count overflow
671
* Revision 6.29 2006/03/06 14:22:48 lavr
672
* Cast (void*) to (char*) to allow ptr arithmetics
674
* Revision 6.28 2006/03/05 17:35:56 lavr
675
* API revised to allow to create ref-counted heap copies
576
677
* Revision 6.27 2005/01/27 19:00:17 lavr
577
678
* Explicit cast of malloc()ed memory