📄 ncbi_heapmgr.c
字号:
/* * =========================================================================== * PRODUCTION $Log: ncbi_heapmgr.c,v $ * PRODUCTION Revision 1000.0 2003/10/29 16:36:50 gouriano * PRODUCTION PRODUCTION: IMPORTED [ORIGINAL] Dev-tree R6.26 * PRODUCTION * =========================================================================== *//* $Id: ncbi_heapmgr.c,v 1000.0 2003/10/29 16:36:50 gouriano Exp $ * =========================================================================== * * PUBLIC DOMAIN NOTICE * National Center for Biotechnology Information * * This software/database is a "United States Government Work" under the * terms of the United States Copyright Act. It was written as part of * the author's official duties as a United States Government employee and * thus cannot be copyrighted. This software/database is freely available * to the public for use. The National Library of Medicine and the U.S. * Government have not placed any restriction on its use or reproduction. * * Although all reasonable efforts have been taken to ensure the accuracy * and reliability of the software and data, the NLM and the U.S. * Government do not and cannot warrant the performance or results that * may be obtained by using this software or data. The NLM and the U.S. * Government disclaim all warranties, express or implied, including * warranties of performance, merchantability or fitness for any particular * purpose. * * Please cite the author in any work or product based on this material. * * =========================================================================== * * Author: Anton Lavrentiev * * Abstract: * * This is a simple heap manager with a primitive garbage collection. * The heap contains blocks of data, stored in the common contiguous pool, * each block preceded with a SHEAP_Block structure. Low word of 'flag' * is either non-zero (True), when the block is in use, or zero (False), * when the block is vacant. 'Size' shows the length of the block in bytes, * (uninterpreted) data field of which is extended past the header * (the header size IS counted in the size of the block). * * When 'HEAP_Alloc' is called, the return value is either a heap pointer, * which points to the block header, marked as allocated and guaranteed * to have enough space to hold the requested data size; or 0 meaning, that the * heap has no more room to provide such a block (reasons for that: * heap is corrupted, heap has no provision to be expanded, expansion failed, * or the heap was attached read-only). * * An application program can then use the data field on its need, * providing not to overcome the size limit. The current block header * can be used to find the next heap block with the use of 'size' member * (note, however, some restrictions below). * * The application program is NOT assumed to keep the returned block pointer, * as the garbage collection can occur on the next allocation attempt, * thus making any heap pointers invalid. Instead, the application program * can keep track of the heap base (header of the very first heap block - * see 'HEAP_Create'), and the size of the heap, and can traverse the heap by * this means, or with call to 'HEAP_Walk' (described below). * * While traversing, if the block found is no longer needed, it can be freed * with 'HEAP_Free' call, supplying the address of the block header * as an argument. * * Prior the heap use, the initialization is required, which comprises * call to either 'HEAP_Create' or 'HEAP_Attach' with the information about * the base heap pointer. 'HEAP_Create' also requires the size of initial * heap area (if there is one), and size of chunk (usually, a page size) * to be used in heap expansions (defaults to alignment if provided as 0). * Additionally (but not compulsory) the application program can provide * heap manager with 'expand' routine, which is supposed to be called, * when no more room is available in the heap, or the heap was not * preallocated (base = 0 in 'HEAP_Create'), and given the arguments: * - current heap base address (or 0 if this is the very first heap alloc), * - new required heap size (or 0 if this is the very last call to deallocate * the entire heap). * If successful, the expand routine must return the new heap base * address (if any) of expanded heap area, and where the exact copy of * the current heap is made. * * Note that all heap base pointers must be aligned on a 'double' boundary. * Please also be warned not to store pointers to the heap area, as a * garbage collection can clobber them. Within a block, however, * it is possible to use local pointers (offsets), which remain same * regardless of garbage collections. * * For automatic traverse purposes there is a 'HEAP_Walk' call, which returns * the next block (either free, or used) from the heap. Given a NULL-pointer, * this function returns the very first block, whereas all subsequent calls * with the argument being the last observed block results in the next block * returned. NULL comes back when no more blocks exist in the heap. * * Note that for proper heap operations, no allocations should happen between * successive calls to 'HEAP_Walk', whereas deallocation of the seen block * is okay. * * Explicit heap traversing should not overcome the heap limit, * as any information above the limit is not maintained by the heap manager. * Every heap operation guarantees, that there are no adjacent free blocks, * only used blocks can follow each other sequentially. * * To discontinue to use the heap, 'HEAP_Destroy' or 'HEAP_Detach' can be * called. The former deallocates the heap (by means of a call to 'expand'), * the latter just removes the heap handle, retaining the heap data intact. * Later, such a heap could be used again if attached with 'HEAP_Attach'. * * Note that attached heap is in read-only mode, that is nothing can be * allocated and/or freed in that heap, as well as an attempt to call * 'HEAP_Destroy' will not destroy the heap data. * * Note also, that 'HEAP_Create' always does heap reset, that is the * memory area pointed by 'base' (if not 0) gets reformatted and lose * all previous contents. * */#include "ncbi_priv.h"#include <connect/ncbi_heapmgr.h>#include <stdlib.h>#include <string.h>struct SHEAP_tag { void* base; TNCBI_Size size; TNCBI_Size chunk; FHEAP_Expand expand; void* arg; int/*bool*/ copy; /* (!=0) keeps user's serial number if provided */};#define _HEAP_ALIGN(a, b) (((unsigned long)(a) + (b) - 1) & ~((b) - 1))#define _HEAP_ALIGNMENT sizeof(double)#define HEAP_ALIGN(a) _HEAP_ALIGN(a, _HEAP_ALIGNMENT)#define HEAP_LAST 0x80000000UL#define HEAP_USED 0x0DEAD2F0UL#define HEAP_FREE 0#define HEAP_ISFREE(b) (((b)->flag & ~HEAP_LAST) == HEAP_FREE)#define HEAP_ISUSED(b) (((b)->flag & ~HEAP_LAST) == HEAP_USED)#define HEAP_ISLAST(b) ((b)->flag & HEAP_LAST)HEAP HEAP_Create(void* base, TNCBI_Size size, TNCBI_Size chunk, FHEAP_Expand expand, void* arg){ SHEAP_Block* b; HEAP heap; if (!base != !size || !(heap = (HEAP) malloc(sizeof(*heap)))) return 0; chunk = (TNCBI_Size) HEAP_ALIGN(chunk); if (!base) { size = (TNCBI_Size) _HEAP_ALIGN(sizeof(*b) + 1, chunk); if (!size || !expand || !(base = (*expand)(0, size, arg))) { CORE_LOGF(eLOG_Warning, ("Heap Create: Cannot create (size = %u)", (unsigned)(size ? size : sizeof(*b)))); free(heap); return 0; } } if ((void*) HEAP_ALIGN(base) != base) { CORE_LOGF(eLOG_Warning, ("Heap Create: Unaligned base (0x%08lX)", (long) base)); } if (size < (TNCBI_Size) HEAP_ALIGN(sizeof(*b) + 1)) { CORE_LOGF(eLOG_Warning, ("Heap Create: Heap is too small (%u, %u)", (unsigned) size, (unsigned) sizeof(*b))); } heap->base = base; heap->size = size; heap->chunk = chunk; heap->expand = expand; heap->arg = expand ? arg : 0; heap->copy = 0/*original*/; b = (SHEAP_Block*) heap->base; b->flag = HEAP_FREE | HEAP_LAST; b->size = size; return heap;}HEAP HEAP_AttachEx(const void* base, TNCBI_Size size){ HEAP heap; if (!base || !size || !(heap = malloc(sizeof(*heap)))) return 0; if ((void*) HEAP_ALIGN(base) != base) { CORE_LOGF(eLOG_Warning, ("Heap Attach: Unaligned base (0x%08lX)", (long) base)); } heap->base = (void*) base; heap->size = size; heap->chunk = 0/*read-only*/; heap->expand = 0; heap->arg = 0; heap->copy = 0/*original*/; return heap;}HEAP HEAP_Attach(const void* base){ TNCBI_Size size; SHEAP_Block* b; if (!base) return 0; size = 0; for (b = (SHEAP_Block*) base; ; b = (SHEAP_Block*)((char*) b + b->size)) { if (!HEAP_ISUSED(b) && !HEAP_ISFREE(b)) { CORE_LOGF(eLOG_Warning, ("Heap Attach: Heap corrupted (0x%08X, %u)", b->flag, (unsigned) b->size)); return 0; } size += b->size; if (HEAP_ISLAST(b)) break; } return HEAP_AttachEx(base, size);}/* Check if a given block 'b' is adjacent to a free block, which * follows 'b', and/or to optionally passed previous block 'p'. * Join block(s) to form a larger free block, and return a pointer * to the next block. */static SHEAP_Block* s_HEAP_Join(SHEAP_Block* p, SHEAP_Block* b){ /* Block following 'b' */ SHEAP_Block* n = (SHEAP_Block*)((char*) b + b->size); assert(HEAP_ISFREE(b)); if (!HEAP_ISLAST(b) && HEAP_ISFREE(n)) { b->size += n->size; b->flag = n->flag; n = (SHEAP_Block*)((char*) n + n->size); } if (p && HEAP_ISFREE(p)) { p->size += b->size; p->flag = b->flag; } return n;}/* Collect garbage in the heap, moving all contents to the * top of the heap, and merging all free blocks at the end * in one large free block. Return pointer to that free block. */static SHEAP_Block* s_HEAP_Collect(HEAP heap){ SHEAP_Block* b = (SHEAP_Block*) heap->base, *f = 0; while ((char*) b < (char*) heap->base + heap->size) { if (HEAP_ISFREE(b)) f = b; else if (HEAP_ISUSED(b) && f) { unsigned int last = b->flag & HEAP_LAST; TNCBI_Size save = f->size; memmove(f, b, b->size); f->flag &= ~HEAP_LAST; f = (SHEAP_Block*)((char*) f + f->size); f->flag = HEAP_FREE | last; f->size = save; b = s_HEAP_Join(0, f); continue; } b = (SHEAP_Block*)((char*) b + b->size); } return f;}/* Take the block 'b' (maybe split in two, if it's roomy enough) * for use of by at most 'size' bytes (including block header). * Return the block to use if taken okay; 0 otherwise. */static SHEAP_Block* s_HEAP_Take(SHEAP_Block* b, TNCBI_Size size){ unsigned int last = b->flag & HEAP_LAST; if (b->size >= size + sizeof(*b)) { SHEAP_Block* n = (SHEAP_Block*)((char*) b + size); n->flag = HEAP_FREE | last; n->size = b->size - size; b->flag = HEAP_USED; b->size = size; s_HEAP_Join(0, n); } else b->flag = HEAP_USED | last; return b;}SHEAP_Block* HEAP_Alloc(HEAP heap, TNCBI_Size size){ SHEAP_Block* b, *p = 0; TNCBI_Size free = 0; if (!heap || size < 1) { if (size) CORE_LOG(eLOG_Warning, "Heap Alloc: Cannot alloc in NULL heap"); return 0; } if (!heap->chunk) { CORE_LOG(eLOG_Warning, "Heap Alloc: Heap is read-only"); return 0; } size = (TNCBI_Size) HEAP_ALIGN(sizeof(*b) + size); b = (SHEAP_Block*) heap->base; while ((char*) b < (char*) heap->base + heap->size) { if (HEAP_ISFREE(b)) { /* if an empty, large enough block found, then take it! */ if (b->size >= size) return s_HEAP_Take(b, size); free += b->size; } else if (!HEAP_ISUSED(b)) { CORE_LOGF(eLOG_Warning, ("Heap Alloc: Heap corrupted (0x%08X, %u)", b->flag, (unsigned) b->size)); return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -