blob: dc8fe7cf5ae43df6c445b4c3ccde695d1fc43378 [file] [log] [blame]
/* malloc.c - Memory allocator - Public Domain - 2016 Mattias Jansson / Rampant Pixels
*
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
* The latest source code is always available at
*
* https://github.com/rampantpixels/rpmalloc
*
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
*
*/
#include "rpmalloc.h"
#ifndef ENABLE_VALIDATE_ARGS
//! Enable validation of args to public entry points
#define ENABLE_VALIDATE_ARGS 0
#endif
#if ENABLE_VALIDATE_ARGS
//! Maximum allocation size to avoid integer overflow
#define MAX_ALLOC_SIZE (((size_t)-1) - 4096)
#endif
#ifdef _MSC_VER
#pragma warning (disable : 4100)
#undef malloc
#undef free
#undef calloc
#endif
//This file provides overrides for the standard library malloc style entry points
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
malloc(size_t size);
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
calloc(size_t count, size_t size);
extern void* RPMALLOC_CDECL
realloc(void* ptr, size_t size);
extern void* RPMALLOC_CDECL
reallocf(void* ptr, size_t size);
extern void*
reallocarray(void* ptr, size_t count, size_t size);
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
valloc(size_t size);
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
pvalloc(size_t size);
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
aligned_alloc(size_t alignment, size_t size);
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
memalign(size_t alignment, size_t size);
extern int RPMALLOC_CDECL
posix_memalign(void** memptr, size_t alignment, size_t size);
extern void RPMALLOC_CDECL
free(void* ptr);
extern void RPMALLOC_CDECL
cfree(void* ptr);
extern size_t RPMALLOC_CDECL
malloc_usable_size(void* ptr);
extern size_t RPMALLOC_CDECL
malloc_size(void* ptr);
#ifdef _WIN32
#include <windows.h>
static size_t page_size;
static int is_initialized;
static void
initializer(void) {
if (!is_initialized) {
is_initialized = 1;
SYSTEM_INFO system_info;
memset(&system_info, 0, sizeof(system_info));
GetSystemInfo(&system_info);
page_size = system_info.dwPageSize;
rpmalloc_initialize();
}
rpmalloc_thread_initialize();
}
static void
finalizer(void) {
rpmalloc_thread_finalize();
if (is_initialized) {
is_initialized = 0;
rpmalloc_finalize();
}
}
#else
#include <pthread.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
static size_t page_size;
static pthread_key_t destructor_key;
static int is_initialized;
static void
thread_destructor(void*);
static void __attribute__((constructor))
initializer(void) {
if (!is_initialized) {
is_initialized = 1;
page_size = (size_t)sysconf(_SC_PAGESIZE);
pthread_key_create(&destructor_key, thread_destructor);
if (rpmalloc_initialize())
abort();
}
rpmalloc_thread_initialize();
}
static void __attribute__((destructor))
finalizer(void) {
rpmalloc_thread_finalize();
if (is_initialized) {
is_initialized = 0;
rpmalloc_finalize();
}
}
typedef struct {
void* (*real_start)(void*);
void* real_arg;
} thread_starter_arg;
static void*
thread_starter(void* argptr) {
thread_starter_arg* arg = argptr;
void* (*real_start)(void*) = arg->real_start;
void* real_arg = arg->real_arg;
rpmalloc_thread_initialize();
rpfree(argptr);
pthread_setspecific(destructor_key, (void*)1);
return (*real_start)(real_arg);
}
static void
thread_destructor(void* value) {
(void)sizeof(value);
rpmalloc_thread_finalize();
}
#ifdef __APPLE__
static int
pthread_create_proxy(pthread_t* thread,
const pthread_attr_t* attr,
void* (*start_routine)(void*),
void* arg) {
rpmalloc_thread_initialize();
thread_starter_arg* starter_arg = rpmalloc(sizeof(thread_starter_arg));
starter_arg->real_start = start_routine;
starter_arg->real_arg = arg;
return pthread_create(thread, attr, thread_starter, starter_arg);
}
typedef struct interpose_s {
void* new_func;
void* orig_func;
} interpose_t;
#define MAC_INTERPOSE(newf, oldf) __attribute__((used)) \
static const interpose_t macinterpose##newf##oldf \
__attribute__ ((section("__DATA, __interpose"))) = \
{ (void*)newf, (void*)oldf }
MAC_INTERPOSE(pthread_create_proxy, pthread_create);
#else
#include <dlfcn.h>
int
pthread_create(pthread_t* thread,
const pthread_attr_t* attr,
void* (*start_routine)(void*),
void* arg) {
#if defined(__linux__) || defined(__APPLE__)
char fname[] = "pthread_create";
#else
char fname[] = "_pthread_create";
#endif
void* real_pthread_create = dlsym(RTLD_NEXT, fname);
rpmalloc_thread_initialize();
thread_starter_arg* starter_arg = rpmalloc(sizeof(thread_starter_arg));
starter_arg->real_start = start_routine;
starter_arg->real_arg = arg;
return (*(int (*)(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*))real_pthread_create)(thread, attr, thread_starter, starter_arg);
}
#endif
#endif
RPMALLOC_RESTRICT void* RPMALLOC_CDECL
malloc(size_t size) {
initializer();
return rpmalloc(size);
}
void* RPMALLOC_CDECL
realloc(void* ptr, size_t size) {
initializer();
return rprealloc(ptr, size);
}
void* RPMALLOC_CDECL
reallocf(void* ptr, size_t size) {
initializer();
return rprealloc(ptr, size);
}
void* RPMALLOC_CDECL
reallocarray(void* ptr, size_t count, size_t size) {
size_t total;
#if ENABLE_VALIDATE_ARGS
#ifdef _MSC_VER
int err = SizeTMult(count, size, &total);
if ((err != S_OK) || (total >= MAX_ALLOC_SIZE)) {
errno = EINVAL;
return 0;
}
#else
int err = __builtin_umull_overflow(count, size, &total);
if (err || (total >= MAX_ALLOC_SIZE)) {
errno = EINVAL;
return 0;
}
#endif
#else
total = count * size;
#endif
return realloc(ptr, total);
}
RPMALLOC_RESTRICT void* RPMALLOC_CDECL
calloc(size_t count, size_t size) {
initializer();
return rpcalloc(count, size);
}
RPMALLOC_RESTRICT void* RPMALLOC_CDECL
valloc(size_t size) {
initializer();
if (!size)
size = page_size;
size_t total_size = size + page_size;
#if ENABLE_VALIDATE_ARGS
if (total_size < size) {
errno = EINVAL;
return 0;
}
#endif
void* buffer = rpmalloc(total_size);
if ((uintptr_t)buffer & (page_size - 1))
return (void*)(((uintptr_t)buffer & ~(page_size - 1)) + page_size);
return buffer;
}
RPMALLOC_RESTRICT void* RPMALLOC_CDECL
pvalloc(size_t size) {
size_t aligned_size = size;
if (aligned_size % page_size)
aligned_size = (1 + (aligned_size / page_size)) * page_size;
#if ENABLE_VALIDATE_ARGS
if (aligned_size < size) {
errno = EINVAL;
return 0;
}
#endif
return valloc(size);
}
RPMALLOC_RESTRICT void* RPMALLOC_CDECL
aligned_alloc(size_t alignment, size_t size) {
initializer();
return rpaligned_alloc(alignment, size);
}
RPMALLOC_RESTRICT void* RPMALLOC_CDECL
memalign(size_t alignment, size_t size) {
initializer();
return rpmemalign(alignment, size);
}
int RPMALLOC_CDECL
posix_memalign(void** memptr, size_t alignment, size_t size) {
initializer();
return rpposix_memalign(memptr, alignment, size);
}
void RPMALLOC_CDECL
free(void* ptr) {
if (!is_initialized || !rpmalloc_is_thread_initialized())
return;
rpfree(ptr);
}
void RPMALLOC_CDECL
cfree(void* ptr) {
free(ptr);
}
size_t RPMALLOC_CDECL
malloc_usable_size(void* ptr) {
if (!rpmalloc_is_thread_initialized())
return 0;
return rpmalloc_usable_size(ptr);
}
size_t RPMALLOC_CDECL
malloc_size(void* ptr) {
return malloc_usable_size(ptr);
}
#ifdef _MSC_VER
extern void* RPMALLOC_CDECL
_expand(void* block, size_t size) {
return realloc(block, size);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_recalloc(void* block, size_t count, size_t size) {
initializer();
if (!block)
return rpcalloc(count, size);
size_t newsize = count * size;
size_t oldsize = rpmalloc_usable_size(block);
void* newblock = rprealloc(block, newsize);
if (newsize > oldsize)
memset((char*)newblock + oldsize, 0, newsize - oldsize);
return newblock;
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_aligned_malloc(size_t size, size_t alignment) {
return aligned_alloc(alignment, size);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_aligned_realloc(void* block, size_t size, size_t alignment) {
initializer();
size_t oldsize = rpmalloc_usable_size(block);
return rpaligned_realloc(block, alignment, size, oldsize, 0);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_aligned_recalloc(void* block, size_t count, size_t size, size_t alignment) {
initializer();
size_t newsize = count * size;
if (!block) {
block = rpaligned_alloc(count, newsize);
memset(block, 0, newsize);
return block;
}
size_t oldsize = rpmalloc_usable_size(block);
void* newblock = rpaligned_realloc(block, alignment, newsize, oldsize, 0);
if (newsize > oldsize)
memset((char*)newblock + oldsize, 0, newsize - oldsize);
return newblock;
}
void RPMALLOC_CDECL
_aligned_free(void* block) {
free(block);
}
extern size_t RPMALLOC_CDECL
_msize(void* ptr) {
return malloc_usable_size(ptr);
}
extern size_t RPMALLOC_CDECL
_aligned_msize(void* block, size_t alignment, size_t offset) {
return malloc_usable_size(block);
}
extern intptr_t RPMALLOC_CDECL
_get_heap_handle(void) {
return 0;
}
extern int RPMALLOC_CDECL
_heap_init(void) {
initializer();
return 1;
}
extern void RPMALLOC_CDECL
_heap_term() {
}
extern int RPMALLOC_CDECL
_set_new_mode(int flag) {
(void)sizeof(flag);
return 0;
}
#ifndef NDEBUG
extern int RPMALLOC_CDECL
_CrtDbgReport(int reportType, char const* fileName, int linenumber, char const* moduleName, char const* format, ...) {
return 0;
}
extern int RPMALLOC_CDECL
_CrtDbgReportW(int reportType, wchar_t const* fileName, int lineNumber, wchar_t const* moduleName, wchar_t const* format, ...) {
return 0;
}
extern int RPMALLOC_CDECL
_VCrtDbgReport(int reportType, char const* fileName, int linenumber, char const* moduleName, char const* format, va_list arglist) {
return 0;
}
extern int RPMALLOC_CDECL
_VCrtDbgReportW(int reportType, wchar_t const* fileName, int lineNumber, wchar_t const* moduleName, wchar_t const* format, va_list arglist) {
return 0;
}
extern int RPMALLOC_CDECL
_CrtSetReportMode(int reportType, int reportMode) {
return 0;
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_malloc_dbg(size_t size, int blockUse, char const* fileName, int lineNumber) {
return malloc(size);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_expand_dbg(void* block, size_t size, int blockUse, char const* fileName, int lineNumber) {
return _expand(block, size);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_calloc_dbg(size_t count, size_t size, int blockUse, char const* fileName, int lineNumber) {
return calloc(count, size);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_realloc_dbg(void* block, size_t size, int blockUse, char const* fileName, int lineNumber) {
return realloc(block, size);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_recalloc_dbg(void* block, size_t count, size_t size, int blockUse, char const* fileName, int lineNumber) {
return _recalloc(block, count, size);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_aligned_malloc_dbg(size_t size, size_t alignment, char const* fileName, int lineNumber) {
return aligned_alloc(alignment, size);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_aligned_realloc_dbg(void* block, size_t size, size_t alignment, char const* fileName, int lineNumber) {
return _aligned_realloc(block, size, alignment);
}
extern RPMALLOC_RESTRICT void* RPMALLOC_CDECL
_aligned_recalloc_dbg(void* block, size_t count, size_t size, size_t alignment, char const* fileName, int lineNumber) {
return _aligned_recalloc(block, count, size, alignment);
}
extern void RPMALLOC_CDECL
_free_dbg(void* block, int blockUse) {
free(block);
}
extern void RPMALLOC_CDECL
_aligned_free_dbg(void* block) {
free(block);
}
extern size_t RPMALLOC_CDECL
_msize_dbg(void* ptr) {
return malloc_usable_size(ptr);
}
extern size_t RPMALLOC_CDECL
_aligned_msize_dbg(void* block, size_t alignment, size_t offset) {
return malloc_usable_size(block);
}
#endif // NDEBUG
extern void* _crtheap = (void*)1;
#endif