mirror of
https://github.com/justinethier/cyclone.git
synced 2025-05-18 13:19:17 +02:00
382 lines
9.9 KiB
C
382 lines
9.9 KiB
C
/**
|
|
* Cyclone Scheme
|
|
* https://github.com/justinethier/cyclone
|
|
*
|
|
* Copyright (c) 2020, Justin Ethier
|
|
* All rights reserved.
|
|
*
|
|
* An optional module that can be used to compile Cyclone when
|
|
* libck is not available. Note there will be a performance impact!
|
|
* It is recommended to compile with libck where possible.
|
|
*/
|
|
|
|
#include "cyclone/types.h"
|
|
#include "cyclone/runtime.h"
|
|
#include "ck-polyfill.h"
|
|
#include <unistd.h>
|
|
|
|
static pthread_mutex_t glock;
|
|
|
|
void ck_polyfill_init()
|
|
{
|
|
// will need to call this as soon as possible, perhaps from main()
|
|
if (pthread_mutex_init(&(glock), NULL) != 0) {
|
|
fprintf(stderr, "Unable to initialize global ck mutex\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
// CK Hashset section
|
|
bool ck_hs_init(ck_hs_t * hs, unsigned int mode, ck_hs_hash_cb_t * hash_func,
|
|
ck_hs_compare_cb_t * cmp, struct ck_malloc *alloc,
|
|
unsigned long capacity, unsigned long seed)
|
|
{
|
|
(*hs).hs = simple_hashset_create();
|
|
if (pthread_mutex_init(&((*hs).lock), NULL) != 0) {
|
|
fprintf(stderr, "Unable to initialize ck hashset mutex\n");
|
|
exit(1);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void *ck_hs_get(ck_hs_t * _hs, unsigned long hash, const void *key)
|
|
{
|
|
void *result = NULL;
|
|
int index = -1;
|
|
simple_hashset_t set = (*_hs).hs;
|
|
|
|
pthread_mutex_lock(&((*_hs).lock));
|
|
|
|
index = simple_hashset_is_member(set, (symbol_type *) key);
|
|
if (index > 0) {
|
|
result = (void *)(set->items[index].item);
|
|
}
|
|
|
|
pthread_mutex_unlock(&((*_hs).lock));
|
|
return result;
|
|
}
|
|
|
|
bool ck_hs_put(ck_hs_t * _hs, unsigned long hash, const void *key)
|
|
{
|
|
bool result = false;
|
|
int rv, index;
|
|
simple_hashset_t hs = (*_hs).hs;
|
|
|
|
pthread_mutex_lock(&((*_hs).lock));
|
|
|
|
//index = simple_hashset_is_member(hs, (symbol_type *)key);
|
|
//if (index == 0) {
|
|
rv = simple_hashset_add(hs, (symbol_type *) key);
|
|
if (rv >= 0) {
|
|
result = true;
|
|
}
|
|
//}
|
|
|
|
pthread_mutex_unlock(&((*_hs).lock));
|
|
return result;
|
|
}
|
|
|
|
// CK Array section
|
|
bool
|
|
ck_array_init(ck_array_t * array, unsigned int mode,
|
|
struct ck_malloc *allocator, unsigned int initial_length)
|
|
{
|
|
(*array).hs = hashset_create();
|
|
if (pthread_mutex_init(&((*array).lock), NULL) != 0) {
|
|
fprintf(stderr, "Unable to initialize ck array mutex\n");
|
|
exit(1);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// DESCRIPTION
|
|
// The ck_array_put_unique(3) function will attempt to insert the value of
|
|
// pointer into the array pointed to by array. This function may incur
|
|
// additional memory allocations if not enough memory has been allocated in
|
|
// the array for a new entry. The operation is also free to apply the opera-
|
|
// tion immediately if there is an opportunity for elimination with a pend-
|
|
// ing (uncommitted) remove operation. The function will not make any modi-
|
|
// fications if the pointer already exists in the array.
|
|
//
|
|
// RETURN VALUES
|
|
// This function returns 1 if the pointer already exists in the array. It
|
|
// returns 0 if the put operation succeeded. It returns -1 on error due to
|
|
// internal memory allocation failures.
|
|
int ck_array_put_unique(ck_array_t * array, void *pointer)
|
|
{
|
|
pthread_mutex_lock(&(array->lock));
|
|
hashset_add(array->hs, pointer);
|
|
pthread_mutex_unlock(&(array->lock));
|
|
return true;
|
|
}
|
|
|
|
// DESCRIPTION
|
|
// The ck_array_remove(3) function will attempt to remove the value of
|
|
// pointer into the array pointed to by array. The operation is also free to
|
|
// apply the operation immediately if there is an opportunity for elimina-
|
|
// tion with a pending (uncommitted) put operation. If no elimination was
|
|
// possible, the function may require to allocate more memory.
|
|
//
|
|
// RETURN VALUES
|
|
// This function returns true if the remove operation succeeded. It will
|
|
// return false otherwise due to internal allocation failures or because the
|
|
// value did not exist.
|
|
bool ck_array_remove(ck_array_t * array, void *pointer)
|
|
{
|
|
pthread_mutex_lock(&(array->lock));
|
|
hashset_remove(array->hs, pointer);
|
|
pthread_mutex_unlock(&(array->lock));
|
|
return true;
|
|
}
|
|
|
|
// DESCRIPTION
|
|
// The ck_array_commit(3) function will commit any pending put or remove
|
|
// operations associated with the array. The function may end up requesting
|
|
// the safe reclamation of memory actively being iterated upon by other
|
|
// threads.
|
|
//
|
|
// RETURN VALUES
|
|
// This function returns true if the commit operation succeeded. It will
|
|
// return false otherwise, and pending operations will not be applied.
|
|
bool ck_array_commit(ck_array_t * array)
|
|
{
|
|
// Nothing to do in this polyfill
|
|
return true;
|
|
}
|
|
|
|
// TODO: global pthread mutex lock for this? obviously not ideal but the
|
|
// whole purpose of this module is a minimal interface for compatibility
|
|
// not speed
|
|
|
|
bool ck_pr_cas_int(int *target, int old_value, int new_value)
|
|
{
|
|
bool result = false;
|
|
pthread_mutex_lock(&glock);
|
|
if (*target == old_value) {
|
|
*target = new_value;
|
|
result = true;
|
|
}
|
|
pthread_mutex_unlock(&glock);
|
|
return result;
|
|
}
|
|
|
|
bool ck_pr_cas_ptr(void *target, void *old_value, void *new_value)
|
|
{
|
|
bool result = false;
|
|
pthread_mutex_lock(&glock);
|
|
if (*(void **)target == old_value) {
|
|
*(void **)target = new_value;
|
|
result = true;
|
|
}
|
|
pthread_mutex_unlock(&glock);
|
|
return result;
|
|
// *(void **)v = set;
|
|
}
|
|
|
|
bool ck_pr_cas_8(uint8_t * target, uint8_t old_value, uint8_t new_value)
|
|
{
|
|
bool result = false;
|
|
pthread_mutex_lock(&glock);
|
|
if (*target == old_value) {
|
|
*target = new_value;
|
|
result = true;
|
|
}
|
|
pthread_mutex_unlock(&glock);
|
|
return result;
|
|
}
|
|
|
|
void ck_pr_add_ptr(void *target, uintptr_t delta)
|
|
{
|
|
pthread_mutex_lock(&glock);
|
|
size_t value = (size_t)target;
|
|
size_t d = (size_t)delta;
|
|
size_t result = value + d;
|
|
*(void **)target = (void *)result;
|
|
// *(void **)v = set;
|
|
pthread_mutex_unlock(&glock);
|
|
}
|
|
|
|
void ck_pr_add_int(int *target, int delta)
|
|
{
|
|
pthread_mutex_lock(&glock);
|
|
(*target) += delta;
|
|
pthread_mutex_unlock(&glock);
|
|
}
|
|
|
|
void ck_pr_add_8(uint8_t * target, uint8_t delta)
|
|
{
|
|
pthread_mutex_lock(&glock);
|
|
(*target) += delta;
|
|
pthread_mutex_unlock(&glock);
|
|
}
|
|
|
|
void *ck_pr_load_ptr(const void *target)
|
|
{
|
|
void *result;
|
|
pthread_mutex_lock(&glock);
|
|
result = *(void **)target;
|
|
pthread_mutex_unlock(&glock);
|
|
return result;
|
|
}
|
|
|
|
int ck_pr_load_int(const int *target)
|
|
{
|
|
int result;
|
|
pthread_mutex_lock(&glock);
|
|
result = *target;
|
|
pthread_mutex_unlock(&glock);
|
|
return result;
|
|
}
|
|
|
|
uint8_t ck_pr_load_8(const uint8_t * target)
|
|
{
|
|
uint8_t result;
|
|
pthread_mutex_lock(&glock);
|
|
result = *target;
|
|
pthread_mutex_unlock(&glock);
|
|
return result;
|
|
}
|
|
|
|
void ck_pr_store_ptr(void *target, void *value)
|
|
{
|
|
pthread_mutex_lock(&glock);
|
|
*(void **)target = value;
|
|
pthread_mutex_unlock(&glock);
|
|
}
|
|
|
|
// Simple hashset
|
|
|
|
static const size_t prime_1 = 73;
|
|
static const size_t prime_2 = 5009;
|
|
|
|
size_t hash_function(const char *str, size_t len)
|
|
{
|
|
unsigned long hash = 5381;
|
|
int c;
|
|
|
|
while (c = *str++) {
|
|
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
simple_hashset_t simple_hashset_create()
|
|
{
|
|
simple_hashset_t set =
|
|
(simple_hashset_t) calloc(1, sizeof(struct simple_hashset_st));
|
|
|
|
if (set == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
set->hash_func = hash_function;
|
|
set->nbits = 3;
|
|
set->capacity = (size_t)(1 << set->nbits);
|
|
set->mask = set->capacity - 1;
|
|
set->items =
|
|
(struct simple_hashset_item_st *)calloc(set->capacity,
|
|
sizeof(struct
|
|
simple_hashset_item_st));
|
|
if (set->items == NULL) {
|
|
simple_hashset_destroy(set);
|
|
return NULL;
|
|
}
|
|
set->nitems = 0;
|
|
set->n_deleted_items = 0;
|
|
return set;
|
|
}
|
|
|
|
void simple_hashset_destroy(simple_hashset_t set)
|
|
{
|
|
if (set) {
|
|
free(set->items);
|
|
}
|
|
free(set);
|
|
}
|
|
|
|
void simple_hashset_set_hash_function(simple_hashset_t set, hash_func_t func)
|
|
{
|
|
set->hash_func = func;
|
|
}
|
|
|
|
static int simple_hashset_add_member(simple_hashset_t set, symbol_type * key,
|
|
size_t hash)
|
|
{
|
|
size_t index;
|
|
|
|
if (hash < 2) {
|
|
return -1;
|
|
}
|
|
|
|
index = set->mask & (prime_1 * hash);
|
|
|
|
while (set->items[index].hash != 0 && set->items[index].hash != 1) {
|
|
if (set->items[index].hash == hash) {
|
|
return 0;
|
|
} else {
|
|
/* search free slot */
|
|
index = set->mask & (index + prime_2);
|
|
}
|
|
}
|
|
|
|
++set->nitems;
|
|
if (set->items[index].hash == 1) {
|
|
--set->n_deleted_items;
|
|
}
|
|
|
|
set->items[index].hash = hash;
|
|
set->items[index].item = key;
|
|
return 1;
|
|
}
|
|
|
|
static void set_maybe_rehash(simple_hashset_t set)
|
|
{
|
|
struct simple_hashset_item_st *old_items;
|
|
size_t old_capacity, index;
|
|
|
|
if (set->nitems + set->n_deleted_items >= (double)set->capacity * 0.85) {
|
|
old_items = set->items;
|
|
old_capacity = set->capacity;
|
|
++set->nbits;
|
|
set->capacity = (size_t)(1 << set->nbits);
|
|
set->mask = set->capacity - 1;
|
|
set->items =
|
|
(struct simple_hashset_item_st *)calloc(set->capacity,
|
|
sizeof(struct
|
|
simple_hashset_item_st));
|
|
set->nitems = 0;
|
|
set->n_deleted_items = 0;
|
|
//assert(set->items);
|
|
for (index = 0; index < old_capacity; ++index) {
|
|
simple_hashset_add_member(set, old_items[index].item,
|
|
old_items[index].hash);
|
|
}
|
|
free(old_items);
|
|
}
|
|
}
|
|
|
|
int simple_hashset_add(simple_hashset_t set, symbol_type * key)
|
|
{
|
|
size_t key_len = strlen(key->desc);
|
|
size_t hash = set->hash_func(key->desc, key_len);
|
|
int rv = simple_hashset_add_member(set, key, hash);
|
|
set_maybe_rehash(set);
|
|
return rv;
|
|
}
|
|
|
|
int simple_hashset_is_member(simple_hashset_t set, symbol_type * key)
|
|
{
|
|
size_t key_len = strlen(key->desc);
|
|
size_t hash = set->hash_func(key->desc, key_len);
|
|
size_t index = set->mask & (prime_1 * hash);
|
|
|
|
while (set->items[index].hash != 0) {
|
|
if (set->items[index].hash == hash) {
|
|
return index;
|
|
} else {
|
|
index = set->mask & (index + prime_2);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|