diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | allocn.c | 84 | ||||
-rw-r--r-- | libsimple.h | 14 | ||||
-rw-r--r-- | test.c | 199 | ||||
-rw-r--r-- | test.h | 20 |
5 files changed, 310 insertions, 12 deletions
@@ -63,15 +63,16 @@ TESTS = $(OBJ:.o=.test) all: libsimple.a $(TESTS) $(OBJ): $(@:.o=.c) libsimple.h -$(TESTS): $(@:=.o) libsimple.a +$(TESTS): $(@:=.o) test.o libsimple.a $(TESTS:=.o): $(@:.test.o=.c) libsimple.h test.h +test.o: test.c libsimple.h test.h libsimple.a: $(OBJ) $(AR) rc $@ $? $(AR) -s $@ .test.o.test: - $(CC) -o $@ $< libsimple.a $(LDFLAGS) + $(CC) -o $@ $< libsimple.a test.o $(LDFLAGS) .c.test.o: $(CC) -c -o $@ $< $(CFLAGS) -DTEST @@ -4,7 +4,7 @@ static inline size_t -alloc_size_product(size_t n, va_list ap) /* TODO test */ +alloc_size_product(size_t n, va_list ap) { size_t prod = n; if (!n) { @@ -25,14 +25,14 @@ alloc_size_product(size_t n, va_list ap) /* TODO test */ } void * -libsimple_vmalloczn(int clear, size_t n, va_list ap) /* TODO test */ +libsimple_vmalloczn(int clear, size_t n, va_list ap) { n = alloc_size_product(n, ap); return !n ? NULL : clear ? calloc(1, n) : malloc(n); } void * -libsimple_vreallocn(void *ptr, size_t n, va_list ap) /* TODO test */ +libsimple_vreallocn(void *ptr, size_t n, va_list ap) { n = alloc_size_product(n, ap); return !n ? NULL : realloc(ptr, n); @@ -45,6 +45,84 @@ libsimple_vreallocn(void *ptr, size_t n, va_list ap) /* TODO test */ int main(void) { + struct allocinfo *info; + void *ptr, *old; + + assert(!libsimple_malloczn(0, 0) && errno == EINVAL); + errno = 0; + assert(!libsimple_malloczn(1, 0) && errno == EINVAL); + errno = 0; + assert(!libsimple_mallocn(0) && errno == EINVAL); + errno = 0; + assert(!libsimple_callocn(0) && errno == EINVAL); + errno = 0; + assert(!libsimple_reallocn(NULL, 0) && errno == EINVAL); + errno = 0; + + assert(!libsimple_malloczn(0, SIZE_MAX, 2, 0) && errno == ENOMEM); + errno = 0; + assert(!libsimple_malloczn(1, SIZE_MAX, 2, 0) && errno == ENOMEM); + errno = 0; + assert(!libsimple_mallocn(SIZE_MAX, 2, 0) && errno == ENOMEM); + errno = 0; + assert(!libsimple_callocn(SIZE_MAX, 2, 0) && errno == ENOMEM); + errno = 0; + assert(!libsimple_reallocn(NULL, SIZE_MAX, 2, 0) && errno == ENOMEM); + errno = 0; + + assert((ptr = libsimple_malloczn(0, 10, 10, 0))); + if (have_custom_malloc()) { + assert((info = get_allocinfo(ptr))); + assert(info->size == 100); + assert(!info->zeroed); + } + free(ptr); + + assert((ptr = libsimple_malloczn(1, 20, 20, 0))); + if (have_custom_malloc()) { + assert((info = get_allocinfo(ptr))); + assert(info->size == 400); + assert(info->zeroed == 400); + } + free(ptr); + + assert((ptr = libsimple_mallocn(11, 11, 0))); + if (have_custom_malloc()) { + assert((info = get_allocinfo(ptr))); + assert(info->size == 121); + assert(!info->zeroed); + } + free(ptr); + + assert((ptr = libsimple_callocn(22, 22, 0))); + if (have_custom_malloc()) { + assert((info = get_allocinfo(ptr))); + assert(info->size == 484); + assert(info->zeroed == 484); + } + free(ptr); + + assert((ptr = libsimple_reallocn(NULL, 5, 0))); + if (have_custom_malloc()) { + assert((info = get_allocinfo(ptr))); + assert(info->size == 5); + assert(!info->zeroed); + info->refcount += 1; + } + stpcpy(ptr, "test"); + assert((ptr = libsimple_reallocn(old = ptr, 10, 0))); + assert(!strcmp(ptr, "test")); + if (have_custom_malloc()) { + assert((info = get_allocinfo(ptr))); + assert(info->size == 10); + assert(!info->zeroed); + assert(ptr != old); + free(old); + } + free(ptr); + + assert(!errno); + return 0; } diff --git a/libsimple.h b/libsimple.h index 9d63c2c..6dfd62b 100644 --- a/libsimple.h +++ b/libsimple.h @@ -876,26 +876,26 @@ void *libsimple_vmalloczn(int, size_t, va_list); #endif _LIBSIMPLE_GCC_ONLY(__attribute__((__warn_unused_result__))) -static inline void *libsimple_vmallocn(size_t __n, va_list __ap) { return libsimple_vmalloczn(0, __n, __ap); } /* TODO test */ +static inline void *libsimple_vmallocn(size_t __n, va_list __ap) { return libsimple_vmalloczn(0, __n, __ap); } #ifndef vmallocn # define vmallocn libsimple_vmallocn #endif _LIBSIMPLE_GCC_ONLY(__attribute__((__warn_unused_result__))) -static inline void *libsimple_vcallocn(size_t __n, va_list __ap) { return libsimple_vmalloczn(1, __n, __ap); } /* TODO test */ +static inline void *libsimple_vcallocn(size_t __n, va_list __ap) { return libsimple_vmalloczn(1, __n, __ap); } #ifndef vcallocn # define vcallocn libsimple_vcallocn #endif _LIBSIMPLE_GCC_ONLY(__attribute__((__warn_unused_result__))) -void *libsimple_vreallocn(void *, size_t, va_list); /* TODO test */ +void *libsimple_vreallocn(void *, size_t, va_list); #ifndef vreallocn # define vreallocn libsimple_vreallocn #endif _LIBSIMPLE_GCC_ONLY(__attribute__((__warn_unused_result__))) static inline void * -libsimple_malloczn(int __clear, size_t __n, ...) /* TODO test */ +libsimple_malloczn(int __clear, size_t __n, ...) { va_list __ap; va_start(__ap, __n); @@ -908,7 +908,7 @@ libsimple_malloczn(int __clear, size_t __n, ...) /* TODO test */ _LIBSIMPLE_GCC_ONLY(__attribute__((__warn_unused_result__))) static inline void * -libsimple_mallocn(size_t __n, ...) /* TODO test */ +libsimple_mallocn(size_t __n, ...) { va_list __ap; va_start(__ap, __n); @@ -921,7 +921,7 @@ libsimple_mallocn(size_t __n, ...) /* TODO test */ _LIBSIMPLE_GCC_ONLY(__attribute__((__warn_unused_result__))) static inline void * -libsimple_callocn(size_t __n, ...) /* TODO test */ +libsimple_callocn(size_t __n, ...) { va_list __ap; va_start(__ap, __n); @@ -934,7 +934,7 @@ libsimple_callocn(size_t __n, ...) /* TODO test */ _LIBSIMPLE_GCC_ONLY(__attribute__((__warn_unused_result__))) static inline void * -libsimple_reallocn(void *__ptr, size_t __n, ...) /* TODO test */ +libsimple_reallocn(void *__ptr, size_t __n, ...) { va_list __ap; va_start(__ap, __n); @@ -0,0 +1,199 @@ +/* See LICENSE file for copyright and license details. */ +#include "libsimple.h" +#include "test.h" +#include <malloc.h> + + +size_t alloc_fail_in = 0; + +static int custom_malloc = 0; + + +size_t +get_pagesize(void) +{ + long r; + assert((r = sysconf(_SC_PAGESIZE)) >= 0); + return (size_t)r; +} + + +size_t +round_up(size_t size) +{ + size_t ps = get_pagesize(); + return size + (ps - size % ps) % ps; +} + + +int +have_custom_malloc(void) +{ + free(malloc(1)); + return custom_malloc; +} + + +struct allocinfo * +get_allocinfo(void *ptr) +{ + assert(ptr); + return (void *)((char *)ptr - sizeof(struct allocinfo)); +} + + +void * +malloc(size_t size) +{ + size_t alignment = get_pagesize(); + while (alignment < sizeof(long long int)) + alignment *= 2; + while (alignment < sizeof(long double)) + alignment *= 2; + return memalign(alignment, size); +} + + +void * +calloc(size_t nelem, size_t elsize) +{ + struct allocinfo *info; + void *ret; + assert(nelem && elsize); /* unspecified behaviour otherwise */ + if (nelem > SIZE_MAX / elsize) { + errno = ENOMEM; + return NULL; + } + ret = malloc(nelem * elsize); + if (!ret) + return NULL; + memset(ret, 0, nelem * elsize); + info = get_allocinfo(ret); + info->zeroed = nelem * elsize; + return ret; +} + + +void * +realloc(void *ptr, size_t size) +{ + struct allocinfo *info; + void *ret; + size_t n; + assert(size); /* unspecified behaviour otherwise */ + if (!ptr) + return malloc(size); + ret = malloc(size); + if (!ret) + return malloc; + info = get_allocinfo(ret); + n = MIN(size, info->size); + info->zeroed = MIN(n, info->zeroed); + memcpy(ret, ptr, n); + free(ptr); + return ret; +} + + +void * +memalign(size_t alignment, size_t size) +{ + struct allocinfo *info; + void *ptr; + size_t n; + uintptr_t off; + + custom_malloc = 1; + + assert(alignment); + assert(!(alignment & (alignment - 1UL))); + assert(size); /* unspecified behaviour otherwise */ + + if (alloc_fail_in && alloc_fail_in-- == 1) + goto enomem; + + n = size; + if (n > SIZE_MAX - alignment) + goto enomem; + n += alignment; + if (n > SIZE_MAX - sizeof(struct allocinfo)) + goto enomem; + n += sizeof(struct allocinfo); + + ptr = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (!ptr) + goto enomem; + + off = (uintptr_t)ptr; + off += sizeof(struct allocinfo); + off += (alignment - off % alignment) % alignment; + off -= (uintptr_t)ptr; + + ptr = (char *)ptr + off; + info = get_allocinfo(ptr); + + info->real_beginning = (char *)ptr - off; + info->real_size = n; + info->size = size; + info->extent = n - size - off; + info->alignment = alignment; + info->zeroed = 0; + info->refcount = 1; + + return ptr; + +enomem: + errno = ENOMEM; + return NULL; +} + + +int +posix_memalign(void **memptr, size_t alignment, size_t size) +{ + int ret, saved_errno = errno; + void **volatile ptrp = memptr; + assert(!(alignment % sizeof(void *))); + assert(ptrp); + *memptr = memalign(alignment, size); + ret = *memptr ? ENOMEM : 0; + errno = saved_errno; + return ret; +} + + +void * +aligned_alloc(size_t alignment, size_t size) +{ + assert(alignment); + assert(!(size % alignment)); + return memalign(alignment, size); +} + + +void * +valloc(size_t size) +{ + return memalign(get_pagesize(), size); +} + + +void * +pvalloc(size_t size) +{ + return memalign(get_pagesize(), round_up(size)); +} + + +void +free(void *ptr) +{ + struct allocinfo *info; + if (!ptr) + return; + info = get_allocinfo(ptr); + assert(info->refcount); + if (info->refcount-- > 1) + return; + assert(!munmap(info->real_beginning, info->real_size)); +} @@ -8,3 +8,23 @@ fprintf(stderr, "Failed at %s:%i: %s\n", __FILE__, __LINE__, #EXPR);\ exit(1);\ } while (0) + + +struct allocinfo { + void *real_beginning; + size_t real_size; + size_t size; + size_t extent; + size_t alignment; + size_t zeroed; + size_t refcount; +}; + + +extern size_t alloc_fail_in; + + +size_t get_pagesize(void); +size_t round_up(size_t); +int have_custom_malloc(void); /* return 0 if run under valgrind(1) */ +struct allocinfo *get_allocinfo(void *); |