--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * alloc - memory allocator routines
+ *
+ * The alloc module implements a simple allocator which you can use to
+ * dynamically allocate space within a region of memory. This can be useful
+ * for suballocations within a given region, or a memory-mapped file.
+ *
+ * All metadata is kept within the memory handed to the allocator: you only
+ * need hand the pointer and the size of the memory to each call.
+ *
+ * The region contents is always in offsets, so it can be mapped in different
+ * places, but is not endian-safe.
+ *
+ * Example:
+ * #include <sys/mman.h>
+ * #include <unistd.h>
+ * #include <sys/types.h>
+ * #include <err.h>
+ * #include "alloc/alloc.h"
+ *
+ * static void usage(const char *name)
+ * {
+ * errx(1, "Usage: %s --create <mapfile>\n"
+ * " %s --check <mapfile>\n"
+ * " %s --alloc <mapfile>\n"
+ * " %s --free=<offset> <mapfile>\n", name, name, name);
+ * }
+ *
+ * // Create a memory mapped file, and allocate from within it
+ * int main(int argc, char *argv[])
+ * {
+ * void *a, *p;
+ * int fd;
+ * enum { CREATE, CHECK, ALLOC, FREE } cmd;
+ *
+ * if (argc != 3)
+ * usage(argv[0]);
+ *
+ * if (strcmp(argv[1], "--create") == 0)
+ * cmd = CREATE;
+ * else if (strcmp(argv[1], "--check") == 0)
+ * cmd = CHECK;
+ * else if (strcmp(argv[1], "--alloc") == 0)
+ * cmd = ALLOC;
+ * else if (strncmp(argv[1], "--free=", strlen("--free=")) == 0)
+ * cmd = FREE;
+ * else
+ * usage(argv[0]);
+ *
+ * if (cmd == CREATE) {
+ * fd = open(argv[2], O_RDWR|O_CREAT|O_EXCL, 0600);
+ * if (fd < 0)
+ * err(1, "Could not create %s", argv[2]);
+ * if (ftruncate(fd, 1048576) != 0)
+ * err(1, "Could not set length on %s", argv[2]);
+ * } else {
+ * fd = open(argv[2], O_RDWR);
+ * if (fd < 0)
+ * err(1, "Could not open %s", argv[2]);
+ * }
+ *
+ * a = mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_SHARED, fd,0);
+ * if (a == MAP_FAILED)
+ * err(1, "Could not map %s", argv[2]);
+ *
+ * switch (cmd) {
+ * case CREATE:
+ * alloc_init(a, 1048576);
+ * break;
+ * case CHECK:
+ * if (!alloc_check(a, 1048576))
+ * err(1, "Region is corrupt");
+ * break;
+ * case ALLOC:
+ * p = alloc_get(a, 1048576, 1024, 16);
+ * if (!p)
+ * errx(1, "Could not allocate");
+ * printf("%zu\n", (char *)p - (char *)a);
+ * break;
+ * case FREE:
+ * p = (char *)a + atol(argv[1] + strlen("--free="));
+ * alloc_free(a, 1048576, p);
+ * break;
+ * }
+ * return 0;
+ * }
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include "alloc.h"
+
+/* FIXME: Doesn't handle non-page-aligned poolsize. */
+/* FIXME: Doesn't handle sub-page allocations. */
+
+#define MIN_SIZE (getpagesize() * 2)
+
+enum page_state
+{
+ FREE,
+ TAKEN,
+ TAKEN_START,
+};
+
+void alloc_init(void *pool, unsigned long poolsize)
+{
+ uint8_t *bits = pool;
+ unsigned int pages = poolsize / getpagesize();
+
+ if (poolsize < MIN_SIZE)
+ return;
+
+ memset(bits, 0, (pages * 2 + CHAR_BIT - 1)/ CHAR_BIT);
+}
+
+static enum page_state get_page_state(const uint8_t *bits, unsigned long page)
+{
+ return bits[page * 2 / CHAR_BIT] >> (page * 2 % CHAR_BIT) & 3;
+}
+
+static void set_page_state(uint8_t *bits, unsigned long page, enum page_state s)
+{
+ bits[page * 2 / CHAR_BIT] &= ~(3 << (page * 2 % CHAR_BIT));
+ bits[page * 2 / CHAR_BIT] |= ((uint8_t)s << (page * 2 % CHAR_BIT));
+}
+
+static unsigned long metadata_length(void *pool, unsigned long poolsize)
+{
+ return ((poolsize / getpagesize() * 2 / CHAR_BIT) + getpagesize() - 1)
+ & ~(getpagesize() - 1);
+}
+
+void *alloc_get(void *pool, unsigned long poolsize,
+ unsigned long size, unsigned long align)
+{
+ unsigned long i, free, want, metalen;
+
+ if (poolsize < MIN_SIZE)
+ return NULL;
+
+ /* FIXME: Necessary for this. */
+ if (size == 0)
+ size = 1;
+
+ want = (size + getpagesize() - 1) / getpagesize();
+ metalen = metadata_length(pool, poolsize);
+
+ free = 0;
+ for (i = 0; i < (poolsize - metalen) / getpagesize(); i++) {
+ switch (get_page_state(pool, i)) {
+ case FREE:
+ if (++free >= want) {
+ unsigned int j;
+ char *ret = (char *)pool + metalen
+ + (i - want + 1) * getpagesize();
+
+ if ((unsigned long)ret % align)
+ continue;
+
+ for (j = i; j > i - want + 1; j--)
+ set_page_state(pool, j, TAKEN);
+ set_page_state(pool, i - want + 1, TAKEN_START);
+ return ret;
+ }
+ break;
+ case TAKEN_START:
+ case TAKEN:
+ free = 0;
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+void alloc_free(void *pool, unsigned long poolsize, void *free)
+{
+ unsigned long pagenum, metalen;
+
+ if (poolsize < MIN_SIZE)
+ return;
+
+ if (!free)
+ return;
+
+ metalen = metadata_length(pool, poolsize);
+
+ assert(free > pool && (char *)pool + poolsize > (char *)free);
+ assert((unsigned long)free % getpagesize() == 0);
+
+ pagenum = ((char *)free - (char *)pool - metalen) / getpagesize();
+
+ assert(get_page_state(pool, pagenum) == TAKEN_START);
+ set_page_state(pool, pagenum, FREE);
+ while (get_page_state(pool, ++pagenum) == TAKEN)
+ set_page_state(pool, pagenum, FREE);
+}
+
+bool alloc_check(void *pool, unsigned long poolsize)
+{
+ unsigned int i, metalen;
+ enum page_state last_state = FREE;
+
+ if (poolsize < MIN_SIZE)
+ return true;
+
+ metalen = metadata_length(pool, poolsize);
+ for (i = 0; i < (poolsize - metalen) / getpagesize(); i++) {
+ enum page_state state = get_page_state(pool, i);
+ switch (state) {
+ case FREE:
+ case TAKEN_START:
+ break;
+ case TAKEN:
+ if (last_state == FREE)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ last_state = state;
+ }
+ return true;
+}
--- /dev/null
+#ifndef ALLOC_H
+#define ALLOC_H
+#include <stdbool.h>
+
+void alloc_init(void *pool, unsigned long poolsize);
+void *alloc_get(void *pool, unsigned long poolsize,
+ unsigned long size, unsigned long align);
+void alloc_free(void *pool, unsigned long poolsize, void *free);
+bool alloc_check(void *pool, unsigned long poolsize);
+
+#endif /* ALLOC_H */
--- /dev/null
+#include "alloc/alloc.h"
+#include "tap.h"
+#include "alloc/alloc.c"
+#include <stdlib.h>
+
+#define POOL_ORD 16
+#define POOL_SIZE (1 << POOL_ORD)
+
+#define sort(p, num, cmp) \
+ qsort((p), (num), sizeof(*p), (int(*)(const void *, const void *))cmp)
+
+static int addr_cmp(void **a, void **b)
+{
+ return (*a) - (*b);
+}
+
+static bool unique(void *p[], unsigned int num)
+{
+ unsigned int i;
+
+ for (i = 1; i < num; i++)
+ if (p[i] == p[i-1])
+ return false;
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ void *mem;
+ unsigned int i, num, max_size;
+ void *p[POOL_SIZE];
+
+ plan_tests(141);
+
+ /* FIXME: Needs to be page aligned for now. */
+ posix_memalign(&mem, getpagesize(), POOL_SIZE);
+
+ /* Small pool, all allocs fail, even 0-length. */
+ alloc_init(mem, 0);
+ ok1(alloc_check(mem, 0));
+ ok1(alloc_get(mem, 0, 1, 1) == NULL);
+ ok1(alloc_get(mem, 0, 128, 1) == NULL);
+ ok1(alloc_get(mem, 0, 0, 1) == NULL);
+
+ /* Free of NULL should work. */
+ alloc_free(mem, 0, NULL);
+
+ alloc_init(mem, POOL_SIZE);
+ ok1(alloc_check(mem, POOL_SIZE));
+ /* Find largest allocation which works. */
+ for (max_size = POOL_SIZE * 2; max_size; max_size--) {
+ p[0] = alloc_get(mem, POOL_SIZE, max_size, 1);
+ if (p[0])
+ break;
+ }
+ ok1(max_size < POOL_SIZE);
+ ok1(max_size > 0);
+ ok1(alloc_check(mem, POOL_SIZE));
+
+ /* Free it, should be able to reallocate it. */
+ alloc_free(mem, POOL_SIZE, p[0]);
+ ok1(alloc_check(mem, POOL_SIZE));
+
+ p[0] = alloc_get(mem, POOL_SIZE, max_size, 1);
+ ok1(p[0]);
+ ok1(alloc_check(mem, POOL_SIZE));
+ alloc_free(mem, POOL_SIZE, p[0]);
+ ok1(alloc_check(mem, POOL_SIZE));
+
+ /* Allocate a whole heap. */
+ for (i = 0; i < POOL_SIZE; i++) {
+ p[i] = alloc_get(mem, POOL_SIZE, 1, 1);
+ if (!p[i])
+ break;
+ }
+
+ num = i;
+ /* Can't allocate this many. */
+ ok1(num != POOL_SIZE);
+ ok1(alloc_check(mem, POOL_SIZE));
+
+ /* Sort them. */
+ sort(p, num, addr_cmp);
+
+ /* Uniqueness check */
+ ok1(unique(p, num));
+
+ /* Free every second one. */
+ for (i = 0; i < num; i += 2) {
+ alloc_free(mem, POOL_SIZE, p[i]);
+ ok1(alloc_check(mem, POOL_SIZE));
+ }
+ for (i = 1; i < num; i += 2) {
+ alloc_free(mem, POOL_SIZE, p[i]);
+ ok1(alloc_check(mem, POOL_SIZE));
+ }
+ ok1(alloc_check(mem, POOL_SIZE));
+
+ /* Should be able to reallocate max size. */
+ p[0] = alloc_get(mem, POOL_SIZE, max_size, 1);
+ ok1(p[0]);
+ ok1(alloc_check(mem, POOL_SIZE));
+
+ /* Re-initializing should be the same as freeing everything */
+ alloc_init(mem, POOL_SIZE);
+ ok1(alloc_check(mem, POOL_SIZE));
+ p[0] = alloc_get(mem, POOL_SIZE, max_size, 1);
+ ok1(p[0]);
+ ok1(alloc_check(mem, POOL_SIZE));
+ alloc_free(mem, POOL_SIZE, p[0]);
+ ok1(alloc_check(mem, POOL_SIZE));
+
+ /* Alignment constraints should be met, as long as powers of two */
+ for (i = 0; i < POOL_ORD-2 /* FIXME: Should be -1 */; i++) {
+ p[i] = alloc_get(mem, POOL_SIZE, i, 1 << i);
+ ok1(p[i]);
+ ok1(((unsigned long)p[i] % (1 << i)) == 0);
+ ok1(alloc_check(mem, POOL_SIZE));
+ }
+
+ for (i = 0; i < POOL_ORD-2 /* FIXME: Should be -1 */; i++) {
+ alloc_free(mem, POOL_SIZE, p[i]);
+ ok1(alloc_check(mem, POOL_SIZE));
+ }
+
+ /* Alignment constraints for a single-byte allocation. */
+ for (i = 0; i < POOL_ORD; i++) {
+ p[0] = alloc_get(mem, POOL_SIZE, 1, 1 << i);
+ ok1(p[0]);
+ ok1(alloc_check(mem, POOL_SIZE));
+ alloc_free(mem, POOL_SIZE, p[0]);
+ ok1(alloc_check(mem, POOL_SIZE));
+ }
+
+ return exit_status();
+}