collections_map.in.c [plain text]
#include <sys/types.h>
#include "collections_utilities.h"
#define opaque_os_map_t IN_MAP(,_t)
#define os_map_hash IN_MAP(,_hash)
#define os_map_key_equals IN_MAP(,_key_equals)
#ifndef _COLLETIONS_MAP_HELPERS_C
#define _COLLETIONS_MAP_HELPERS_C
#define _MAP_MAX_FILL_NUMER 4
#define _MAP_MAX_FILL_DENOM 5
#define _MAP_GROW_SHIFT_RATE 8
#define _MAP_MIN_FILL_DENOM 8
OS_ALWAYS_INLINE
static inline uint32_t
map_next(uint32_t i, uint32_t size)
{
i++;
return i >= size ? 0 : i;
}
OS_ALWAYS_INLINE
static inline uint32_t
map_prev(uint32_t i, uint32_t size)
{
return ((i != 0) ? i : size) - 1;
}
struct _os_map_internal_struct {
void *data;
uint32_t count;
uint32_t size;
uint16_t grow_shift;
uint16_t max_bucket_offset;
};
#endif
#define _os_map_t IN_MAP(_,_t)
typedef struct _os_map_internal_struct _os_map_t;
#define _os_map_data_segregated IN_MAP(_,_data_segregated)
struct _os_map_data_segregated {
os_map_key_t *keys;
void **vals;
};
#define _os_map_data_ref_t IN_MAP(_,_data_ref_t)
typedef struct _os_map_data_segregated _os_map_data_ref_t;
#define _alloc_data IN_MAP(_,_alloc_data)
static inline void *
_alloc_data(uint32_t size)
{
assert(size < UINT32_MAX);
void *result = calloc(size, sizeof(os_map_key_t) + sizeof(void *));
assert(result != NULL);
return result;
}
#define _get_data_ref IN_MAP(_,_get_data_ref)
static inline void
_get_data_ref(_os_map_t *m, _os_map_data_ref_t *data)
{
data->keys = (os_map_key_t *)(m->data);
data->vals = (void *)(((char *)(m->data)) +
(m->size * sizeof(os_map_key_t)));
}
#define _free_data IN_MAP(_,_free_data)
static inline void
_free_data(_os_map_data_ref_t *data)
{
free((void *)data->keys);
}
#define _get_key(data, i) data.keys[i]
#define _get_val(data, i) data.vals[i]
#define _set_key(data, i, key) data.keys[i] = key
#define _set_val(data, i, val) data.vals[i] = val
#define _os_map_bucket IN_MAP(_,_probe_bucket)
static inline uint32_t
_os_map_bucket(_os_map_t *m, os_map_key_t key)
{
return os_map_hash(key) % m->size;
}
#define _os_map_bucket_offset IN_MAP(_,_bucket_offset)
static inline uint32_t
_os_map_bucket_offset(_os_map_t *m, os_map_key_t key, uint32_t i)
{
uint32_t bucket = _os_map_bucket(m, key);
return (i - bucket) % m->size;
}
#ifdef DEBUG
#define _get_next_offset IN_MAP(_,_verify_next_offset)
inline int64_t
_get_next_offset(_os_map_t *m, _os_map_data_ref_t data,
uint32_t index, uint32_t size)
{
uint32_t next_index = map_next(index, size);
if (_get_val(data, next_index) == NULL) {
return -1;
}
return _os_map_bucket_offset(m, _get_key(data, next_index),
next_index);
}
#define _os_map_verify IN_MAP(_,_verify)
void
_os_map_verify(_os_map_t *m)
{
_os_map_data_ref_t data;
_get_data_ref(m, &data);
int64_t current_bucket_offset;
int64_t next_bucket_offset;
if (_get_val(data, 0) == NULL) {
next_bucket_offset = -1;
} else {
next_bucket_offset = _os_map_bucket_offset(m,
_get_key(data, 0), 0);
}
uint32_t size = m->size;
for (uint32_t index = 0; index < size; index++){
current_bucket_offset = next_bucket_offset;
next_bucket_offset = _get_next_offset(m, data, index, size);
DEBUG_ASSERT(next_bucket_offset <= current_bucket_offset + 1);
}
}
#else
#define _os_map_verify(A)
#endif
#define _swap_if_needed IN_MAP(_,_swap_if_needed)
static inline void
_swap_if_needed(_os_map_t *m, _os_map_data_ref_t data, os_map_key_t *key,
void **val, uint32_t *bucket_offset, uint32_t i)
{
if (*bucket_offset != 0) {
os_map_key_t loop_key = _get_key(data, i);
DEBUG_ASSERT(!os_map_key_equals(loop_key, *key));
uint32_t loop_bucket_offset = _os_map_bucket_offset(m,
loop_key, i);
if (*bucket_offset > loop_bucket_offset) {
if (*bucket_offset > m->max_bucket_offset) {
assert(*bucket_offset <= UINT16_MAX);
DEBUG_ASSERT(*bucket_offset ==
m->max_bucket_offset + 1);
m->max_bucket_offset = *bucket_offset;
}
void *new_val = _get_val(data, i);
_set_key(data, i, *key);
_set_val(data, i, *val);
*key = loop_key;
*val = new_val;
*bucket_offset = loop_bucket_offset;
}
}
}
#define _os_map_insert_no_rehash IN_MAP(_,_insert_no_rehash)
static void
_os_map_insert_no_rehash(_os_map_t *m, os_map_key_t key, void *val)
{
uint32_t size = m->size, loop_limit = m->size;
uint32_t i = os_map_hash(key) % size;
_os_map_data_ref_t data;
_get_data_ref(m, &data);
uint32_t bucket_offset = 0;
for (;;) {
DEBUG_ASSERT(bucket_offset ==
_os_map_bucket_offset(m, key, i));
assert(loop_limit-- != 0);
void *loop_val = _get_val(data, i);
if (loop_val == NULL) {
break;
}
_swap_if_needed(m, data, &key, &val, &bucket_offset, i);
bucket_offset++;
i = map_next(i, size);
}
DEBUG_ASSERT(_get_val(data, i) == NULL);
if (bucket_offset > m->max_bucket_offset) {
assert(bucket_offset <= UINT16_MAX);
DEBUG_ASSERT(bucket_offset == m->max_bucket_offset + 1);
m->max_bucket_offset = bucket_offset;
}
_set_key(data, i, key);
_set_val(data, i, val);
m->count++;
}
#define _os_map_rehash IN_MAP(_,_rehash)
static void
_os_map_rehash(_os_map_t *m, int direction)
{
_os_map_verify(m);
uint32_t old_size = m->size;
_os_map_data_ref_t old_data;
_get_data_ref(m, &old_data);
if (direction > 0) {
m->size += (1 << m->grow_shift);
if (m->size ==
((uint32_t)_MAP_GROW_SHIFT_RATE << m->grow_shift)) {
m->grow_shift++;
}
} else if (direction < 0) {
if (m->grow_shift > MAP_MINSHIFT) {
m->grow_shift--;
}
m->size = roundup(m->size / 2, (1 << m->grow_shift));
}
m->count = 0;
m->max_bucket_offset = 0;
m->data = _alloc_data(m->size);
assert(m->data);
for (uint32_t i = 0; i < old_size; i++) {
if (_get_val(old_data, i) == NULL) {
continue;
}
_os_map_insert_no_rehash(m, _get_key(old_data, i),
_get_val(old_data, i));
}
_free_data(&old_data);
_os_map_verify(m);
}
#define _os_map_find_helper_empty_key IN_MAP(_,_find_helper_empty_key)
#define _os_map_find_helper IN_MAP(_,_find_helper)
static inline void *
_os_map_find_helper(_os_map_t *m, os_map_key_t key, uint32_t *i)
{
if (m->count == 0) {
return NULL;
}
uint32_t size = m->size, loop_limit = m->size;
_os_map_data_ref_t data;
_get_data_ref(m, &data);
*i = _os_map_bucket(m, key);
uint32_t bucket_offset = 0;
for (;;) {
assert(loop_limit-- != 0);
if (bucket_offset > m->max_bucket_offset ||
_get_val(data, *i) == NULL) {
return NULL;
}
os_map_key_t loop_key = _get_key(data, *i);
if (os_map_key_equals(key, loop_key)) {
return _get_val(data, *i);
}
*i = map_next(*i, size);
bucket_offset++;
}
}
#define _os_map_remove_entry IN_MAP(_,_remove_entry)
void
_os_map_remove_entry(_os_map_t *m, uint32_t current_index)
{
_os_map_data_ref_t data;
_get_data_ref(m, &data);
uint32_t size = m->size;
uint32_t next_index = map_next(current_index, size);
void *next_val = _get_val(data, next_index);
os_map_key_t next_key = _get_key(data, next_index);
while(next_val != NULL &&
_os_map_bucket(m, next_key) != next_index) {
_set_key(data, current_index, next_key);
_set_val(data, current_index, next_val);
DEBUG_ASSERT(_os_map_bucket_offset(m,
next_key, current_index) <
_os_map_bucket_offset(m, next_key,
next_index));
current_index = next_index;
next_index = map_next(current_index, size);
next_val = _get_val(data, next_index);
next_key = _get_key(data, next_index);
}
_set_val(data, current_index, NULL);
m->count--;
if ((m->size >= MAP_MINSIZE * 2) &&
(m->count < m->size / _MAP_MIN_FILL_DENOM)) {
_os_map_rehash(m, -1);
}
}
#define os_map_init IN_MAP(,_init)
void
os_map_init(opaque_os_map_t *m_raw, os_map_config_t *config,
__unused int struct_version)
{
static_assert(sizeof(opaque_os_map_t) == sizeof(_os_map_t),
"Opaque string map incorrect size");
_os_map_t *m = (_os_map_t *)m_raw;
if (config) {
m->size = MAX(config->initial_size, MAP_MINSIZE);
} else {
m->size = MAP_MINSIZE;
}
m->count = 0;
m->max_bucket_offset = 0;
m->data = _alloc_data(m->size);
assert(m->data != NULL);
m->grow_shift = MAP_MINSHIFT;
DEBUG_ASSERT_MAP_INVARIANTS(m);
}
#define os_map_destroy IN_MAP(,_destroy)
void
os_map_destroy(opaque_os_map_t *m_raw)
{
_os_map_t *m = (_os_map_t *)m_raw;
free(m->data);
m->data = NULL;
m->size = 0;
}
#define os_map_insert IN_MAP(,_insert)
void
os_map_insert(opaque_os_map_t *m_raw, os_map_key_t key, void *val)
{
_os_map_t *m = (_os_map_str_t *)m_raw;
assert(val != NULL);
if (m->count >= _MAP_MAX_FILL_NUMER * m->size /
_MAP_MAX_FILL_DENOM) {
_os_map_rehash(m, 1);
}
_os_map_insert_no_rehash(m, key, val);
DEBUG_ASSERT_MAP_INVARIANTS(m);
}
#define os_map_find IN_MAP(,_find)
void *
os_map_find(opaque_os_map_t *m_raw, os_map_key_t key)
{
_os_map_t *m = (_os_map_t *)m_raw;
uint32_t i;
return _os_map_find_helper(m, key, &i);
}
#define os_map_delete IN_MAP(,_delete)
void *
os_map_delete(opaque_os_map_t *m_raw, os_map_key_t key)
{
_os_map_t *m = (_os_map_t *)m_raw;
uint32_t i;
void *val = _os_map_find_helper(m, key, &i);
if (val == NULL) {
return NULL;
}
_os_map_remove_entry(m, i);
return val;
}
#define os_map_payload_handler_t IN_MAP(,_payload_handler_t)
typedef bool (^os_map_payload_handler_t) (os_map_key_t, void *);
#define os_map_clear IN_MAP(,_clear)
void
os_map_clear(opaque_os_map_t *m_raw,
OS_NOESCAPE os_map_payload_handler_t handler)
{
_os_map_t *m = (_os_map_t *)m_raw;
_os_map_data_ref_t oldData;
_get_data_ref(m, &oldData);
uint32_t oldSize = m->size;
m->count = 0;
m->max_bucket_offset = 0;
m->size = MAP_MINSIZE;
m->data = _alloc_data(m->size);
m->grow_shift = MAP_MINSHIFT;
DEBUG_ASSERT_MAP_INVARIANTS(m);
if (handler != NULL) {
for (uint32_t i = 0; i < oldSize; i++) {
void *val = _get_val(oldData, i);
if (val != NULL) {
handler(_get_key(oldData, i), val);
}
}
}
_free_data(&oldData);
}
#define os_map_count IN_MAP(,_count)
size_t
os_map_count(opaque_os_map_t *m_raw)
{
_os_map_t *m = (_os_map_t *)m_raw;
return m->count;
}
#define os_map_foreach IN_MAP(,_foreach)
void
os_map_foreach(opaque_os_map_t *m_raw,
OS_NOESCAPE os_map_payload_handler_t handler)
{
_os_map_t *m = (_os_map_t *)m_raw;
_os_map_data_ref_t data;
_get_data_ref(m, &data);
for (uint32_t i = 0; i < m->size; i++) {
void *val = _get_val(data, i);
if (val != NULL) {
if (!handler(_get_key(data, i), val)) break;
}
}
}
#ifdef MAP_SUPPORTS_ENTRY
#define os_map_entry IN_MAP(,_entry)
os_map_key_t
os_map_entry(opaque_os_map_t *m_raw, os_map_key_t key)
{
_os_map_t *m = (_os_map_t *)m_raw;
_os_map_data_ref_t data;
_get_data_ref(m, &data);
uint32_t i;
if (_os_map_find_helper(m, key, &i) == NULL) {
return (os_map_key_t)NULL;
}
return _get_key(data, i);
}
#endif