/* da.cpp -- simple debugging allocator implementation
*
* NOTES
*
* 1. This code using 8-byte alignment, and may not be right for some
* architectures.
*
* 2. There needs to be a mutex on da_serial to be thread safe, but it doesn't
* matter very much.
*
*
* LICENSE
*
* Copyright (C) 2000 Ravenbrook Limited .
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by
* Ravenbrook Limited .
* 4. Neither the name of Ravenbrook Limited nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY RAVENBROOK LIMITED AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL RAVENBROOK LIMITED OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. */
#include
#include
#include
#include
#include "da.h"
#define ALIGNMENT 8u
#define ALIGN_UP(value, alignment) \
(((value) + (alignment) - 1) & ~((alignment) - 1))
#define IS_ALIGNED(value, alignment) \
(((value) & ((alignment) - 1)) == 0)
#define MALLOC_FILL 'M'
#define FREE_FILL 'F'
#define DEAD_BRAND 0xDEADBEEF
typedef struct leader_s *leader_t;
#define LEADER_BRAND 0xBD8EADE4
typedef struct leader_s {
unsigned long brand;
size_t size;
} leader_s;
#define LEADER_SIZE ALIGN_UP(sizeof(leader_s), ALIGNMENT)
typedef struct trailer_s *trailer_t;
#define TRAILER_BRAND 0xBD24A11E
typedef struct trailer_s {
unsigned long serial;
unsigned long brand;
} trailer_s;
#define TRAILER_SIZE ALIGN_UP(sizeof(trailer_s), ALIGNMENT)
static unsigned long da_serial = 0;
static int da_block_check(void *p)
{
leader_t leader;
size_t size;
size_t aligned_size;
trailer_t trailer;
assert(p != NULL);
assert(IS_ALIGNED((unsigned long)p, ALIGNMENT));
leader = (leader_t)((char *)p - LEADER_SIZE);
assert((void *)leader < p);
assert(leader->brand == LEADER_BRAND);
size = leader->size;
assert(size > 0);
aligned_size = ALIGN_UP(size, ALIGNMENT);
trailer = (trailer_t)((char *)p + aligned_size);
assert(p < (void *)trailer);
assert(trailer->brand == TRAILER_BRAND);
assert(trailer->serial < da_serial);
return 1;
}
static size_t da_block_info(void *p, leader_t *leader_o, trailer_t *trailer_o)
{
leader_t leader;
trailer_t trailer;
size_t aligned_size;
assert(da_block_check(p));
leader = (leader_t)((char *)p - LEADER_SIZE);
aligned_size = ALIGN_UP(leader->size, ALIGNMENT);
trailer = (trailer_t)((char *)p + aligned_size);
if(leader_o != NULL)
*leader_o = leader;
if(trailer_o != NULL)
*trailer_o = trailer;
return leader->size;
}
static void *da_malloc_inner(size_t size, const char *file, unsigned long line)
{
size_t aligned_size;
size_t block_size;
void *block;
leader_t leader;
trailer_t trailer;
void *p;
assert(size >= 0); /* some systems have signed sizes */
assert(file != NULL);
assert(line > 0);
if(size == 0) /* allocate a dummy byte for empty mallocs */
size = 1;
aligned_size = ALIGN_UP(size, ALIGNMENT);
block_size = LEADER_SIZE + aligned_size + TRAILER_SIZE;
block = (malloc)(block_size);
if(block == NULL)
return NULL;
assert(IS_ALIGNED((unsigned long)block, ALIGNMENT));
leader = (leader_t)block;
p = (void *)((char *)block + LEADER_SIZE);
trailer = (trailer_t)((char *)p + aligned_size);
assert((char *)trailer + TRAILER_SIZE == (char *)block + block_size);
leader->brand = LEADER_BRAND;
leader->size = size;
trailer->serial = da_serial++;
trailer->brand = TRAILER_BRAND;
memset(p, MALLOC_FILL, aligned_size);
assert(da_block_check(p));
return p;
}
extern void *da_malloc(size_t size, const char *file, unsigned long line)
{
void *p;
assert(file != NULL);
assert(line > 0);
p = da_malloc_inner(size, file, line);
if(p == NULL) {
fprintf(stderr,
"malloc of %lu bytes in file \"%s\" line %lu failed",
(unsigned long)size,
file,
line);
return NULL;
}
return p;
}
extern void *da_realloc(void *p, size_t size, const char *file, unsigned long line)
{
void *new_mem;
assert(file != NULL);
assert(line > 0);
assert(p == NULL || da_block_check(p));
new_mem = da_malloc_inner(size, file, line);
if(new_mem == NULL) {
fprintf(stderr,
"realloc of 0x%lX to %lu bytes in file \"%s\" line %lu failed",
(unsigned long)p,
(unsigned long)size,
file,
line);
return NULL;
}
if(p != NULL) {
size_t old_size = da_block_info(p, NULL, NULL);
memcpy(new_mem, p, size < old_size ? size : old_size);
da_free(p, file, line);
}
assert(da_block_check(new_mem));
return new_mem;
}
extern void da_free(void *p, const char *file, unsigned long line)
{
leader_t leader;
trailer_t trailer;
(void)file; /* unused parameter */
(void)line; /* unused parameter */
/* There's an implicit block check in da_block_info. */
(void)da_block_info(p, &leader, &trailer);
memset(p, FREE_FILL, leader->size);
/* Prevent double frees by making the brands illegal, but also record */
/* when the free ocurred by XORing in the serial number. */
leader->brand = DEAD_BRAND ^ da_serial;
trailer->brand = DEAD_BRAND ^ da_serial;
++da_serial;
(free)(leader);
}
extern void *da_calloc(size_t n, size_t size, const char *file, unsigned long line)
{
size_t total;
void *p;
total = n * size;
assert(total >= n); /* overflow check */
assert(total >= size); /* overflow check */
p = da_malloc_inner(size, file, line);
if(p == NULL) {
fprintf(stderr,
"calloc of %lu objects of %lu bytes in file \"%s\" line %lu failed",
(unsigned long)n,
(unsigned long)size,
file,
line);
return NULL;
}
memset(p, 0, size);
return p;
}
extern char *da_strdup(const char *p, const char *file, unsigned long line)
{
size_t size;
char *new_mem;
assert(p != NULL);
size = strlen(p) + 1;
assert(size > 0);
new_mem = (char *)da_malloc_inner(size, file, line);
if (new_mem == NULL) {
fprintf(stderr,
"strdup of 0x%lX (\"%s\") in file \"%s\" line %lu failed",
(unsigned long)p,
p,
file,
line);
return NULL;
}
memcpy(new_mem, p, size);
return new_mem;
}
/* This version of the delete operator has a signature that matches
operator new so that Microsoft Visual C++ will call it in the event
of an exception being thrown in a constructor. In other
circumstances the usual delete operator will be called. I don't
believe that this is part of the C++ standard (there's no mention of
such a feature in Stroustrup 1997), but it may be necessary in MSVC++
and it is harmless otherwise. I don't know what arguments MSVC++
will pass for the file and line arguments (hopefully the same ones as
for the corresponding operator new). */
extern void operator delete(void *p, const char *file, unsigned long line)
{
da_free(p, file, line);
}
extern void operator delete(void *p)
{
/* As far as I know there's no way to get the real file and line
here. Sorry. */
da_free(p, __FILE__, __LINE__);
}
/* Avoid the operator definition below being stymied by the redefinition
of new in da.h. I would have liked to write (new) to avoid the
macro, but operator() is special in C++. */
#undef new
extern void *operator new(size_t size, const char *file, unsigned long line)
{
return da_malloc(size, file, line);
}
/* end of da.cpp */