00001
00006 #include "ctl/ctldef.h"
00007 #include "ctl/ll.h"
00008 #include "ctl/debug.h"
00009
00010 #ifndef CTL_UNIT
00011
00013 static void ctl_defaultnewhandler( const char* sz, size_t needed )
00014 {
00015 debug_log( LOG_ERROR, "%s\nAllocation failure, needed %d bytes\n", sz, needed );
00016 exit(EXIT_FAILURE);
00017 }
00018
00020 static ctl_newhandler curr_newhandler = ctl_defaultnewhandler;
00021
00023 ctl_newhandler ctl_set_new_handler( ctl_newhandler handler )
00024 {
00025 ctl_newhandler ret = curr_newhandler;
00026 curr_newhandler = handler ? handler : ctl_defaultnewhandler;
00027 assertcodeptr(curr_newhandler);
00028 return ret;
00029 }
00030
00034 typedef void (*destruct_t)(void*);
00035 typedef struct AllocList
00036 {
00037 #ifndef NDEBUG
00038 uint32 check;
00039 dll_decllink(struct AllocList,allocated);
00040 const char* szFileLine;
00041 #define HEAD_CHECK 0xBEA75F0D
00042 #define TAIL_CHECK 0x0D5FA7BE
00043 #define MEM_ABSURDREFS 100000001
00044 #endif
00045 int32 refcount;
00046 size_t size;
00047 destruct_t destruct;
00048 ppDebug(uint32 check2; )
00049 } AllocList;
00050 dll_autolist(AllocList,allocated, memoryList);
00051
00052 #define ALLOC_MOD 4
00053 #define ALLOC_OFFSET(ptr) ((ptr)?((AllocList*)(ptr)+1):(AllocList*)(ptr))
00054 #define DETAIL_OFFSET(ptr) ((ptr)?((AllocList*)(ptr)-1):(AllocList*)(ptr))
00055 #define ALLOC_EXCESS(size) ( sizeof(AllocList) + (size) + sizeof(uint32) )
00056 #define ALLOC_TAILPTR(ptr) ((uint32*)((uint8*)ptr+(DETAIL_OFFSET(ptr)->size)))
00057 #define DETAIL_TAILPTR(data) ((uint32*)((uint8*)(data+1)+((data)->size)))
00058
00062 static void justmemory( void* pmem )
00063 {
00064 #if !defined(NDEBUG) || defined(CTL_FILL_FREE)
00065 #ifndef CTL_FILL_FREE
00066 #define CTL_FILL_FREE 0xFD
00067 #endif
00068 AllocList* data = DETAIL_OFFSET(pmem);
00069 memset( data, CTL_FILL_FREE+0, ALLOC_EXCESS(data->size) );
00070 #endif
00071 }
00072
00073 bool _ctlmalloc( void** ptr, size_t size ppDebugParam(const char* szFileLine) )
00074 {
00075 size_t needsize = ALLOC_EXCESS(size);
00076 void* newptr = malloc( needsize );
00077 if( NULL == newptr )
00078 {
00079 assertcodeptr(curr_newhandler);
00080 curr_newhandler( ppDebRel(szFileLine,"_ctlmalloc"), size );
00081 newptr = malloc( needsize );
00082 if( NULL == newptr )
00083 curr_newhandler( ppDebRel(szFileLine,"_ctlmalloc"), size );
00084 }
00085 if( NULL == newptr )
00086 {
00087 *ptr = NULL;
00088 return false;
00089 }
00090 else
00091 {
00092 AllocList* data = (AllocList*)(newptr);
00093 data->size = size;
00094 data->refcount = 1;
00095 data->destruct = justmemory;
00096 #ifndef NDEBUG
00097
00098 dll_push_back(AllocList,allocated, memoryList, data);
00099 data->szFileLine = szFileLine;
00100
00101 data->check = data->check2 = HEAD_CHECK;
00102
00103 *DETAIL_TAILPTR(data) = TAIL_CHECK;
00104 #endif
00105 *ptr = ALLOC_OFFSET(newptr);
00106 }
00107 return true;
00108 }
00109
00110 bool _ctlcalloc( void** ptr, size_t size ppDebugParam(const char* szFileLine) )
00111 {
00112 *ptr = NULL;
00113 if( _ctlmalloc( ptr, size ppDebugParam(szFileLine) ) )
00114 {
00115 memset( ptr, 0, size );
00116 return true;
00117 }
00118 return false;
00119 }
00120
00121 bool _ctlrealloc( void** ptr, size_t size ppDebugParam(const char* szFileLine) )
00122 {
00123 void* newptr;
00124 if( NULL == *ptr )
00125 {
00126 _ctlmalloc( ptr, size ppDebugParam(szFileLine) );
00127 return NULL != *ptr;
00128 }
00129 else
00130 {
00131 size_t needsize = ALLOC_EXCESS(size);
00132 AllocList* data = DETAIL_OFFSET(*ptr);
00133 assert( _ctlvalidate( *ptr, szFileLine ) );
00134
00135 ppDebug(dll_erase( AllocList,allocated, memoryList, data);)
00136
00137 newptr = realloc( data, needsize );
00138 if( NULL == newptr )
00139 {
00140 assertcodeptr(curr_newhandler);
00141 curr_newhandler( ppDebRel(szFileLine,"_ctlrealloc"), size );
00142 newptr = realloc( data, needsize );
00143 if( NULL == newptr )
00144 {
00145 curr_newhandler( ppDebRel(szFileLine,"_ctlrealloc"), size );
00146
00147 ppDebug(dll_push_back(AllocList,allocated, memoryList, data);)
00148 return false;
00149 }
00150 }
00151 }
00152 {
00153 AllocList* data = (AllocList*)newptr;
00154 *ptr = ALLOC_OFFSET(newptr);
00155 data->size = size;
00156
00157
00158
00159
00160 #ifndef NDEBUG
00161
00162 dll_push_back(AllocList,allocated, memoryList, data);
00163
00164 *DETAIL_TAILPTR(data) = TAIL_CHECK;
00165
00166
00167
00168
00169
00170
00171 #endif
00172 }
00173 return true;
00174 }
00175
00176
00177 void _ctlfree( void** ptr ppDebugParam(const char* szFileLine) )
00178 {
00179 if( *ptr )
00180 {
00181 AllocList* data = DETAIL_OFFSET(*ptr);
00182 assert( _ctlvalidate( *ptr, szFileLine ) );
00183 assert( data->refcount <= 1 );
00184
00185
00186 #ifndef NDEBUG
00187 dll_erase( AllocList,allocated, memoryList, data);
00188 #endif
00189 assertcodeptr(data->destruct);
00190 data->destruct(*ptr);
00191 free(DETAIL_OFFSET(*ptr));
00192 *ptr = NULL;
00193 }
00194 }
00195
00196 size_t _ctlsize( void* ptr ppDebugParam(const char* szFileLine) )
00197 {
00198 if( NULL != ptr )
00199 {
00200 assert( _ctlvalidate( ptr, szFileLine ) );
00201 return DETAIL_OFFSET(ptr)->size;
00202 }
00203 return 0;
00204 }
00205
00206 bool _ctlstrdup( char** ptr, const char* sz ppDebugParam(const char* szFileLine) )
00207 {
00208 if( _ctlmalloc( (void**)ptr, strlen(sz) + sizeof(char) ppDebugParam(szFileLine) ) )
00209 {
00210 strcpy( *ptr, sz );
00211 return true;
00212 }
00213 return false;
00214 }
00215
00216 bool _ctlwcsdup( wchar_t** ptr, const wchar_t*sz ppDebugParam(const char* szFileLine) )
00217 {
00218 if( _ctlmalloc( (void**)ptr, wcslen(sz) + sizeof(wchar_t) ppDebugParam(szFileLine) ) )
00219 {
00220 wcscpy( *ptr, sz );
00221 return true;
00222 }
00223 return false;
00224 }
00225
00226
00227
00228
00229 bool _ctlnew( void** ptr, size_t size, void (*destruct)(void*) ppDebugParam(const char* szFileLine) )
00230 {
00231 assertptr(ptr,sizeof(void*));
00232 if( _ctlmalloc( ptr, size ppDebugParam(szFileLine) ) )
00233 {
00234 AllocList* data = DETAIL_OFFSET(*ptr);
00235 data->destruct = destruct;
00236 return true;
00237 }
00238 return false;
00239 }
00240
00241
00242
00243 int _ctldelete( void** ptr ppDebugParam(const char* szFileLine) )
00244 {
00245 if( NULL != *ptr )
00246 {
00247 AllocList* data = DETAIL_OFFSET(*ptr);
00248 assert( _ctlvalidate( *ptr, szFileLine ) );
00249 if( 0 == --data->refcount )
00250 {
00251 _ctlfree( ptr ppDebugParam(szFileLine) );
00252 }
00253 else
00254 {
00255 *ptr = NULL;
00256 }
00257 return data->refcount;
00258 }
00259 return 0;
00260 }
00261
00262
00263
00264
00265 int _ctlref( void* ptr ppDebugParam(const char* szFileLine))
00266 {
00267 if( NULL != ptr )
00268 {
00269 AllocList* data = DETAIL_OFFSET(ptr);
00270 assert( _ctlvalidate( ptr, szFileLine ) );
00271 return ++data->refcount;
00272 }
00273 return 0;
00274 }
00275
00276
00277
00278
00279
00280
00281 int _ctlrefcount( void* ptr ppDebugParam(const char* szFileLine) )
00282 {
00283 if( NULL != ptr )
00284 {
00285 AllocList* data = DETAIL_OFFSET(ptr);
00286 assert( _ctlvalidate( ptr, szFileLine ) );
00287 return data->refcount;
00288 }
00289 return 0;
00290 }
00291
00292 #ifndef NDEBUG
00293 bool _ctlvalidate( void* ptr, const char* szFileLine )
00294 {
00295 if( (size_t)ptr > 0x10000 && (0 == ((size_t)(ptr) & 3)) )
00296 {
00297 AllocList* data = DETAIL_OFFSET(ptr);
00298 uint32* tailptr;
00299 if( data->check != HEAD_CHECK || data->check2 != HEAD_CHECK )
00300 {
00301
00302 trace(("Memory pointer %p failed header validation\n", ptr ));
00303 trace(("Doing %.128s\n", szFileLine));
00304 return false;
00305 }
00306
00307 tailptr = DETAIL_TAILPTR(data);
00308 if( data->refcount >= 0 && data->refcount < MEM_ABSURDREFS && *tailptr == TAIL_CHECK )
00309 return true;
00310 assertcodeptr(data->destruct);
00311 trace(("Memory pointer %p failed validation\n", ptr));
00312 trace(("Doing %.128s\n", szFileLine));
00313 trace(("Came from %.128s\n", data->szFileLine));
00314 trace(("Tail: %08x == %08x\n", *tailptr,TAIL_CHECK));
00315 return false;
00316 }
00317 trace(("Bad pointer %p\n", ptr));
00318 return false;
00319 }
00325 size_t ctl_memory_dump( uint32 logmask )
00326 {
00327 size_t count = 0;
00328 dll_foreach( AllocList,allocated, memoryList, curr )
00329 {
00330 count++;
00331 if( curr->check == HEAD_CHECK && curr->check2 == HEAD_CHECK )
00332 {
00333 LOG( logmask, "%s: %u\n", curr->szFileLine, curr->size );
00334 _ctlvalidate( ALLOC_OFFSET(curr), "ctl_memory_dump" );
00335 }
00336 else
00337 {
00338
00339 LOG( LOG_ERROR, "Bad memory node link\n" );
00340 break;
00341 }
00342 }
00343 return count;
00344 }
00345 #endif
00346
00347 struct ctl_pool_base_block;
00348 struct ctl_pool_base_node;
00349 struct ctl_pool_base;
00353 struct ctl_pool_base_node
00354 {
00355 union
00356 {
00357 struct ctl_pool_base_node* free;
00358 ctl_pool_base* pool;
00359 } specialization;
00360
00361
00362
00363 };
00371 struct ctl_pool_base_block
00372 {
00373 struct ctl_pool_base_block* next;
00374
00375 };
00376
00377
00378 ctl_pool_base* ctl_pool_base_list = NULL;
00379
00380 #ifndef NDEBUG
00381
00384 size_t ctl_pool_dump( uint32 logmask )
00385 {
00386 size_t total = 0;
00387 sll_foreach_const(ctl_pool_base,poolList, ctl_pool_base_list, pool )
00388 {
00389 LOG( logmask, "%s: U:%u F:%u A:%u\n", pool->name, pool->unit, pool->cFree, pool->cAlloc );
00390 total++;
00391 }
00392 return total;
00393 }
00394
00398 void ctl_destroy_all_pools( void )
00399 {
00400 while( NULL != ctl_pool_base_list )
00401 {
00402 ctl_pool_base* pool;
00403 sll_pop_front(ctl_pool_base,poolList, ctl_pool_base_list, pool );
00404 if( 0 != pool->cAlloc )
00405 LOG( LOG_ERROR, "Active Allocations in pool %s: U:%u F:%u A:%u\n", pool->name, pool->unit, pool->cFree, pool->cAlloc );
00406 ctl_pool_base_destroy( pool );
00407 }
00408 }
00409 #endif
00410
00417 void ctl_pool_base_init( ctl_pool_base* pool, size_t unitSize, size_t delta, const char* name )
00418 {
00419 assertobjptr(pool);
00420 assertconst(name,1);
00421
00422
00423 pool->freelist = NULL;
00424 pool->blocks = NULL;
00425 pool->poolList = NULL;
00426 pool->name = name;
00427
00428 pool->unit = unitSize;
00429 pool->delta = delta;
00430 pool->cFree = pool->cAlloc = 0;
00431 }
00432
00438 void ctl_pool_base_grow( ctl_pool_base* pool, size_t delta )
00439 {
00440
00441 struct ctl_pool_base_block* block;
00442 size_t unitSize = roundup(pool->unit, sizeof( struct ctl_pool_base_node )) + sizeof( struct ctl_pool_base_node );
00443 size_t size = sizeof(struct ctl_pool_base_block) + (unitSize * delta);
00444 ctl_malloc( block, size );
00445
00446 if( !pool->blocks )
00447 sll_push_front(ctl_pool_base,poolList, ctl_pool_base_list, pool );
00448 sll_push_front(struct ctl_pool_base_block,next, pool->blocks, block );
00449 {
00450 uint8* pCurr = (uint8*)block+size;
00451 size_t curr = delta;
00452 while( curr-- )
00453 {
00454 struct ctl_pool_base_node* pfree;
00455 pCurr -= unitSize;
00456 pfree = (struct ctl_pool_base_node*)pCurr;
00457 sll_push_front(struct ctl_pool_base_node,specialization.free, pool->freelist, pfree );
00458 }
00459 pool->cFree += delta;
00460 }
00461 }
00462
00467 void ctl_pool_base_destroy( ctl_pool_base* pool )
00468 {
00469
00470 struct ctl_pool_base_block* curr = pool->blocks;
00471 pool->blocks = NULL;
00472 while( curr )
00473 {
00474 struct ctl_pool_base_block* next = curr->next;
00475 ctl_free( curr );
00476 curr = next;
00477 }
00478
00479 sll_erase(ctl_pool_base,poolList, ctl_pool_base_list, pool );
00480 memset(pool,0,sizeof(*pool));
00481 }
00482
00488 void* ctl_pool_base_alloc( ctl_pool_base* pool )
00489 {
00490 struct ctl_pool_base_node* pfree = pool->freelist;
00491 if( NULL == pfree )
00492 {
00493 ctl_pool_base_grow( pool, pool->delta );
00494 pfree = pool->freelist;
00495 }
00496 pool->cFree--;
00497 pool->cAlloc++;
00498 sll_erase(struct ctl_pool_base_node,specialization.free, pool->freelist, pfree );
00499 ppDebug(pfree->specialization.pool = pool);
00500 return pfree+1;
00501 }
00502
00508 void ctl_pool_base_free( ctl_pool_base* pool, void* ptr )
00509 {
00510 struct ctl_pool_base_node* pfree = (struct ctl_pool_base_node*)ptr-1;
00511 assertobjptr(pool);
00512 assertptr(ptr,4);
00513
00514
00515 assert( pool->cAlloc > 0 );
00516
00517
00518 assert( pfree->specialization.pool == pool );
00519
00520
00521 pool->cFree++;
00522 pool->cAlloc--;
00523 sll_push_front(struct ctl_pool_base_node,specialization.free, pool->freelist, pfree );
00524 }
00525
00527 #define ctl_xenum(def) ppConcat(ctl_xenum_,def)
00528 #define ctl_xenum_space( size, delta ) ppConcat(ctl_mlspace_,size)
00529 #define ctl_xenum_object( type, delta ) ppConcat(ctl_mlspace_,type)
00530 #define ctl_xenum_label( label, type, delta ) ppConcat(ctl_mlspace_,label)
00531 #define ctl_xenum_term( type, delta ) ctl_mlspace_count
00532
00534 #define ctl_xtab_size(def) ppConcat(ctl_xtab_size_,def)
00535 #define ctl_xtab_size_space( size, delta ) ctl_pool_base_entry( size, delta, "raw_" size ),
00536 #define ctl_xtab_size_object( type, delta )
00537 #define ctl_xtab_size_label( label, type, delta )
00538 #define ctl_xtab_size_term( type, delta ) {0}
00539
00541 #define ctl_xttab_type(def) ppConcat(ctl_xttab_type_,def)
00542 #define ctl_xttab_type_space( size, delta ) {0},
00543 #define ctl_xttab_type_object( type, delta ) ctl_pool_base_entry( size, delta, type ),
00544 #define ctl_xttab_type_label( label, type, delta ) ctl_pool_base_entry( sizeof(type), delta, label ),
00545 #define ctl_xttab_type_term( type, delta ) {0},
00546
00548 #define ctl_xtab_type_space( size, delta ) void
00549 #define ctl_xtab_type_object( type, delta ) type
00550 #define ctl_xtab_type_label( label, type, delta ) type
00551 #define ctl_xtab_type_term( type, delta ) void
00552
00554 #define CTL_META_LIST(def)\
00555 def( space(16, 64) )\
00556 def( space(32, 32) )\
00557 def( space(64, 16) )\
00558 def( space(96, 16) )\
00559 def( space(128, 16) )\
00560 def( space(192, 16) )\
00561 def( space(256, 8) )\
00562 def( space(384, 8) )\
00563 def( space(512, 8) )\
00564 def( space(640, 8) )\
00565 def( space(768, 4) )\
00566 def( space(1024, 4) )\
00567 def( space(1536, 4) )\
00568 def( space(2048, 4) )\
00569 def( space(3072, 4) )\
00570 def( space(4096, 4) )\
00571 def( space(6144, 3) )\
00572 def( space(8192, 2) )\
00573 def( object(type, 256) )\
00574 def( object(type, 256) )\
00575
00576
00577 #define ctl_mp_alloc_type(type)
00578 #define ctl_mp_free_type(type,ptr)
00579 #define ctl_mp_alloc_size(size)
00580 #define ctl_mp_realloc_size(size,ptr)
00581 #define ctl_mp_free_size(size,ptr)
00582 #define ctl_mp_free(ptr)
00583 #define ctl_mp_initialize()
00584 #define ctl_mp_make_report()
00585 #define ctl_mp_init_report()
00586
00587
00588 #else
00589
00590 #include "unit/unit.h"
00591 #include "pool.h"
00592
00593 #define TEST_SIZE 3
00594 #define TEST_ARRAY_SIZE 32
00595
00596
00597 typedef struct Test
00598 {
00599 int value;
00600 const char* sz;
00601 } Test;
00602 ctl_fp_decl(Test,TEST_SIZE, TestPool );
00603
00604
00611 void Test_Malloc(void)
00612 {
00613
00614 {
00615 char* szDup = NULL;
00616 ctl_strdup(szDup,"CloneMe");
00617 Test_Error( !strcmp(szDup,"CloneMe") );
00618 Test_Error( ctl_size(szDup) == sizeof("CloneMe") );
00619 ctl_free(szDup);
00620 Test_Error( 0 == ctl_size(szDup) );
00621 Test_Error( NULL == szDup );
00622 }
00623
00624 {
00625 TestPool scratchPool;
00626 Test* ptr;
00627 Test* ptr2;
00628 Test* ptr3;
00629 Test* ptr4;
00630 ctl_fp_init(TestPool, &scratchPool );
00631 ctl_fp_alloc(TestPool, &scratchPool, ptr );
00632 ctl_fp_alloc(TestPool, &scratchPool, ptr2 );
00633 ctl_fp_alloc(TestPool, &scratchPool, ptr3 );
00634 ctl_fp_alloc(TestPool, &scratchPool, ptr4 );
00635 Test_Error( NULL != ptr );
00636 Test_Error( NULL != ptr2 );
00637 Test_Error( NULL != ptr3 );
00638 Test_Error( NULL == ptr4 );
00639 ptr->value = 1;
00640 ptr->sz = "Spork";
00641 ptr2->value = 2;
00642 ptr2->sz = "Foon";
00643 ptr3->value = TEST_SIZE;
00644 ptr3->sz = "spam";
00645 Test_Error( false == ctl_fp_valid(TestPool, &scratchPool, "junk" ) );
00646 ctl_fp_free(TestPool, &scratchPool, ptr );
00647 ctl_fp_free(TestPool, &scratchPool, ptr2 );
00648 ctl_fp_free(TestPool, &scratchPool, ptr3 );
00649 }
00650
00651 {
00652 ctl_pool_auto(TestPool,scratch,TEST_SIZE);
00653 ctl_pool_destroy(TestPool,&scratch);
00654 }
00655 {
00656
00657 ctl_pool_auto(Test,scratch,TEST_SIZE);
00658 Test* ptr;
00659 Test* ptr2;
00660 Test* ptr3;
00661 Test* ptr4;
00662 ctl_pool_alloc(Test,&scratch,ptr);
00663 ctl_pool_alloc(Test,&scratch,ptr2);
00664 ctl_pool_alloc(Test,&scratch,ptr3);
00665 Test_Error( scratch.cFree == 0 );
00666 ctl_pool_alloc(Test,&scratch,ptr4);
00667 Test_Error( scratch.cFree == 2 );
00668 Test_Error( NULL != ptr );
00669 Test_Error( NULL != ptr2 );
00670 Test_Error( NULL != ptr3 );
00671 Test_Error( NULL != ptr4 );
00672 Test_Error( scratch.cAlloc == 4 );
00673
00674 ptr->value = 1;
00675 ptr->sz = "Spork";
00676 ptr2->value = 2;
00677 ptr2->sz = "Foon";
00678 ptr3->value = TEST_SIZE;
00679 ptr3->sz = "spam";
00680 ptr4->value = TEST_SIZE;
00681 ptr4->sz = "Egg";
00682
00683 ctl_pool_free(Test,&scratch,ptr);
00684 ctl_pool_free(Test,&scratch,ptr2);
00685 ctl_pool_free(Test,&scratch,ptr3);
00686 ctl_pool_free(Test,&scratch,ptr4);
00687 ctl_pool_destroy(Test,&scratch);
00688 }
00689 }
00690
00691 #endif