|  | @@ -0,0 +1,293 @@
 | 
	
		
			
				|  |  | +/* Copyright 2007 Nick Mathewson */
 | 
	
		
			
				|  |  | +/* See LICENSE for licensing information */
 | 
	
		
			
				|  |  | +/* $Id: /tor/trunk/src/common/util.c 12153 2007-03-12T03:11:12.797278Z nickm  $ */
 | 
	
		
			
				|  |  | +#include <stdlib.h>
 | 
	
		
			
				|  |  | +#include <string.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define MEMPOOL_PRIVATE
 | 
	
		
			
				|  |  | +#include "mempool.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* DRAWBACKS:
 | 
	
		
			
				|  |  | + *   - Not even slightly threadsafe.
 | 
	
		
			
				|  |  | + *   - Likes to have lots of items per chunks.
 | 
	
		
			
				|  |  | + *   - One pointer overhead per allocated thing.  (The alternative is
 | 
	
		
			
				|  |  | + *     something like glib's use of an RB-tree to keep track of what
 | 
	
		
			
				|  |  | + *     chunk any given piece of memory is in.)
 | 
	
		
			
				|  |  | + *   - Only aligns allocated things to void* level: redefign ALIGNMENT_TYPE
 | 
	
		
			
				|  |  | + *     if you need doubles.
 | 
	
		
			
				|  |  | + *   - Could probably be optimized a bit; the representation contains
 | 
	
		
			
				|  |  | + *     a bit more info than it really needs to have.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* NOTES:
 | 
	
		
			
				|  |  | + *   - The algorithm is similar to the one used by Python, but assumes that
 | 
	
		
			
				|  |  | + *     we'll know in advance which objects we want to pool, and doesn't
 | 
	
		
			
				|  |  | + *     try to handle a zillion objects of weird different sizes.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if 1
 | 
	
		
			
				|  |  | +/* Tor dependencies */
 | 
	
		
			
				|  |  | +#include "orconfig.h"
 | 
	
		
			
				|  |  | +#include "util.h"
 | 
	
		
			
				|  |  | +#include "compat.h"
 | 
	
		
			
				|  |  | +#include "log.h"
 | 
	
		
			
				|  |  | +#define ALLOC(x) tor_malloc(x)
 | 
	
		
			
				|  |  | +#define FREE(x) tor_free(x)
 | 
	
		
			
				|  |  | +#define ASSERT(x) tor_assert(x)
 | 
	
		
			
				|  |  | +/* End Tor dependencies */
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +#include <assert.h>
 | 
	
		
			
				|  |  | +#define PREDICT_UNLIKELY(x) (x)
 | 
	
		
			
				|  |  | +#define PREDICT_LIKELY(x) (x)
 | 
	
		
			
				|  |  | +#define ALLOC(x) malloc(x)
 | 
	
		
			
				|  |  | +#define FREE(x) free(x)
 | 
	
		
			
				|  |  | +#define STRUCT_OFFSET(tp, member)                       \
 | 
	
		
			
				|  |  | +  ((off_t) (((char*)&((tp*)0)->member)-(char*)0))
 | 
	
		
			
				|  |  | +#define ASSERT(x) assert(x)
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Tuning parameters */
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +#define ALIGNMENT_TYPE void *
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +#define ALIGNMENT sizeof(void*)
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +#define MAX_CHUNK (8*(1L<<20))
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +#define MIN_CHUNK 4096
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +struct mp_allocated_t {
 | 
	
		
			
				|  |  | +  mp_chunk_t *in_chunk;
 | 
	
		
			
				|  |  | +  union {
 | 
	
		
			
				|  |  | +    mp_allocated_t *next_free;
 | 
	
		
			
				|  |  | +    char mem[1];
 | 
	
		
			
				|  |  | +    ALIGNMENT_TYPE _dummy;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +#define MP_CHUNK_MAGIC 0x09870123
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +#define CHUNK_OVERHEAD (sizeof(mp_chunk_t)-1)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +#define A2M(a) (&(a)->mem)
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +#define M2A(p) ( ((char*)p) - STRUCT_OFFSET(mp_chunk_t, mem) )
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* INVARIANT: every chunk can hold 2 or more items. */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +static mp_chunk_t *
 | 
	
		
			
				|  |  | +mp_chunk_new(mp_pool_t *pool)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  size_t sz = pool->new_chunk_capacity * pool->item_alloc_size;
 | 
	
		
			
				|  |  | +  mp_chunk_t *chunk = ALLOC(CHUNK_OVERHEAD + sz);
 | 
	
		
			
				|  |  | +  memset(chunk, 0, sizeof(mp_chunk_t)); /* Doesn't clear the whole thing. */
 | 
	
		
			
				|  |  | +  chunk->magic = MP_CHUNK_MAGIC;
 | 
	
		
			
				|  |  | +  chunk->capacity = pool->new_chunk_capacity;
 | 
	
		
			
				|  |  | +  chunk->mem_size = sz;
 | 
	
		
			
				|  |  | +  chunk->next_mem = chunk->mem;
 | 
	
		
			
				|  |  | +  chunk->pool = pool;
 | 
	
		
			
				|  |  | +  return chunk;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +void *
 | 
	
		
			
				|  |  | +mp_pool_get(mp_pool_t *pool)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  mp_chunk_t *chunk;
 | 
	
		
			
				|  |  | +  mp_allocated_t *allocated;
 | 
	
		
			
				|  |  | +  if (PREDICT_LIKELY(pool->used_chunks != NULL)) {
 | 
	
		
			
				|  |  | +    chunk = pool->used_chunks;
 | 
	
		
			
				|  |  | +  } else if (pool->empty_chunks) {
 | 
	
		
			
				|  |  | +    /* Put the most recently emptied chunk on the used list. */
 | 
	
		
			
				|  |  | +    chunk = pool->empty_chunks;
 | 
	
		
			
				|  |  | +    pool->empty_chunks = chunk->next;
 | 
	
		
			
				|  |  | +    if (chunk->next)
 | 
	
		
			
				|  |  | +      chunk->next->prev = NULL;
 | 
	
		
			
				|  |  | +    chunk->next = pool->used_chunks;
 | 
	
		
			
				|  |  | +    if (chunk->next)
 | 
	
		
			
				|  |  | +      chunk->next->prev = chunk;
 | 
	
		
			
				|  |  | +    pool->used_chunks = chunk;
 | 
	
		
			
				|  |  | +    ASSERT(!chunk->prev);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    /* Allocate a new chunk and add it to the used list. */
 | 
	
		
			
				|  |  | +    chunk = mp_chunk_new(pool);
 | 
	
		
			
				|  |  | +    chunk->next = pool->used_chunks;
 | 
	
		
			
				|  |  | +    if (chunk->next)
 | 
	
		
			
				|  |  | +      chunk->next->prev = chunk;
 | 
	
		
			
				|  |  | +    pool->used_chunks = chunk;
 | 
	
		
			
				|  |  | +    ASSERT(!chunk->prev);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ASSERT(chunk->n_allocated < chunk->capacity);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (chunk->first_free) {
 | 
	
		
			
				|  |  | +    allocated = chunk->first_free;
 | 
	
		
			
				|  |  | +    chunk->first_free = allocated->next_free;
 | 
	
		
			
				|  |  | +    allocated->next_free = NULL; /* debugging */
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    ASSERT(chunk->next_mem + pool->item_alloc_size <
 | 
	
		
			
				|  |  | +           chunk->mem + chunk->mem_size);
 | 
	
		
			
				|  |  | +    allocated = (void*)chunk->next_mem;
 | 
	
		
			
				|  |  | +    chunk->next_mem += pool->item_alloc_size;
 | 
	
		
			
				|  |  | +    allocated->in_chunk = chunk;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ++chunk->n_allocated;
 | 
	
		
			
				|  |  | +  if (PREDICT_UNLIKELY(chunk->n_allocated == chunk->capacity)) {
 | 
	
		
			
				|  |  | +    /* This is now a full chunk. */
 | 
	
		
			
				|  |  | +    ASSERT(chunk == pool->used_chunks);
 | 
	
		
			
				|  |  | +    ASSERT(chunk->prev == NULL);
 | 
	
		
			
				|  |  | +    pool->used_chunks = chunk->next;
 | 
	
		
			
				|  |  | +    if (chunk->next)
 | 
	
		
			
				|  |  | +      chunk->next->prev = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    chunk->next = pool->full_chunks;
 | 
	
		
			
				|  |  | +    pool->full_chunks->prev = chunk;
 | 
	
		
			
				|  |  | +    pool->full_chunks = chunk;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return A2M(allocated);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +void
 | 
	
		
			
				|  |  | +mp_pool_release(void *_item)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  mp_allocated_t *allocated = (void*) M2A(_item);
 | 
	
		
			
				|  |  | +  mp_chunk_t *chunk = allocated->in_chunk;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ASSERT(chunk);
 | 
	
		
			
				|  |  | +  ASSERT(chunk->magic == MP_CHUNK_MAGIC);
 | 
	
		
			
				|  |  | +  ASSERT(chunk->n_allocated > 0);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  allocated->next_free = chunk->first_free;
 | 
	
		
			
				|  |  | +  chunk->first_free = allocated;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (PREDICT_UNLIKELY(chunk->n_allocated == chunk->capacity)) {
 | 
	
		
			
				|  |  | +    /* This chunk was full and is about to be used. */
 | 
	
		
			
				|  |  | +    mp_pool_t *pool = chunk->pool;
 | 
	
		
			
				|  |  | +    /* unlink from full */
 | 
	
		
			
				|  |  | +    if (chunk->prev)
 | 
	
		
			
				|  |  | +      chunk->prev->next = chunk->next;
 | 
	
		
			
				|  |  | +    if (chunk->next)
 | 
	
		
			
				|  |  | +      chunk->next->prev = chunk->prev;
 | 
	
		
			
				|  |  | +    if (chunk == pool->full_chunks)
 | 
	
		
			
				|  |  | +      pool->full_chunks = chunk->next;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* link to used */
 | 
	
		
			
				|  |  | +    chunk->next = pool->used_chunks;
 | 
	
		
			
				|  |  | +    chunk->prev = NULL;
 | 
	
		
			
				|  |  | +    if (chunk->next)
 | 
	
		
			
				|  |  | +      chunk->next->prev = chunk;
 | 
	
		
			
				|  |  | +    pool->used_chunks = chunk;
 | 
	
		
			
				|  |  | +  } else if (PREDICT_UNLIKELY(chunk->n_allocated == 1)) {
 | 
	
		
			
				|  |  | +    /* This was used and is about to be empty. */
 | 
	
		
			
				|  |  | +    mp_pool_t *pool = chunk->pool;
 | 
	
		
			
				|  |  | +    /* unlink from used */
 | 
	
		
			
				|  |  | +    if (chunk->prev)
 | 
	
		
			
				|  |  | +      chunk->prev->next = chunk->next;
 | 
	
		
			
				|  |  | +    if (chunk->next)
 | 
	
		
			
				|  |  | +      chunk->next->prev = chunk->prev;
 | 
	
		
			
				|  |  | +    if (chunk == pool->used_chunks)
 | 
	
		
			
				|  |  | +      pool->used_chunks = chunk->next;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* link to empty */
 | 
	
		
			
				|  |  | +    chunk->next = pool->empty_chunks;
 | 
	
		
			
				|  |  | +    chunk->prev = NULL;
 | 
	
		
			
				|  |  | +    if (chunk->next)
 | 
	
		
			
				|  |  | +      chunk->next->prev = chunk;
 | 
	
		
			
				|  |  | +    pool->empty_chunks = chunk;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* reset guts to defragment this chunk. */
 | 
	
		
			
				|  |  | +    chunk->first_free = NULL;
 | 
	
		
			
				|  |  | +    chunk->next_mem = chunk->mem;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    ++pool->n_empty_chunks;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  --chunk->n_allocated;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +mp_pool_t *
 | 
	
		
			
				|  |  | +mp_pool_new(size_t item_size, size_t chunk_capacity)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  mp_pool_t *pool;
 | 
	
		
			
				|  |  | +  size_t alloc_size;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  pool = ALLOC(sizeof(mp_pool_t));
 | 
	
		
			
				|  |  | +  memset(pool, 0, sizeof(mp_pool_t));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* First, minimal size with overhead. */
 | 
	
		
			
				|  |  | +  alloc_size = STRUCT_OFFSET(mp_allocated_t, mem) + item_size;
 | 
	
		
			
				|  |  | +  if (alloc_size < sizeof(mp_allocated_t))
 | 
	
		
			
				|  |  | +    alloc_size = sizeof(mp_allocated_t);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Then, round up to alignment. */
 | 
	
		
			
				|  |  | +  if (alloc_size % ALIGNMENT) {
 | 
	
		
			
				|  |  | +    alloc_size = alloc_size + ALIGNMENT - (alloc_size % ALIGNMENT);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (alloc_size < ALIGNMENT)
 | 
	
		
			
				|  |  | +    alloc_size = ALIGNMENT;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ASSERT((alloc_size % ALIGNMENT) == 0);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (chunk_capacity > MAX_CHUNK)
 | 
	
		
			
				|  |  | +    chunk_capacity = MAX_CHUNK;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (chunk_capacity < alloc_size * 2 + CHUNK_OVERHEAD)
 | 
	
		
			
				|  |  | +    chunk_capacity = alloc_size * 2 + CHUNK_OVERHEAD;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (chunk_capacity < MIN_CHUNK) /* Guess system page size. */
 | 
	
		
			
				|  |  | +    chunk_capacity = MIN_CHUNK;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  pool->new_chunk_capacity = (chunk_capacity-CHUNK_OVERHEAD / alloc_size);
 | 
	
		
			
				|  |  | +  pool->item_alloc_size = alloc_size;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return pool;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +void
 | 
	
		
			
				|  |  | +mp_pool_clean(mp_pool_t *pool)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  if (pool->empty_chunks) {
 | 
	
		
			
				|  |  | +    mp_chunk_t *next, *chunk = pool->empty_chunks->next;
 | 
	
		
			
				|  |  | +    while (chunk) {
 | 
	
		
			
				|  |  | +      next = chunk->next;
 | 
	
		
			
				|  |  | +      FREE(chunk);
 | 
	
		
			
				|  |  | +      chunk = next;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    pool->empty_chunks->next = NULL;
 | 
	
		
			
				|  |  | +    pool->n_empty_chunks = 1;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +destroy_chunks(mp_chunk_t *chunk)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  mp_chunk_t *next;
 | 
	
		
			
				|  |  | +  while (chunk) {
 | 
	
		
			
				|  |  | +    chunk->magic = 0xd3adb33f;
 | 
	
		
			
				|  |  | +    next = chunk->next;
 | 
	
		
			
				|  |  | +    FREE(chunk);
 | 
	
		
			
				|  |  | +    chunk = next;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** DOCDOC */
 | 
	
		
			
				|  |  | +void
 | 
	
		
			
				|  |  | +mp_pool_destroy(mp_pool_t *pool)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  destroy_chunks(pool->empty_chunks);
 | 
	
		
			
				|  |  | +  destroy_chunks(pool->used_chunks);
 | 
	
		
			
				|  |  | +  destroy_chunks(pool->full_chunks);
 | 
	
		
			
				|  |  | +  memset(pool, 0xe0, sizeof(mp_pool_t));
 | 
	
		
			
				|  |  | +  FREE(pool);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 |