696 lines
18 KiB
C++
696 lines
18 KiB
C++
// This may look like C code, but it is really -*- C++ -*-
|
|
|
|
// ------------------------------------------------------------------
|
|
// The Goldware Library
|
|
// Copyright (C) 1990-1999 Odinn Sorensen
|
|
// Copyright (C) 2000 Alexander S. Aganichev
|
|
// ------------------------------------------------------------------
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Library General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Library General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Library General Public
|
|
// License along with this program; if not, write to the Free
|
|
// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
// MA 02111-1307, USA
|
|
// ------------------------------------------------------------------
|
|
// $Id$
|
|
// ------------------------------------------------------------------
|
|
// Memory management routines with debugging features.
|
|
// Based on free code from SNIPPETS 9404 by Walter Bright.
|
|
// ------------------------------------------------------------------
|
|
|
|
#include <climits>
|
|
#include <gdbgerr.h>
|
|
#include <gmemdbg.h>
|
|
#include <glog.h>
|
|
#include <gmemall.h>
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Global vars
|
|
|
|
int throw_alloc_extra = 0;
|
|
|
|
#if defined(GTHROW_LOG)
|
|
glog* throw_log = NULL;
|
|
#define TLOG if(throw_log) throw_log
|
|
#endif
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
#define gmem_strdup strdup
|
|
#define gmem_malloc malloc
|
|
#define gmem_calloc calloc
|
|
#define gmem_realloc realloc
|
|
#define gmem_free free
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
#if defined(GTHROW_DEBUG)
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Various values
|
|
|
|
const dword BEFOREVAL = 0x12345678L;
|
|
const dword AFTERVAL = 0x87654321L;
|
|
|
|
const byte BADVAL = 0xFF;
|
|
const byte MALLOCVAL = 0xEE;
|
|
|
|
const size_t BAD_SIZE = 0xFFFFFFFF;
|
|
const size_t MALLOC_SIZE = 0xEEEEEEEE;
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Struct
|
|
|
|
#if defined(GOLD_CANPACK)
|
|
#pragma pack(1)
|
|
#endif
|
|
|
|
struct Throw {
|
|
Throw* next;
|
|
Throw* prev;
|
|
const char* file;
|
|
int line;
|
|
int index;
|
|
size_t nbytes;
|
|
dword beforeval;
|
|
char data[1];
|
|
};
|
|
|
|
#if defined(GOLD_CANPACK)
|
|
#pragma pack()
|
|
#endif
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
struct ThrowIndex {
|
|
Throw* pointer;
|
|
};
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Internal vars
|
|
|
|
int throw_inited = 0;
|
|
int throw_count;
|
|
uint32_t throw_allocations = 0;
|
|
Throw throw_alloclist;
|
|
int throw_max_count = 0;
|
|
Throw** throw_index = NULL;
|
|
int throw_index_size = 0;
|
|
int throw_index_free = 0;
|
|
int throw_last_free = -1;
|
|
uint32_t throw_index_cache_hits = 0;
|
|
int throw_overhead = sizeof(Throw) - 1;
|
|
|
|
#define throw_index_init_size 1000
|
|
#define throw_index_increment 100
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Inlined functions
|
|
|
|
inline Throw* throw_ptrtodl(const void* ptr) { return (Throw*)((const char*)ptr-sizeof(Throw)+1); }
|
|
inline void* throw_dltoptr(Throw* dl) { return (void*)dl->data; }
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
int throw_index_find_free() {
|
|
|
|
if(throw_last_free != -1) {
|
|
throw_index_cache_hits++;
|
|
return throw_last_free;
|
|
}
|
|
else if(throw_index_free) {
|
|
Throw** i = throw_index;
|
|
for(int n=0; n<throw_index_size; n++,i++) {
|
|
if(*i == NULL) {
|
|
throw_last_free = n;
|
|
return throw_last_free;
|
|
}
|
|
}
|
|
}
|
|
if(throw_index_size) {
|
|
throw_index = (Throw**)realloc(throw_index, (throw_index_size+throw_index_increment)*sizeof(Throw*));
|
|
throw_new(throw_index);
|
|
memset(throw_index+throw_index_size, 0, throw_index_increment*sizeof(Throw*));
|
|
throw_last_free = throw_index_size;
|
|
throw_index_size += throw_index_increment;
|
|
throw_index_free = throw_index_increment;
|
|
}
|
|
else {
|
|
throw_index = (Throw**)calloc(throw_index_init_size, sizeof(Throw*));
|
|
throw_new(throw_index);
|
|
throw_index_size = throw_index_init_size;
|
|
throw_index_free = throw_index_init_size;
|
|
throw_last_free = 0;
|
|
}
|
|
return throw_last_free;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
int throw_index_add(Throw* pointer) {
|
|
|
|
int index = throw_index_find_free();
|
|
throw_index[index] = pointer;
|
|
throw_last_free = -1;
|
|
throw_index_free--;
|
|
return index;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
void throw_index_remove(int index) {
|
|
|
|
throw_index[index] = NULL;
|
|
throw_last_free = index;
|
|
throw_index_free++;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
Throw* throw_find_overrun(Throw* pointer) {
|
|
|
|
Throw* last_candidate = NULL;
|
|
long last_diff = LONG_MAX;
|
|
Throw** i = throw_index;
|
|
for(int n=0; n<throw_index_size; n++,i++) {
|
|
long diff = (unsigned long)*i - (unsigned long)pointer;
|
|
if((diff > 0) and (diff < last_diff)) {
|
|
last_candidate = *i;
|
|
last_diff = diff;
|
|
}
|
|
}
|
|
return last_candidate;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
Throw* throw_find_underrun(Throw* pointer) {
|
|
|
|
Throw* last_candidate = NULL;
|
|
long last_diff = LONG_MAX;
|
|
Throw** i = throw_index;
|
|
for(int n=0; n<throw_index_size; n++,i++) {
|
|
long diff = (unsigned long)pointer - (unsigned long)*i;
|
|
if((diff > 0) and (diff < last_diff)) {
|
|
last_candidate = *i;
|
|
last_diff = diff;
|
|
}
|
|
}
|
|
return last_candidate;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Dump pointer information to the log
|
|
|
|
#if defined(GTHROW_LOG)
|
|
|
|
void throw_printdl(Throw* dl) {
|
|
|
|
char buf[100];
|
|
|
|
char* ptr = (char*)throw_dltoptr(dl);
|
|
TLOG->printf(": Ptr (%p,%u) at [%s,%u].", ptr, (uint)dl->nbytes, CleanFilename(dl->file), (uint)dl->line);
|
|
TLOG->printf(": %s", HexDump16(buf, ptr, dl->nbytes, HEX_DUMP2));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
inline void throw_printdl(Throw* dl) {}
|
|
|
|
#endif
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Debugging new
|
|
|
|
void throw_new_debug(const void* __ptr, const char* __file, int __line) {
|
|
|
|
if(__ptr == NULL) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errmemory(__file, __line);
|
|
TLOG->printf("! A memory allocation failed (out of memory).");
|
|
TLOG->printf("+ Advice: Restart.");
|
|
#endif
|
|
MemoryErrorExit();
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Debugging strdup()
|
|
|
|
char* throw_strdup_debug(const char* __str, const char* __file, int __line) {
|
|
|
|
char* _ptr = (char*)(__str ? throw_calloc_debug(1, strlen(__str)+1, __file, __line) : NULL);
|
|
return _ptr ? strcpy(_ptr, __str) : _ptr;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Debugging malloc()
|
|
|
|
void* throw_malloc_debug(size_t __size, const char* __file, int __line) {
|
|
|
|
void* _ptr = throw_calloc_debug(1, __size, __file, __line);
|
|
if(_ptr)
|
|
memset(_ptr, MALLOCVAL, __size);
|
|
return _ptr;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Debugging calloc()
|
|
|
|
void* throw_calloc_debug(size_t __items, size_t __size, const char* __file, int __line) {
|
|
|
|
__size *= __items;
|
|
__size += throw_alloc_extra;
|
|
|
|
if(__size == 0) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errmemory(__file, __line);
|
|
TLOG->printf("! Attempted to allocate zero bytes of memory.");
|
|
TLOG->printf("+ Advice: This is a bug. Please report to the Author.");
|
|
#endif
|
|
MemoryErrorExit();
|
|
}
|
|
|
|
size_t _siz = sizeof(Throw) + __size + sizeof(AFTERVAL) - 1;
|
|
|
|
Throw* dl = (Throw*)calloc(_siz, 1);
|
|
if(dl == NULL) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errmemory(__file, __line);
|
|
TLOG->printf("! A memory allocation failed (out of memory).");
|
|
TLOG->printf(": Needed %u (%Xh) bytes.", (uint)__size, (uint)__size);
|
|
if(__size == BAD_SIZE) {
|
|
TLOG->printf("+ Info: Value could be from free'd data.");
|
|
TLOG->printf("+ Info: This indicates a serious bug.");
|
|
TLOG->printf("+ Advice: Report to the Author immediately.");
|
|
}
|
|
else if(__size == MALLOC_SIZE) {
|
|
TLOG->printf("+ Info: Value could be from uninitialized data");
|
|
TLOG->printf("+ Info: This indicates a serious bug.");
|
|
TLOG->printf("+ Advice: Report to the Author immediately.");
|
|
}
|
|
else {
|
|
TLOG->printf("+ Advice: Restart.");
|
|
}
|
|
#endif
|
|
MemoryErrorExit();
|
|
}
|
|
|
|
dl->index = throw_index_add(dl);
|
|
|
|
dl->file = __file;
|
|
dl->line = __line;
|
|
dl->nbytes = __size;
|
|
dl->beforeval = BEFOREVAL;
|
|
*(dword*)&(dl->data[__size]) = AFTERVAL;
|
|
|
|
dl->next = throw_alloclist.next;
|
|
dl->prev = &throw_alloclist;
|
|
throw_alloclist.next = dl;
|
|
if(dl->next != NULL)
|
|
dl->next->prev = dl;
|
|
|
|
throw_count++;
|
|
throw_max_count = maximum_of_two(throw_count, throw_max_count);
|
|
throw_allocations++;
|
|
|
|
return throw_dltoptr(dl);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Debugging realloc()
|
|
|
|
void* throw_realloc_debug(void* __oldptr, size_t __size, const char* __file, int __line) {
|
|
|
|
void* _ptr;
|
|
Throw* dl = throw_ptrtodl(__oldptr);
|
|
|
|
if(__size == 0) {
|
|
throw_free_debug(__oldptr,__file,__line);
|
|
_ptr = NULL;
|
|
}
|
|
else if(__oldptr == NULL) {
|
|
_ptr = throw_malloc_debug(__size,__file,__line);
|
|
}
|
|
else {
|
|
_ptr = throw_malloc_debug(__size,__file,__line);
|
|
if(dl->nbytes < __size)
|
|
__size = dl->nbytes;
|
|
memcpy(_ptr,__oldptr,__size);
|
|
throw_free_debug(__oldptr,__file,__line);
|
|
}
|
|
return _ptr;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Debugging free()
|
|
|
|
void throw_free_debug(void* __ptr, const char* __file, int __line) {
|
|
|
|
Throw* dl;
|
|
Throw* overrun_dl = NULL;
|
|
Throw* underrun_dl = NULL;
|
|
|
|
if(__ptr == NULL)
|
|
return;
|
|
int inerr = false;
|
|
if(throw_count <= 0) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errmemory(__file, __line);
|
|
TLOG->printf("! More free's than allocs.");
|
|
TLOG->printf("+ Info: This indicates a potentially serious bug.");
|
|
TLOG->printf("+ Advice: Report to the Author immediately.");
|
|
#endif
|
|
goto err;
|
|
}
|
|
dl = throw_ptrtodl(__ptr);
|
|
if(dl->nbytes == BAD_SIZE) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errpointer(__file, __line);
|
|
TLOG->printf("! A memory allocation was already free'd.");
|
|
#endif
|
|
goto err2;
|
|
}
|
|
if(dl->beforeval != BEFOREVAL) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errpointer(__file, __line);
|
|
TLOG->printf("! An allocated memory region was underrun.");
|
|
#endif
|
|
dl->prev = dl->next = NULL;
|
|
dl->file = NULL;
|
|
underrun_dl = throw_find_underrun(dl);
|
|
goto err2;
|
|
}
|
|
if(*(dword*)&dl->data[dl->nbytes] != AFTERVAL) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errpointer(__file, __line);
|
|
TLOG->printf("! An allocated memory region was overrun.");
|
|
#endif
|
|
overrun_dl = throw_find_overrun(dl);
|
|
inerr = true;
|
|
}
|
|
|
|
if(inerr)
|
|
goto err2;
|
|
|
|
if(dl->prev)
|
|
dl->prev->next = dl->next;
|
|
if(dl->next)
|
|
dl->next->prev = dl->prev;
|
|
|
|
throw_index_remove(dl->index);
|
|
|
|
memset(dl,BADVAL,sizeof(*dl)+dl->nbytes);
|
|
throw_count--;
|
|
|
|
free(dl);
|
|
return;
|
|
|
|
err2:
|
|
throw_printdl(dl);
|
|
if(underrun_dl) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->printf("! Possibly caused by overrun in this allocation:");
|
|
throw_printdl(underrun_dl);
|
|
if(*(dword*)&underrun_dl->data[underrun_dl->nbytes] != AFTERVAL)
|
|
TLOG->printf("! Overrun of previous allocation confirmed.");
|
|
#endif
|
|
}
|
|
if(overrun_dl) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->printf("! Possibly cause - Underrun in this allocation:");
|
|
throw_printdl(overrun_dl);
|
|
if(overrun_dl->beforeval != BEFOREVAL)
|
|
TLOG->printf("! Underrun of previous allocation confirmed.");
|
|
#endif
|
|
}
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->printf(": Detected while freeing the allocation.");
|
|
TLOG->printf("+ Info: This indicates a serious bug.");
|
|
TLOG->printf("+ Advice: Report to the Author immediately.");
|
|
#endif
|
|
err:
|
|
//PointerErrorExit();
|
|
;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Check all allocations
|
|
|
|
void throw_check_debug(const char* __file, int __line) {
|
|
|
|
Throw* dl = throw_alloclist.next;
|
|
while(dl != NULL) {
|
|
throw_checkptr_debug(throw_dltoptr(dl), __file, __line);
|
|
dl = dl->next;
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Check individual allocation
|
|
|
|
void throw_checkptr_debug(const void* __ptr, const char* __file, int __line) {
|
|
|
|
int inerr = false;
|
|
Throw* dl;
|
|
Throw* overrun_dl = NULL;
|
|
Throw* underrun_dl = NULL;
|
|
if(__ptr == NULL) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errpointer(__file, __line);
|
|
TLOG->printf("! Found NULL pointer instead of allocated memory.");
|
|
#endif
|
|
goto err3;
|
|
}
|
|
dl = throw_ptrtodl(__ptr);
|
|
if(dl->beforeval != BEFOREVAL) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errpointer(__file, __line);
|
|
TLOG->printf("! An allocated memory region was underrun.");
|
|
#endif
|
|
dl->prev = dl->next = NULL;
|
|
dl->file = NULL;
|
|
underrun_dl = throw_find_underrun(dl);
|
|
goto err2;
|
|
}
|
|
if(*(dword*)&dl->data[dl->nbytes] != AFTERVAL) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errpointer(__file, __line);
|
|
TLOG->printf("! An allocated memory region was overrun.");
|
|
#endif
|
|
overrun_dl = throw_find_overrun(dl);
|
|
inerr = true;
|
|
}
|
|
if(inerr)
|
|
goto err2;
|
|
return;
|
|
|
|
err2:
|
|
throw_printdl(dl);
|
|
if(underrun_dl) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->printf("! Possibly caused by overrun in this allocation:");
|
|
throw_printdl(underrun_dl);
|
|
if(*(dword*)&underrun_dl->data[underrun_dl->nbytes] != AFTERVAL)
|
|
TLOG->printf("! Overrun of previous allocation confirmed.");
|
|
#endif
|
|
}
|
|
if(overrun_dl) {
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->printf("! Possibly cause - Underrun in this allocation:");
|
|
throw_printdl(overrun_dl);
|
|
if(overrun_dl->beforeval != BEFOREVAL)
|
|
TLOG->printf("! Underrun of previous allocation confirmed.");
|
|
#endif
|
|
}
|
|
err3:
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->printf(": Detected while checking the allocation.");
|
|
TLOG->printf("+ Info: This indicates a serious bug.");
|
|
TLOG->printf("+ Advice: Report to the Author immediately.");
|
|
#endif
|
|
PointerErrorExit();
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Termination function
|
|
|
|
static void throw_term(void) {
|
|
|
|
if(throw_inited) {
|
|
#if defined(GTHROW_LOG)
|
|
if(throw_count)
|
|
TLOG->printf("! Detected %i unfree'd memory allocation%s.", throw_count, throw_count==1?"":"s");
|
|
if(not error_exit) {
|
|
Throw* dl = throw_alloclist.next;
|
|
int count = throw_count;
|
|
for(; dl; ) {
|
|
Throw* dl_next = dl->next;
|
|
throw_printdl(dl);
|
|
throw_free_debug(throw_dltoptr(dl), __FILE__, __LINE__);
|
|
dl = dl_next;
|
|
}
|
|
throw_count = count;
|
|
if(throw_count) {
|
|
TLOG->printf("+ Info: The memory should have been free'd before exit.");
|
|
TLOG->printf("+ Info: This indicates a potentially serious bug.");
|
|
TLOG->printf("+ Advice: Report to the Author immediately.");
|
|
}
|
|
}
|
|
#endif
|
|
free(throw_index);
|
|
throw_inited = 0;
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// Init function
|
|
|
|
void throw_init() {
|
|
|
|
if(throw_inited == 0) {
|
|
throw_count = 0;
|
|
throw_alloclist.next = NULL;
|
|
throw_alloclist.prev = NULL;
|
|
throw_alloclist.file = __FILE__;
|
|
throw_alloclist.line = __LINE__;
|
|
throw_alloclist.nbytes = 0;
|
|
throw_alloclist.beforeval = BEFOREVAL;
|
|
throw_alloclist.data[0] = 0xFF;
|
|
throw_inited++;
|
|
atexit(throw_term);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
#ifdef gmem_strdup
|
|
#undef gmem_strdup
|
|
#define gmem_strdup(a) throw_strdup_debug(a, file, line)
|
|
#undef gmem_malloc
|
|
#define gmem_malloc(a) throw_malloc_debug(a, file, line)
|
|
#undef gmem_calloc
|
|
#define gmem_calloc(a,b) throw_calloc_debug(a, b, file, line)
|
|
#undef gmem_realloc
|
|
#define gmem_realloc(a,b) throw_realloc_debug(a, b, file, line)
|
|
#undef gmem_free
|
|
#define gmem_free(a) throw_free_debug(a, file, line)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
void* throw_outofmem_report(const char* file, int line, uint size) {
|
|
|
|
#if defined(GTHROW_LOG)
|
|
TLOG->errmemory(file, line);
|
|
if(size == 0) {
|
|
TLOG->printf("! Attempted to allocate zero bytes of memory.");
|
|
TLOG->printf("+ Advice: This is a bug. Please report to the Author.");
|
|
}
|
|
else {
|
|
TLOG->printf("! A memory allocation failed (out of memory).");
|
|
TLOG->printf(": Needed %u (%Xh) bytes.", size, size);
|
|
TLOG->printf("+ Advice: Restart.");
|
|
}
|
|
#else
|
|
NW(file);
|
|
NW(line);
|
|
NW(size);
|
|
#endif
|
|
MemoryErrorExit();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
void throw_xnew_debug(void* ptr, const char* file, int line) {
|
|
|
|
if(ptr == NULL)
|
|
throw_outofmem_report(file, line, 0);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
char* throw_xstrdup_debug(const char* str, const char* file, int line) {
|
|
|
|
char* s = gmem_strdup(str);
|
|
return s ? s : (char*)throw_outofmem_report(file, line, strlen(str));
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
void* throw_xmalloc_debug(size_t size, const char* file, int line) {
|
|
|
|
void* p = gmem_malloc(size+throw_alloc_extra);
|
|
return p ? p : throw_outofmem_report(file, line, size);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
void* throw_xcalloc_debug(size_t items, size_t size, const char* file, int line) {
|
|
|
|
void* p = gmem_calloc(items, size+throw_alloc_extra);
|
|
return p ? p : throw_outofmem_report(file, line, items*size);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
void* throw_xrealloc_debug(void* ptr, size_t size, const char* file, int line) {
|
|
|
|
void* p = gmem_realloc(ptr, size+throw_alloc_extra);
|
|
return p ? p : throw_outofmem_report(file, line, size);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
void throw_xfree_debug(void* ptr, const char* file, int line) {
|
|
|
|
if(ptr)
|
|
gmem_free(ptr);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|