aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--allocn.c84
-rw-r--r--libsimple.h14
-rw-r--r--test.c199
-rw-r--r--test.h20
5 files changed, 310 insertions, 12 deletions
diff --git a/Makefile b/Makefile
index 763ccda..505ab47 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/allocn.c b/allocn.c
index 671f8a3..cc27dc5 100644
--- a/allocn.c
+++ b/allocn.c
@@ -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);
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..05d74d4
--- /dev/null
+++ b/test.c
@@ -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));
+}
diff --git a/test.h b/test.h
index e4e08f2..5281cbc 100644
--- a/test.h
+++ b/test.h
@@ -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 *);