#include "prism/util/pm_constant_pool.h"
void
pm_constant_id_list_init(pm_constant_id_list_t *list) {
list->ids = NULL;
list->size = 0;
list->capacity = 0;
}
void
pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity) {
if (capacity) {
list->ids = xcalloc(capacity, sizeof(pm_constant_id_t));
if (list->ids == NULL) abort();
} else {
list->ids = NULL;
}
list->size = 0;
list->capacity = capacity;
}
bool
pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id) {
if (list->size >= list->capacity) {
list->capacity = list->capacity == 0 ? 8 : list->capacity * 2;
list->ids = (pm_constant_id_t *) xrealloc(list->ids, sizeof(pm_constant_id_t) * list->capacity);
if (list->ids == NULL) return false;
}
list->ids[list->size++] = id;
return true;
}
void
pm_constant_id_list_insert(pm_constant_id_list_t *list, size_t index, pm_constant_id_t id) {
assert(index < list->capacity);
assert(list->ids[index] == PM_CONSTANT_ID_UNSET);
list->ids[index] = id;
list->size++;
}
bool
pm_constant_id_list_includes(pm_constant_id_list_t *list, pm_constant_id_t id) {
for (size_t index = 0; index < list->size; index++) {
if (list->ids[index] == id) return true;
}
return false;
}
void
pm_constant_id_list_free(pm_constant_id_list_t *list) {
if (list->ids != NULL) {
xfree(list->ids);
}
}
static inline uint32_t
pm_constant_pool_hash(const uint8_t *start, size_t length) {
uint32_t value = 5381;
for (size_t index = 0; index < length; index++) {
value = ((value << 5) + value) + start[index];
}
return value;
}
static uint32_t
next_power_of_two(uint32_t v) {
if (v == 0) {
return 1;
}
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
#ifndef NDEBUG
static bool
is_power_of_two(uint32_t size) {
return (size & (size - 1)) == 0;
}
#endif
static inline bool
pm_constant_pool_resize(pm_constant_pool_t *pool) {
assert(is_power_of_two(pool->capacity));
uint32_t next_capacity = pool->capacity * 2;
if (next_capacity < pool->capacity) return false;
const uint32_t mask = next_capacity - 1;
const size_t element_size = sizeof(pm_constant_pool_bucket_t) + sizeof(pm_constant_t);
void *next = xcalloc(next_capacity, element_size);
if (next == NULL) return false;
pm_constant_pool_bucket_t *next_buckets = next;
pm_constant_t *next_constants = (void *)(((char *) next) + next_capacity * sizeof(pm_constant_pool_bucket_t));
for (uint32_t index = 0; index < pool->capacity; index++) {
pm_constant_pool_bucket_t *bucket = &pool->buckets[index];
if (bucket->id != PM_CONSTANT_ID_UNSET) {
uint32_t next_index = bucket->hash & mask;
while (next_buckets[next_index].id != PM_CONSTANT_ID_UNSET) {
next_index = (next_index + 1) & mask;
}
next_buckets[next_index] = *bucket;
}
}
memcpy(next_constants, pool->constants, pool->size * sizeof(pm_constant_t));
xfree(pool->buckets);
pool->constants = next_constants;
pool->buckets = next_buckets;
pool->capacity = next_capacity;
return true;
}
bool
pm_constant_pool_init(pm_constant_pool_t *pool, uint32_t capacity) {
const uint32_t maximum = (~((uint32_t) 0));
if (capacity >= ((maximum / 2) + 1)) return false;
capacity = next_power_of_two(capacity);
const size_t element_size = sizeof(pm_constant_pool_bucket_t) + sizeof(pm_constant_t);
void *memory = xcalloc(capacity, element_size);
if (memory == NULL) return false;
pool->buckets = memory;
pool->constants = (void *)(((char *)memory) + capacity * sizeof(pm_constant_pool_bucket_t));
pool->size = 0;
pool->capacity = capacity;
return true;
}
pm_constant_t *
pm_constant_pool_id_to_constant(const pm_constant_pool_t *pool, pm_constant_id_t constant_id) {
assert(constant_id != PM_CONSTANT_ID_UNSET && constant_id <= pool->size);
return &pool->constants[constant_id - 1];
}
pm_constant_id_t
pm_constant_pool_find(const pm_constant_pool_t *pool, const uint8_t *start, size_t length) {
assert(is_power_of_two(pool->capacity));
const uint32_t mask = pool->capacity - 1;
uint32_t hash = pm_constant_pool_hash(start, length);
uint32_t index = hash & mask;
pm_constant_pool_bucket_t *bucket;
while (bucket = &pool->buckets[index], bucket->id != PM_CONSTANT_ID_UNSET) {
pm_constant_t *constant = &pool->constants[bucket->id - 1];
if ((constant->length == length) && memcmp(constant->start, start, length) == 0) {
return bucket->id;
}
index = (index + 1) & mask;
}
return PM_CONSTANT_ID_UNSET;
}
static inline pm_constant_id_t
pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t length, pm_constant_pool_bucket_type_t type) {
if (pool->size >= (pool->capacity / 4 * 3)) {
if (!pm_constant_pool_resize(pool)) return PM_CONSTANT_ID_UNSET;
}
assert(is_power_of_two(pool->capacity));
const uint32_t mask = pool->capacity - 1;
uint32_t hash = pm_constant_pool_hash(start, length);
uint32_t index = hash & mask;
pm_constant_pool_bucket_t *bucket;
while (bucket = &pool->buckets[index], bucket->id != PM_CONSTANT_ID_UNSET) {
pm_constant_t *constant = &pool->constants[bucket->id - 1];
if ((constant->length == length) && memcmp(constant->start, start, length) == 0) {
if (type == PM_CONSTANT_POOL_BUCKET_OWNED) {
xfree((void *) start);
} else if (bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) {
xfree((void *) constant->start);
constant->start = start;
bucket->type = (unsigned int) (PM_CONSTANT_POOL_BUCKET_DEFAULT & 0x3);
}
return bucket->id;
}
index = (index + 1) & mask;
}
uint32_t id = ++pool->size;
assert(pool->size < ((uint32_t) (1 << 30)));
*bucket = (pm_constant_pool_bucket_t) {
.id = (unsigned int) (id & 0x3fffffff),
.type = (unsigned int) (type & 0x3),
.hash = hash
};
pool->constants[id - 1] = (pm_constant_t) {
.start = start,
.length = length,
};
return id;
}
pm_constant_id_t
pm_constant_pool_insert_shared(pm_constant_pool_t *pool, const uint8_t *start, size_t length) {
return pm_constant_pool_insert(pool, start, length, PM_CONSTANT_POOL_BUCKET_DEFAULT);
}
pm_constant_id_t
pm_constant_pool_insert_owned(pm_constant_pool_t *pool, uint8_t *start, size_t length) {
return pm_constant_pool_insert(pool, start, length, PM_CONSTANT_POOL_BUCKET_OWNED);
}
pm_constant_id_t
pm_constant_pool_insert_constant(pm_constant_pool_t *pool, const uint8_t *start, size_t length) {
return pm_constant_pool_insert(pool, start, length, PM_CONSTANT_POOL_BUCKET_CONSTANT);
}
void
pm_constant_pool_free(pm_constant_pool_t *pool) {
for (uint32_t index = 0; index < pool->capacity; index++) {
pm_constant_pool_bucket_t *bucket = &pool->buckets[index];
if (bucket->id != PM_CONSTANT_ID_UNSET && bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) {
pm_constant_t *constant = &pool->constants[bucket->id - 1];
xfree((void *) constant->start);
}
}
xfree(pool->buckets);
}