0a3d342011-11-10Arne Goedeke #include <stdlib.h>
a5e1b82011-11-09Arne Goedeke #include "bitvector.h"
0a3d342011-11-10Arne Goedeke #include "pike_error.h"
a5e1b82011-11-09Arne Goedeke  struct ba_page { char * data;
0a3d342011-11-10Arne Goedeke  uint16_t free;
a5e1b82011-11-09Arne Goedeke  uintptr_t mask[1];
0a3d342011-11-10Arne Goedeke }; #include "block_allocator.h"
a5e1b82011-11-09Arne Goedeke  #define BA_DIVIDE(a, b) ((a) / (b) + !!((a) % (b))) #define BA_PAGESIZE(a) ((a)->blocks * (a)->block_size) #define BA_MASK_NUM(a) BA_DIVIDE((a)->blocks, sizeof(uintptr_t)*8) #define BA_SPAGE_SIZE(a) (sizeof(struct ba_page) + (BA_MASK_NUM(a) - 1)*sizeof(uintptr_t)) #define BA_HASH_MASK(a) ((1 << (a->allocated + 1)) - 1)
0a3d342011-11-10Arne Goedeke #define BA_CHECK_PTR(a, p, ptr) ((char*)ptr >= p->data && ((uintptr_t)(p->data) + BA_PAGESIZE(a) > (uintptr_t)ptr))
f25b0b2011-11-10Arne Goedeke #define BA_PAGE(a, n) ((ba_page)((char*)a->pages + (n) * BA_SPAGE_SIZE(a)))
a5e1b82011-11-09Arne Goedeke  void ba_init(struct block_allocator * a,
0a3d342011-11-10Arne Goedeke  uint32_t block_size, uint32_t blocks) { uint32_t page_size = block_size * blocks;
a5e1b82011-11-09Arne Goedeke  a->free = 0;
0a3d342011-11-10Arne Goedeke  if ((page_size & (page_size - 1)) == 0)
a5e1b82011-11-09Arne Goedeke  a->magnitude = (uint16_t)clz32(page_size); else a->magnitude = (uint16_t)clz32(round_up32(page_size));
0a3d342011-11-10Arne Goedeke  a->block_size = block_size;
a5e1b82011-11-09Arne Goedeke  a->blocks = blocks; a->num_pages = 0; a->allocated = 5;
f25b0b2011-11-10Arne Goedeke  a->pages = malloc(( BA_SPAGE_SIZE(a) + 2 * sizeof(uint16_t))
a5e1b82011-11-09Arne Goedeke  * (1 << a->allocated)); if (!a->pages) { Pike_error("no mem"); }
f25b0b2011-11-10Arne Goedeke  a->htable = (uint16_t*) BA_PAGE(a, (1 << a->allocated)); memset(a->pages, 0,
a5e1b82011-11-09Arne Goedeke  ( BA_SPAGE_SIZE(a) + 2 * sizeof(uint16_t)) * (1 << a->allocated)); } static inline uint16_t mix(uintptr_t t) { t ^= (t >> 20) ^ (t >> 12); return (uint16_t)(t ^ (t >> 7) ^ (t >> 4)); } static inline uint16_t hash1(struct block_allocator * a, void * ptr) { register uintptr_t t = ((uintptr_t)ptr) >> a->magnitude; return mix(t); } static inline uint16_t hash2(struct block_allocator * a, void * ptr) { register uintptr_t t = ((uintptr_t)ptr) >> a->magnitude; return mix(t+1); } /* * insert the pointer to an allocated page into the * hashtable. uses linear probing and open allocation. */ static inline void ba_htable_insert(struct block_allocator * a, void * ptr, uint16_t n) {
0a3d342011-11-10Arne Goedeke  uint16_t hval = hash1(a, ptr);
a5e1b82011-11-09Arne Goedeke  while (a->htable[hval & BA_HASH_MASK(a)]) hval++; a->htable[hval & BA_HASH_MASK(a)] = n; } static inline uint16_t ba_htable_lookup(struct block_allocator * a, void * ptr) { uint16_t hval; uint16_t n;
0a3d342011-11-10Arne Goedeke  ba_page p;
a5e1b82011-11-09Arne Goedeke 
0a3d342011-11-10Arne Goedeke  hval = hash1(a, ptr);
a5e1b82011-11-09Arne Goedeke  while ((n = a->htable[hval & BA_HASH_MASK(a)])) {
f25b0b2011-11-10Arne Goedeke  p = BA_PAGE(a, n-1);
a5e1b82011-11-09Arne Goedeke  if (BA_CHECK_PTR(a, p, ptr)) { return n; } }
0a3d342011-11-10Arne Goedeke  hval = hash2(a, ptr);
a5e1b82011-11-09Arne Goedeke  while ((n = a->htable[hval & BA_HASH_MASK(a)])) {
f25b0b2011-11-10Arne Goedeke  p = BA_PAGE(a, n-1);
a5e1b82011-11-09Arne Goedeke  if (BA_CHECK_PTR(a, p, ptr)) { return n; } } return 0; } void * ba_alloc(struct block_allocator * a) { ba_page p; size_t i, j; if (a->free == 0) {
f25b0b2011-11-10Arne Goedeke  p = BA_PAGE(a, a->num_pages++);
a5e1b82011-11-09Arne Goedeke  p->data = malloc(BA_PAGESIZE(a)); if (!p->data) { Pike_error("no mem"); } memset((void*)p->mask, 0xff, BA_MASK_NUM(a)*sizeof(uintptr_t));
0a3d342011-11-10Arne Goedeke  p->free = 0;
a5e1b82011-11-09Arne Goedeke  a->free = a->num_pages;
0a3d342011-11-10Arne Goedeke  ba_htable_insert(a, (void*)((char*)p->data + BA_PAGESIZE(a) - 1), a->num_pages);
a5e1b82011-11-09Arne Goedeke  p->mask[0] = ~TBITMASK(uintptr_t, 0); return p->data; } else {
f25b0b2011-11-10Arne Goedeke  p = BA_PAGE(a, a->free - 1);
a5e1b82011-11-09Arne Goedeke 
f25b0b2011-11-10Arne Goedeke  for (j = -1, i = 0; i < BA_MASK_NUM(a); i++) {
a5e1b82011-11-09Arne Goedeke  uintptr_t m = p->mask[i]; if (m) { if (sizeof(uintptr_t) == 8) j = ctz64(m); else j = ctz32(m);
0a3d342011-11-10Arne Goedeke  p->mask[i] ^= TBITMASK(uintptr_t, j);
a5e1b82011-11-09Arne Goedeke  break; } }
f25b0b2011-11-10Arne Goedeke  if (j == -1) { Pike_error("This should not happen!\n"); }
a5e1b82011-11-09Arne Goedeke  // now empty. if (p->mask[i] == 0 && i == BA_MASK_NUM(a)-1) { a->free = p->free; p->free = 0; } return (void*)(((char*)p->data) + (i*sizeof(uintptr_t)*8 + j)*a->block_size); } } void ba_free(struct block_allocator * a, void * ptr) { uint16_t n = ba_htable_lookup(a, ptr);
0a3d342011-11-10Arne Goedeke  uintptr_t t; unsigned int mask, bit; ba_page p;
a5e1b82011-11-09Arne Goedeke  if (!n) { Pike_error("Unknown pointer: %p\n", ptr); }
f25b0b2011-11-10Arne Goedeke  p = BA_PAGE(a, n-1);
0a3d342011-11-10Arne Goedeke  t = (uintptr_t)((char*)ptr - p->data)/a->block_size; mask = t / (sizeof(uintptr_t) * 8); bit = t & ((sizeof(uintptr_t)*8) - 1);
a5e1b82011-11-09Arne Goedeke  if (p->mask[mask] & TBITMASK(uintptr_t, bit)) { Pike_error("double free!"); } if (p->mask[mask] == 0) { p->mask[mask] = TBITMASK(uintptr_t, bit); if (a->free == 0) { a->free = n; p->free = 0; return; } if (a->free > n) { p->free = a->free; a->free = n; } else {
f25b0b2011-11-10Arne Goedeke  ba_page tmp = BA_PAGE(a, a->free-1);
a5e1b82011-11-09Arne Goedeke  while (tmp->free && tmp->free < n) {
f25b0b2011-11-10Arne Goedeke  tmp = BA_PAGE(a, tmp->free - 1);
a5e1b82011-11-09Arne Goedeke  } p->free = tmp->free; tmp->free = n; } return; } p->mask[mask] |= ((uintptr_t)1 << bit); for (mask = 0; mask < BA_MASK_NUM(a); mask++) { if (p->mask[mask] != ~((uintptr_t)0)) return; } // TODO: reclaim memory to the system }