#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);