|
|
- #include "erl_nif.h"
-
- ErlNifResourceType* bsn_type;
- ERL_NIF_TERM ATOM_TRUE, ATOM_FALSE;
-
- /*
- typedef struct {
- unsigned size;
- unsigned char* data;
- } ErlNifBinary;
-
- */
-
- struct bsn_elem_struct {
- ErlNifBinary bin;
- struct bsn_elem_struct* next;
- };
- typedef struct bsn_elem_struct bsn_elem;
-
- typedef bsn_elem* bsn_list;
-
- typedef struct {
- unsigned int count; /* count of elements */
- unsigned int max; /* count of slots */
- ErlNifMutex *mutex;
- bsn_list* list;
- } bsn_res;
-
-
- inline static ERL_NIF_TERM bool_to_term(int value) {
- return value ? ATOM_TRUE : ATOM_FALSE;
- }
-
- /* Calculate the sum of chars. */
- unsigned int
- private_hash(const ErlNifBinary* b, unsigned int max)
- {
- unsigned char* ptr;
- unsigned int i, sum = 0;
-
- ptr = b->data;
- i = b->size;
-
- for (; i; i--, ptr++)
- sum += *ptr;
-
- return sum % max;
- }
-
- inline void
- private_clear_elem(bsn_elem* el)
- {
- enif_release_binary(&(el->bin));
- enif_free(el);
- }
-
- inline void
- private_chain_clear_all(bsn_elem* ptr)
- {
- bsn_elem* next;
-
- while (ptr != NULL) {
-
- next = ptr->next;
- private_clear_elem(ptr);
- ptr = next;
- }
- }
-
- inline int
- private_compare(ErlNifBinary* b1, ErlNifBinary* b2)
- {
- unsigned char* p1;
- unsigned char* p2;
- unsigned len;
-
- if (b1->size != b2->size)
- return 0;
-
- p1 = b1->data;
- p2 = b2->data;
- len = b1->size;
-
- while (len) {
- if ((*p1) != (*p2))
- return 0;
-
- len--; p1++; p2++;
- }
- return 1;
- }
-
- /* Skip existing elements. If the element bin is not found, return last element.
- * If el.bin == bin, return el. */
- bsn_elem*
- private_chain_shift(bsn_elem* ptr, ErlNifBinary* bin, int* num_ptr)
- {
- (*num_ptr)++;
- if ((ptr) == NULL)
- return ptr;
-
- while (1) {
- if (private_compare(&(ptr->bin), bin)) {
- /* found an equal binary. Invert num */
- (*num_ptr) *= -1;
- return ptr;
- }
- if ((ptr->next) == NULL)
- return ptr;
- ptr = ptr->next;
- (*num_ptr)++;
- }
- }
-
- /* Append the element `el' to the chain `chain' */
- void
- private_chain_append(bsn_elem** chain, bsn_elem* el, int* num_ptr)
- {
- bsn_elem* last;
-
- if ((*chain) == NULL) {
- /* The new element is last */
- *chain = el;
- } else {
- last = private_chain_shift(*chain, &(el->bin), num_ptr);
- if ((*num_ptr) < 0) {
- /* Element was already added. */
- private_clear_elem(el);
- } else {
- last->next = el;
- }
- }
- }
-
- bsn_elem*
- private_chain_shift_clear(bsn_elem** ptr, ErlNifBinary* bin, int* num_ptr)
- {
- bsn_elem** prev = NULL;
- bsn_elem* el;
-
- while ((*ptr) != NULL) {
- if (private_compare(&((*ptr)->bin), bin)) {
- (*num_ptr) *= -1;
-
- /* found an equal binary. Delete elem. Invert num */
- if (prev == NULL) {
- el = *ptr;
- (*ptr) = (*ptr)->next;
- return el;
- }
- *prev = (*ptr)->next;
- return *ptr;
- }
- prev = ptr;
- el = *ptr;
- ptr = (bsn_elem**) &(el->next);
- (*num_ptr)++;
- }
-
- return NULL;
- }
-
- static ERL_NIF_TERM
- bsn_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- unsigned int max;
- bsn_list* ptr;
- bsn_res* r;
-
- if (!(enif_get_uint(env, argv[0], &max) && (max>0)))
- return enif_make_badarg(env);
-
- ptr = enif_alloc(sizeof(bsn_list) * max);
- if (ptr == NULL)
- return enif_make_badarg(env);
-
- r = (bsn_res*) enif_alloc_resource(bsn_type, sizeof(bsn_res));
- r->mutex = enif_mutex_create("Mutex for the BSN writer");
- r->count = 0;
- r->max = max;
- r->list = ptr;
-
- for (; max; max--, ptr++)
- *ptr = NULL;
-
- return enif_make_resource(env, r);
- }
-
- static ERL_NIF_TERM
- bsn_add(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- ErlNifBinary bin;
- bsn_res* r;
- unsigned int pos;
- int num = 0;
- bsn_elem* elem_ptr;
-
- if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r)
- && enif_inspect_binary(env, argv[1], &bin)))
- return enif_make_badarg(env);
-
- enif_realloc_binary(&bin, bin.size);
- pos = private_hash(&bin, r->max);
-
- elem_ptr = enif_alloc(sizeof(bsn_elem));
- if (elem_ptr == NULL)
- return enif_make_badarg(env);
-
- elem_ptr->next = NULL;
- elem_ptr->bin = bin;
-
- enif_mutex_lock(r->mutex);
- private_chain_append(&(r->list[pos]), elem_ptr, &num);
- if (num >= 0)
- (r->count)++;
- enif_mutex_unlock(r->mutex);
-
- /* Already added */
- if (num < 0)
- enif_release_binary(&(bin));
-
- return enif_make_int(env, num);
- }
-
- static ERL_NIF_TERM
- bsn_search(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- ErlNifBinary bin;
- bsn_res* r;
- unsigned int pos;
- int num = 0;
-
- if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r)
- && enif_inspect_binary(env, argv[1], &bin)))
- return enif_make_badarg(env);
-
- pos = private_hash(&bin, r->max);
-
- enif_mutex_lock(r->mutex);
- private_chain_shift(r->list[pos], &bin, &num);
- enif_mutex_unlock(r->mutex);
-
- return enif_make_int(env, num);
- }
-
- static ERL_NIF_TERM
- bsn_clear(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- ErlNifBinary bin;
- bsn_res* r;
- unsigned int pos;
- int num = 0;
- bsn_elem* elem_ptr;
-
- if (!(enif_get_resource(env, argv[0], bsn_type, (void**) &r)
- && enif_inspect_binary(env, argv[1], &bin)))
- return enif_make_badarg(env);
-
- pos = private_hash(&bin, r->max);
-
- enif_mutex_lock(r->mutex);
- elem_ptr = private_chain_shift_clear(&(r->list[pos]), &bin, &num);
- if (elem_ptr != NULL) {
- private_clear_elem(elem_ptr);
- (r->count)--;
- }
- enif_mutex_unlock(r->mutex);
-
- return enif_make_int(env, num);
- }
-
- static ERL_NIF_TERM
- bsn_all_chain(ErlNifEnv* env, bsn_elem* e, ERL_NIF_TERM tail)
- {
- ERL_NIF_TERM head;
- ErlNifBinary bin;
- while (e != NULL) {
- bin = e->bin;
- enif_realloc_binary(&bin, bin.size);
- head = enif_make_binary(env, &bin);
- tail = enif_make_list_cell(env, head, tail);
- e = e->next;
- }
- return tail;
- }
-
- static ERL_NIF_TERM
- bsn_chains(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- bsn_res* r;
- unsigned int max;
- bsn_list* ptr;
- ERL_NIF_TERM tail, head;
-
- if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r))
- return enif_make_badarg(env);
- tail = enif_make_list(env, 0);
-
- ptr = r->list;
-
- enif_mutex_lock(r->mutex);
- max = r->max;
-
- while (max) {
- head = enif_make_list(env, 0);
- head = bsn_all_chain(env, *ptr, head);
- tail = enif_make_list_cell(env, head, tail);
-
- ptr++;
- max--;
- }
- enif_mutex_unlock(r->mutex);
-
- return tail;
- }
-
- static ERL_NIF_TERM
- bsn_all(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- bsn_res* r;
- unsigned int max;
- bsn_list* ptr;
- ERL_NIF_TERM list;
-
- if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r))
- return enif_make_badarg(env);
- list = enif_make_list(env, 0);
-
- ptr = r->list;
-
- enif_mutex_lock(r->mutex);
- max = r->max;
-
- while (max) {
- list = bsn_all_chain(env, *ptr, list);
- ptr++;
- max--;
- }
- enif_mutex_unlock(r->mutex);
-
- return list;
- }
-
-
- static ERL_NIF_TERM
- bsn_count(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- bsn_res* r;
-
- if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r))
- return enif_make_badarg(env);
-
- return enif_make_int(env, r->count);
- }
-
-
- static ERL_NIF_TERM
- bsn_hash(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- ErlNifBinary bin;
- unsigned int max;
-
- if (!(enif_inspect_binary(env, argv[0], &bin)
- && enif_get_uint(env, argv[1], &max) && (max>0)))
- return enif_make_badarg(env);
-
- return enif_make_uint(env,
- private_hash(&bin, max));
- }
-
-
- static ERL_NIF_TERM
- bsn_compare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- ErlNifBinary b1, b2;
-
- if (!(enif_inspect_binary(env, argv[0], &b1)
- && enif_inspect_binary(env, argv[1], &b2)))
- return enif_make_badarg(env);
-
- return bool_to_term(private_compare(&b1, &b2));
- }
-
- void private_clear_all(bsn_res* r)
- {
- unsigned int max;
- bsn_list* ptr;
- max = r->max;
- ptr = r->list;
-
- while (max) {
- private_chain_clear_all(*ptr);
- ptr++;
- max--;
- }
- }
-
- void
- bsn_type_dtor(ErlNifEnv* env, void* obj)
- {
- bsn_res* r = (bsn_res*) obj;
- private_clear_all(r);
- enif_mutex_destroy(r->mutex);
- enif_free(r->list);
- }
-
-
-
- int
- on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
- {
- ATOM_TRUE = enif_make_atom(env, "true");
- ATOM_FALSE = enif_make_atom(env, "false");
-
- ErlNifResourceFlags flags = (ErlNifResourceFlags)(ERL_NIF_RT_CREATE |
- ERL_NIF_RT_TAKEOVER);
-
- bsn_type = enif_open_resource_type(env, NULL, "bsn_type",
- bsn_type_dtor, flags, NULL);
-
- if (bsn_type == NULL) return 1;
-
- return 0;
- }
-
-
- int
- on_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
- {
- return 0;
- }
-
-
- static ErlNifFunc nif_functions[] = {
- {"new", 1, bsn_new},
- {"add", 2, bsn_add},
- {"all", 1, bsn_all},
- {"chains", 1, bsn_chains},
- {"in", 2, bsn_search},
- {"clear", 2, bsn_clear},
- {"count", 1, bsn_count},
-
- {"hash", 2, bsn_hash},
- {"compare", 2, bsn_compare},
- };
-
-
- ERL_NIF_INIT(bsn_ext, nif_functions, &on_load, &on_load, &on_upgrade, NULL);
|