--- /dev/null
+/** allocwithin
+ * This is a very, very basic allocator.
+ * It burns a lot of space, and makes no effort at being clever nor efficient.
+ * But it does allow one to use a segment of (it assumes shared) memory as a
+ * pool for dynamic allocation, and allows that block to be moved around.
+ * The heavy-handed signal masking is probably not the most elegant way of avoiding interprocess corruption.
+**/
+
+#include "allocwithin_int.h"
+
+#define NOTIFY_ERROR(...) do { fprintf(stderr, "ERROR "); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } while (0)
+#ifdef PRINT_DEBUG
+#define NOTIFY_DEBUG(...) do { fprintf(stderr, "DEBUG [%d] %s> ", __LINE__, __func__); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } while (0)
+#else /* PRINT_DEBUG */
+#define NOTIFY_DEBUG(...) do { } while (0)
+#endif /* PRINT_DEBUG */
+
+static const char * const src_id_ = "$Id$";
+
+static pthread_once_t init_once_ = PTHREAD_ONCE_INIT;
+static int init_result_ = -1;
+static pthread_mutexattr_t allocw_mutexattr_;
+
+/**
+ * private function prototypes
+**/
+static void allocw_init_once_(void);
+static int region_init_(allocw_region_t *region, size_t size);
+static int region_fini_(allocw_region_t *region);
+static int region_lock_(allocw_region_t *region, sigset_t *oset);
+static int region_unlock_(allocw_region_t *region, sigset_t *oset);
+static void region_freelist_insert_(allocw_region_t *region, struct allocw_block_ *b);
+static struct allocw_block_ *block_split_(allocw_region_t *region, struct allocw_block_ *b, size_t payload_size);
+static void block_unsplit_(allocw_region_t *region, struct allocw_block_ *b);
+static struct allocw_block_ *alloc_block_(allocw_region_t *region, struct allocw_block_ *block, size_t size);
+
+
+/** allocw_init_once_
+ * called once to initialize static attrs
+**/
+static
+void allocw_init_once_(void)
+{
+ int r;
+
+ NOTIFY_DEBUG("initializing");
+
+ memset(&allocw_mutexattr_, 0, sizeof allocw_mutexattr_);
+
+ if ( (r = pthread_mutexattr_init(&allocw_mutexattr_)) )
+ {
+ NOTIFY_ERROR("%s:%s", "pthread_mutexattr_init", strerror(r));
+ init_result_ = r;
+ return;
+ }
+
+#if 0
+ /* darwin doesn't like this */
+ if ( (r = pthread_mutexattr_setpshared(&allocw_mutexattr_, PTHREAD_PROCESS_SHARED)) )
+ {
+ NOTIFY_ERROR("%s:%s", "pthread_mutexattr_setpshared", strerror(r));
+ init_result_ = r;
+ return;
+ }
+#endif
+
+ init_result_ = 0;
+}
+
+
+#ifdef PRINT_DEBUG
+/** dump_region
+ * debug function, display a region's basic details
+**/
+static
+inline
+void dump_region(char *label, allocw_region_t *region)
+{
+ NOTIFY_DEBUG("%s:%p", label, region);
+ NOTIFY_DEBUG("\t%s->%s:0x%zx", label, "size", region->size);
+ NOTIFY_DEBUG("\t%s->%s:0x%zx", label, "free_start", region->free_start);
+ NOTIFY_DEBUG("\t%s->%s:0x%zx", label, "free_end", region->free_end);
+ NOTIFY_DEBUG("\t%s->%s:0x%x", label, "num_alloced", region->num_alloced);
+}
+/** dump_block
+ * debug function, display a block's basic details
+**/
+static
+inline
+void dump_block(char *label, struct allocw_block_ *block)
+{
+ NOTIFY_DEBUG("%s:%p", label, block);
+ NOTIFY_DEBUG("\t%s->%s:0x%zx", label, "size", block->size);
+ NOTIFY_DEBUG("\t%s->%s:0x%zx", label, "prev_off", block->prev_off);
+ NOTIFY_DEBUG("\t%s->%s:0x%zx", label, "next_off", block->next_off);
+}
+#endif /* PRINT_DEBUG */
+
+
+/** region_init_
+ * Initializes a region's header fields.
+**/
+static
+int region_init_(allocw_region_t *region, size_t size)
+{
+ struct allocw_block_ *b;
+ int r;
+
+ region->size = size;
+ memset(®ion->freelist_mutex, 0, sizeof region->freelist_mutex);
+ if ( (r = pthread_mutex_init(®ion->freelist_mutex, &allocw_mutexattr_)) )
+ {
+ NOTIFY_ERROR("%s:%s", "pthread_mutex_init", strerror(r));
+ memset(region, 0, sizeof *region);
+ return r;
+ }
+
+#ifdef PRINT_DEBUG
+ dump_region("region", region);
+#endif /* PRINT_DEBUG */
+
+ /* initialize the freelist */
+ b = (struct allocw_block_ *)region->data_;
+ b->next_off = 0;
+ b->prev_off = 0;
+ b->size = region->size - sizeof *region;
+ region->free_start = region->free_end = BLOCK_OFF(region, b);
+ region->num_alloced = 0;
+
+#ifdef PRINT_DEBUG
+ dump_block("first_free_block", b);
+#endif /* PRINT_DEBUG */
+
+ return 0;
+}
+
+/** allocw_region_init
+ * Prepares a segment of memory for use as an allocation region.
+ * @note Total usable size will be #size minus per-allocation overhead;
+ *
+ * @param[in] region Pointer to the block of memory to initialize.
+ * @param[in] size Total size in bytes of memory region to initialize; usable size will be minus region header size and per-allocation overhead.
+ * @return Success or failure.
+**/
+int allocw_region_init(allocw_region_t *region, size_t size)
+{
+ struct allocw_block_ *b;
+ int r;
+
+ if (region == NULL)
+ {
+ NOTIFY_ERROR("null argument");
+ return EINVAL;
+ }
+ if (size < sizeof *region + sizeof *b )
+ {
+ NOTIFY_ERROR("headers require 0x%zx size, 0x%zx requested", sizeof *region + sizeof *b, size);
+ return ENOMEM;
+ }
+
+ if ( (r = pthread_once(&init_once_, allocw_init_once_)) )
+ {
+ NOTIFY_ERROR("%s:%s", "pthread_once", strerror(r));
+ return r;
+ }
+ if (init_result_)
+ {
+ NOTIFY_ERROR("%s:%d", "allocw_init_once_", init_result_);
+ memset(region, 0, sizeof *region);
+ return init_result_;
+ }
+
+ NOTIFY_DEBUG("sizeof allocw_region_t:0x%zx", sizeof *region);
+ NOTIFY_DEBUG("sizeof allocw_block_:0x%zx", sizeof *b);
+
+ if ( (r = region_init_(region, size)) )
+ {
+ NOTIFY_DEBUG("%s:%s", "allocw_region_init_", "failed");
+ return r;
+ }
+
+ return 0;
+}
+
+
+/** region_fini_
+ *
+**/
+static
+inline
+int region_fini_(allocw_region_t *region)
+{
+ int r;
+
+ if ( (r = pthread_mutex_destroy(®ion->freelist_mutex)) )
+ {
+ NOTIFY_ERROR("%s:%s", "pthread_mutex_destroy", strerror(r));
+ return r;
+ }
+
+ memset(region, 0, sizeof *region);
+
+ return 0;
+}
+
+/** allocw_region_fini
+ * release a region
+ *
+ * @param[in] region
+ * @return Success or failure.
+**/
+int allocw_region_fini(allocw_region_t *region)
+{
+ if (region == NULL)
+ {
+ NOTIFY_ERROR("null argument");
+ return EINVAL;
+ }
+
+ NOTIFY_DEBUG("destroying region %p", region);
+
+ return region_fini_(region);
+}
+
+
+/** region_lock_
+ * Acquires the lock on a region and disables signal processing.
+ *
+ * @param[in] region which region to lock
+ * @param[out] set the signal mask which was cleared
+ * @return Success or failure.
+**/
+static
+inline
+int region_lock_(allocw_region_t *region, sigset_t *oset)
+{
+ sigset_t set;
+ int r;
+
+ if (oset)
+ sigfillset(&set);
+
+ if ( (r = pthread_mutex_lock(®ion->freelist_mutex)) )
+ {
+ NOTIFY_ERROR("%s:%s", "pthread_mutex_lock", strerror(r));
+ errno = r;
+ return -1;
+ }
+
+ if ( oset && (r = pthread_sigmask(SIG_SETMASK, &set, oset)) )
+ {
+ NOTIFY_ERROR("%s:%s", "pthread_sigmask", strerror(r));
+ errno = r;
+ return -1;
+ }
+
+ NOTIFY_DEBUG("region %p %sLOCKED", region, "");
+
+ return 0;
+}
+
+
+/** region_unlock_
+ * Releases the lock on a region and restores signal processing.
+ *
+ * @param[in] region which region to lock
+ * @param[in] oset the signal mask to restore
+ * @return Success or failure.
+**/
+static
+inline
+int region_unlock_(allocw_region_t *region, sigset_t *oset)
+{
+ int retval = 0;
+ int r;
+
+ if ( oset && (r = pthread_sigmask(SIG_SETMASK, oset, NULL)) )
+ {
+ NOTIFY_ERROR("%s:%s", "pthread_sigmask", strerror(r));
+ errno = r;
+ retval = -1;
+ }
+
+ if ( (r = pthread_mutex_unlock(®ion->freelist_mutex)) )
+ {
+ NOTIFY_ERROR("%s:%s", "pthread_mutex_unlock", strerror(r));
+ errno = r;
+ retval = -1;
+ }
+
+ NOTIFY_DEBUG("region %p %sLOCKED", region, "UN");
+
+ return retval;
+}
+
+
+/** block_split_
+ * possibly partitions a free block, returns pointer to the size-d block
+ * block must be equal to or greater than split size
+ *
+ * @param[in] region region
+ * @param[in] b block the block to split
+ * @param[in] payload_size size of the desired block's user data
+ * @return pointer to the newly-pared block of at least #size payload-bytes
+**/
+static
+inline
+struct allocw_block_ *block_split_(allocw_region_t *region, struct allocw_block_ *b, size_t payload_size)
+{
+ const size_t new_block_size = payload_size + sizeof *b;
+ struct allocw_block_ *newb;
+
+ /* to split a block, we need room to spare for at least the header of next block */
+ if (b->size < new_block_size + sizeof *b)
+ {
+ NOTIFY_DEBUG("not enough room to split 0x%zx bytes off from 0x%zx byte block", new_block_size, b->size);
+ /* we'll just overcommit by the extra bytes */
+ return b;
+ }
+
+ NOTIFY_DEBUG("splitting %p (size:0x%zx) to fit requested 0x%zx (total 0x%zx)", b, b->size, payload_size, new_block_size + sizeof *b);
+
+ newb = (struct allocw_block_ *)((char *)b + new_block_size);
+ newb->size = b->size - (new_block_size);
+ b->size = new_block_size;
+ newb->prev_off = BLOCK_OFF(region, b);
+ newb->next_off = b->next_off;
+ if (newb->next_off)
+ BLOCK_PTR(region, newb->next_off)->prev_off = BLOCK_OFF(region, newb);
+ else
+ region->free_end = BLOCK_OFF(region, newb);
+
+ b->size = new_block_size;
+ b->next_off = BLOCK_OFF(region, newb);
+
+#ifdef PRINT_DEBUG
+ dump_block("newb", newb);
+ dump_block("b", b);
+#endif /* PRINT_DEBUG */
+
+ return b;
+}
+
+
+/** block_unsplit_
+ * attempt to merge a free block with its next
+**/
+static
+inline
+void block_unsplit_(allocw_region_t *region, struct allocw_block_ *b)
+{
+ if (b->next_off)
+ {
+ struct allocw_block_ *n = BLOCK_PTR(region, b->next_off);
+
+ if (BLOCK_OFF(region, b) + b->size == b->next_off)
+ {
+ NOTIFY_DEBUG("merging id %zu (%p) size:%zu with id %zu (%p) size:%zu",
+ BLOCK_OFF(region, b), b, b->size,
+ BLOCK_OFF(region, n), n, n->size);
+ b->size += n->size;
+ b->next_off = n->next_off;
+ if (n->next_off)
+ BLOCK_PTR(region, n->next_off)->prev_off = BLOCK_OFF(region, b);
+ else
+ region->free_end = BLOCK_OFF(region, b);
+ NOTIFY_DEBUG("new block id %zu (%p) size:%zu",
+ BLOCK_OFF(region, b), b, b->size);
+ }
+ }
+}
+
+
+/** region_freelist_insert
+ * Returns a block to the freelist, merges adjacent blocks.
+ *
+ * @param[in] region region
+ * @param[in] b block
+**/
+static
+inline
+void region_freelist_insert_(allocw_region_t *region, struct allocw_block_ *b)
+{
+ struct allocw_block_ *x;
+
+ NOTIFY_DEBUG("insert of block %zu (%p) size:%zu", BLOCK_OFF(region, b), b, b->size);
+
+ /* empty freelist? easy. */
+ if (region->free_start == 0)
+ {
+ NOTIFY_DEBUG("inserting into empty freelist");
+ b->prev_off = 0;
+ b->next_off = 0;
+ region->free_start = BLOCK_OFF(region, b);
+ region->free_end = BLOCK_OFF(region, b);
+ return;
+ }
+
+ /* find the block to insert in front of */
+ x = BLOCK_PTR(region, region->free_start);
+ while ( BLOCK_OFF(region, x)
+ && BLOCK_OFF(region, x) < BLOCK_OFF(region, b) )
+ {
+ x = BLOCK_PTR(region, x->next_off);
+ }
+
+ if (BLOCK_OFF(region, x))
+ {
+ NOTIFY_DEBUG("inserting before block %zu (%p) size:%zu prev:%zu next:%zu", BLOCK_OFF(region, x), x, x->size, x->prev_off, x->next_off);
+ b->next_off = BLOCK_OFF(region, x);
+ b->prev_off = x->prev_off;
+ if (BLOCK_PTR(region, x->prev_off))
+ BLOCK_PTR(region, b->prev_off)->next_off = BLOCK_OFF(region, b);
+ else
+ region->free_start = BLOCK_OFF(region, b);
+ if (BLOCK_PTR(region, b->next_off))
+ BLOCK_PTR(region, b->next_off)->prev_off = BLOCK_OFF(region, b);
+ else
+ region->free_end = BLOCK_OFF(region, b);
+ }
+ else /* otherwise, b's offset is bigger than everything else, so tack it onto the end */
+ {
+ NOTIFY_DEBUG("inserting at end of freelist, after %zu (%p)", region->free_end, BLOCK_PTR(region, region->free_end));
+ b->next_off = 0;
+ b->prev_off = region->free_end;
+ BLOCK_PTR(region, region->free_end)->next_off = BLOCK_OFF(region, b);
+ region->free_end = BLOCK_OFF(region, b);
+ }
+
+ block_unsplit_(region, b);
+ if (b->prev_off)
+ {
+ x = BLOCK_PTR(region, b->prev_off);
+ block_unsplit_(region, x);
+ }
+}
+
+
+/** alloc_block_
+ * Rounds #size up to an allocatable chunk, carves off enough storage for it from free-block #block, and removes it from the freelist.
+ * #block must be big enough.
+**/
+static
+inline
+struct allocw_block_ *alloc_block_(allocw_region_t *region, struct allocw_block_ *block, size_t size)
+{
+ const size_t granularity = sizeof (struct allocw_block_);
+ const size_t size_adj = (size + granularity - 1) & ~(granularity - 1);
+
+ block = block_split_(region, block, size_adj);
+
+ if (block->prev_off)
+ BLOCK_PTR(region, block->prev_off)->next_off = block->next_off;
+ else
+ region->free_start = block->next_off;
+ if (block->next_off)
+ BLOCK_PTR(region, block->next_off)->prev_off = block->prev_off;
+ else
+ region->free_end = block->prev_off;
+ block->next_off = block->prev_off = 0;
+
+ return block;
+}
+
+/** allocw_realloc
+ * Resize a previously allocated id.
+ * Implementation idiosyncrasies: if size is zero, returns null after freeing id.
+ *
+ * @param[in] region
+ * @param[in] id
+ * @param[in] size
+ * @return new id
+**/
+allocw_id_t allocw_realloc(allocw_region_t *region, allocw_id_t id, size_t size)
+{
+ allocw_id_t retval = 0;
+ sigset_t set;
+ struct allocw_block_ *b,
+ *b_id,
+ *b_want,
+ *best_fit = NULL;
+
+ if (region == NULL)
+ {
+ NOTIFY_ERROR("null argument");
+ return 0;
+ };
+
+ /* route simple requests away */
+
+ if (id == 0)
+ return allocw_malloc(region, size);
+
+ if (size == 0)
+ {
+ allocw_free(region, id);
+ return 0;
+ }
+
+ if (size <= BLOCK_PTR(region, id)->size)
+ {
+ NOTIFY_DEBUG("ignoring realloc to smaller block");
+ return id;
+ }
+
+ if (region_lock_(region, &set))
+ return 0;
+
+ /*
+ Locate the smallest region we can fit into, EXCEPT favor a freeblock
+ of any adequate size immediately following the original block, to
+ avoid copying.
+ */
+ b_id = BLOCK_PTR(region, id);
+ b_want = BLOCK_PTR(region, id + b_id->size);
+ for ( b = BLOCK_PTR(region, region->free_start);
+ (void *)b != (void *)region;
+ b = BLOCK_PTR(region, b->next_off) )
+ {
+ if (b->size == size || b->size >= size + sizeof *b)
+ {
+ if (best_fit == NULL
+ || b->size < best_fit->size)
+ {
+ best_fit = b;
+ if (best_fit == b_want)
+ break;
+ }
+ }
+ }
+
+ if (best_fit == NULL)
+ {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ if (best_fit == b_want)
+ {
+ size_t size_diff = size - b_id->size;
+
+ NOTIFY_DEBUG("growing %p (size:0x%zx) by size_diff:0x%zx into freeblock %p (size:0x%zx)",
+ b_id, b_id->size,
+ size_diff,
+ best_fit, best_fit->size);
+
+ best_fit = alloc_block_(region, best_fit, size_diff);
+ b_id->size += best_fit->size;
+ b_id->next_off = best_fit->next_off;
+ if (b_id->next_off)
+ BLOCK_PTR(region, best_fit->next_off)->prev_off = BLOCK_OFF(region, b_id);
+ else
+ region->free_end = id;
+
+ retval = id;
+ }
+ else
+ {
+ size_t min_size = (b_id->size < size) ? b_id->size : size;
+
+ NOTIFY_DEBUG("reallocating %p (size:0x%zx) to %p (size:0x%zx)",
+ b_id, b_id->size,
+ best_fit, best_fit->size);
+
+ best_fit = alloc_block_(region, best_fit, size);
+ retval = BLOCK_OFF(region, best_fit);
+ memcpy(best_fit->data_, b_id->data_, min_size);
+ region_freelist_insert_(region, b_id);
+ }
+
+ if (region_unlock_(region, &set))
+ return retval;
+
+ return retval;
+}
+
+/** allocw_free
+ * Free a previously allocated memory id in a region.
+ *
+ * @param[in] region region
+ * @param[in] id id of memory in region to free
+**/
+void allocw_free(allocw_region_t *region, allocw_id_t id)
+{
+ sigset_t set;
+ struct allocw_block_ *b;
+
+ if (region == NULL)
+ {
+ NOTIFY_ERROR("null argument");
+ errno = EINVAL;
+ return;
+ }
+
+ if (id == 0)
+ {
+ NOTIFY_ERROR("attempted free of null id");
+ return;
+ }
+
+ b = BLOCK_PTR(region, id);
+
+ NOTIFY_DEBUG("want to free block %p size:0x%zx", b, b->size);
+
+ if (region_lock_(region, &set))
+ return;
+
+ region_freelist_insert_(region, b);
+
+ region->num_alloced--;
+
+ if (region_unlock_(region, &set))
+ return;
+}
+
+
+/** allocw_malloc
+ * Allocates a block of memory from the region.
+ *
+ * @param[in] region region
+ * @param[in] size size in bytes to allocate
+ * @return id of newly allocated memory
+**/
+allocw_id_t allocw_malloc(allocw_region_t *region, size_t size)
+{
+ const size_t granularity = sizeof (struct allocw_block_);
+ const size_t size_adj = (size + granularity - 1) & ~(granularity - 1);
+ sigset_t set;
+ struct allocw_block_ *b;
+ allocw_id_t ret_id = 0;
+
+ if (region == NULL)
+ {
+ NOTIFY_ERROR("null argument");
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (region_lock_(region, &set))
+ return 0;
+
+ for (b = BLOCK_PTR(region, region->free_start);
+ (void *)b != (void *)region;
+ b = BLOCK_PTR(region, b->next_off) )
+ {
+ NOTIFY_DEBUG("checking free block:%p size:0x%zu", b, b->size);
+ if (b->size >= size_adj)
+ break;
+ }
+
+ if ((void *)b == (void *)region)
+ {
+ NOTIFY_DEBUG("no free block with enough room");
+ if (region_unlock_(region, &set))
+ return 0;
+ errno = EINVAL;
+ return 0;
+ }
+
+ b = alloc_block_(region, b, size);
+ ret_id = BLOCK_OFF(region, b);
+
+ region->num_alloced++;
+
+ if (region_unlock_(region, &set))
+ return ret_id;
+
+ return ret_id;
+}
+
+
+/** allocw_ptr
+ * Converts a (region,id) to a normal pointer.
+ *
+ * @param[in] region region
+ * @param[in] id id
+ * @return pointer
+**/
+inline
+void *allocw_ptr(allocw_region_t *region, allocw_id_t id)
+{
+ if (region == NULL || id == 0)
+ return NULL;
+
+ return &(BLOCK_PTR(region, id)->data_);
+}
+
+
+/** allocw_region_migrate
+ * initialize a new region with an existing region's data
+ *
+ * @param[in] dst_region
+ * @param[in] dst_size
+ * @param[in] src_region
+ * @return success or failure
+**/
+int allocw_region_migrate(allocw_region_t *dst_region, size_t dst_size, allocw_region_t *src_region)
+{
+ sigset_t set;
+ int retval = 0;
+ int r;
+
+ if (dst_region == NULL || src_region == NULL)
+ {
+ NOTIFY_ERROR("null argument");
+ return EINVAL;
+ }
+
+ NOTIFY_DEBUG("migrating region (%p size:%zu) to (%p size:%zu)",
+ src_region, src_region->size,
+ dst_region, dst_size);
+
+ if (dst_size < (src_region->size + sizeof (struct allocw_block_))
+ && dst_size != src_region->size )
+ {
+ if (src_region->free_end + BLOCK_PTR(src_region, src_region->free_end)->size == src_region->size)
+ {
+ NOTIFY_DEBUG("src_region is shrinkable by 0x%zx bytes",
+ BLOCK_PTR(src_region, src_region->free_end)->size);
+ NOTIFY_DEBUG("but shrinking isn't implemented yet");
+ }
+ NOTIFY_ERROR("destination region too small");
+ return ENOMEM;
+ }
+
+ if ( (r = region_init_(dst_region, dst_size)) )
+ {
+ NOTIFY_DEBUG("%s:%s", "region_init_", "failed");
+ return r;
+ }
+
+ if (region_lock_(dst_region, &set))
+ return -1;
+
+ if (region_lock_(src_region, NULL))
+ {
+ region_unlock_(dst_region, &set);
+ return -1;
+ }
+
+ memmove(&(dst_region->data_), &(src_region->data_), src_region->size);
+ dst_region->free_start = src_region->free_start;
+ dst_region->free_end = src_region->free_end;
+ dst_region->num_alloced = src_region->num_alloced;
+
+ if (dst_size > src_region->size)
+ {
+ struct allocw_block_ *new_free_block = BLOCK_PTR(dst_region, src_region->size);
+ new_free_block->size = dst_region->size - src_region->size;
+ region_freelist_insert_(dst_region, new_free_block);
+ }
+
+ if (region_unlock_(src_region, NULL))
+ retval = -1;
+
+ if (region_unlock_(dst_region, &set))
+ retval = -1;
+
+ return retval;
+}
+
+
+#ifdef TEST
+/** freelist_stats
+ * debugging function - info on freelist
+**/
+static
+void freelist_stats(allocw_region_t *region)
+{
+ struct allocw_block_ *b,
+ *b_largest = NULL,
+ *b_smallest = NULL;
+
+ for ( b = BLOCK_PTR(region, region->free_start);
+ (void *)b != (void *)region;
+ b = BLOCK_PTR(region, b->next_off) )
+ {
+ if (b_smallest == NULL
+ || b->size < b_smallest->size)
+ b_smallest = b;
+
+ if (b_largest == NULL
+ || b->size > b_largest->size)
+ b_largest = b;
+ }
+
+ if (b_smallest)
+ fprintf(stderr, "\t%s block: %p (size:0x%zx)\n", "smallest", b_smallest, b_smallest->size);
+ if (b_largest)
+ fprintf(stderr, "\t%s block: %p (size:0x%zx)\n", "largest", b_largest, b_largest->size);
+
+ b = BLOCK_PTR(region, region->free_end);
+ if ((void *)b != (void *)region)
+ fprintf(stderr, "\t%s block: %p (size:0x%zx)\n",
+ (BLOCK_OFF(region, b) + b->size == region->size) ? "wilderness" : "last",
+ b, b->size);
+}
+
+/** freelist_walk
+ * debugging function - iterates over a region's freelist
+**/
+static
+void freelist_walk(allocw_region_t *region)
+{
+ sigset_t set;
+ struct allocw_block_ *block;
+
+ if (region_lock_(region, &set))
+ return;
+
+#ifdef PRINT_DEBUG
+ dump_region("region", region);
+#endif /* PRINT_DEBUG */
+
+ for ( block = BLOCK_PTR(region, region->free_start);
+ (void *)block != (void *)region;
+ block = BLOCK_PTR(region, block->next_off)
+ )
+ {
+#ifdef PRINT_DEBUG
+ dump_block("block", block);
+#endif /* PRINT_DEBUG */
+ }
+
+ if (region_unlock_(region, &set))
+ return;
+}
+
+
+/** main
+ * for testing
+**/
+int main(int argc UNUSED, char **argv UNUSED)
+{
+ int r;
+ allocw_region_t *region, *region_two;
+ size_t region_sz;
+ allocw_id_t ptr[10];
+ size_t i;
+
+ region_sz = 1024;
+ region = malloc(region_sz);
+ region_two = malloc(region_sz * 2);
+ if (region == NULL
+ || region_two == NULL)
+ {
+ NOTIFY_ERROR("%s:%s", "malloc", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if ( (r = allocw_region_init(region, region_sz)) )
+ {
+ NOTIFY_ERROR("%s:%d", "allocw_region_init", r);
+ exit(EXIT_FAILURE);
+ }
+
+ freelist_walk(region);
+
+ for (i = 0; i < (sizeof ptr / sizeof *ptr); i++)
+ {
+ ptr[i] = allocw_malloc(region, 20);
+ if (ptr == NULL)
+ {
+ NOTIFY_ERROR("%s:%s(%d)", "allocw_malloc", strerror(errno), errno);
+ exit(EXIT_FAILURE);
+ }
+ fprintf(stderr, "allocated id:%zu (%p)\n", ptr[i], allocw_ptr(region, ptr[i]));
+ }
+
+ fprintf(stderr, "dumping pre-migration freelist...\n");
+ freelist_walk(region);
+ freelist_stats(region);
+
+ r = allocw_region_migrate(region_two, region_sz * 2, region);
+ if (r)
+ {
+ NOTIFY_ERROR("%s:%s(%d)", "allocw_region_migrate", strerror(r), r);
+ exit(EXIT_FAILURE);
+ }
+
+ r = allocw_region_fini(region);
+ if (r)
+ {
+ NOTIFY_ERROR("%s:%s(%d)", "allocw_region_fini", strerror(r), r);
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stderr, "dumping post-migration freelist...\n");
+ freelist_walk(region_two);
+ freelist_stats(region_two);
+
+ for (i = 0; i < (sizeof ptr / sizeof *ptr); i++)
+ {
+ allocw_free(region_two, ptr[i]);
+ fprintf(stderr, "freed id:%zu (%p)\n", ptr[i], allocw_ptr(region_two, ptr[i]));
+ }
+
+ freelist_walk(region_two);
+ freelist_stats(region_two);
+
+ region = region_two;
+
+ ptr[0] = allocw_realloc(region, 0, 100);
+ fprintf(stderr, "\t%zu\n", ptr[0]);
+ ptr[1] = allocw_realloc(region, ptr[0], 200);
+ fprintf(stderr, "\t%zu\n", ptr[1]);
+ ptr[2] = allocw_realloc(region, ptr[1], 300);
+ fprintf(stderr, "\t%zu\n", ptr[2]);
+ ptr[3] = allocw_realloc(region, ptr[2], 0);
+ fprintf(stderr, "\t%zu\n", ptr[3]);
+
+ freelist_walk(region);
+ freelist_stats(region);
+
+ exit(EXIT_SUCCESS);
+}
+#endif /* TEST */