#include "erl_nif.h" ErlNifResourceType* bsn_type; ERL_NIF_TERM ATOM_TRUE, ATOM_FALSE, ATOM_NO_MORE; struct bsn_elem_struct { ErlNifBinary bin; unsigned int hash; }; typedef struct bsn_elem_struct bsn_elem; typedef struct { unsigned int count; /* count of elements */ unsigned int max; /* count of slots */ ErlNifMutex *mutex; bsn_elem* list; unsigned int (*next_pos) (void*, unsigned int, unsigned int); } bsn_res; inline static ERL_NIF_TERM bool_to_term(int value) { return value ? ATOM_TRUE : ATOM_FALSE; } unsigned int next_pos_linear(bsn_res* r, unsigned int hash, unsigned int step) { return (hash + step) % (r->max); } unsigned int next_pos_quadric(bsn_res* r, unsigned int hash, unsigned int step) { return (hash + (step*step)) % (r->max); } /* 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 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; } static ERL_NIF_TERM bsn_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { int max; /* This value will be set by a client: if (max<0) -> use quadric algorithm */ bsn_elem* ptr; bsn_res* r; if (!enif_get_int(env, argv[0], &max) || (max == 0)) 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; /* Select an algorithm */ if (max>0) { r->next_pos = &next_pos_linear; } else if (max<0) { r->next_pos = &next_pos_quadric; max *= -1; } /* Now max is cells' count in the array. */ r->max = (unsigned int) max; ptr = enif_alloc(sizeof(bsn_elem) * max); if (ptr == NULL) return enif_make_badarg(env); r->list = ptr; for (; max; max--, ptr++) ptr->hash = r->max; 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, hash, max; 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); hash = pos = private_hash(&bin, r->max); enif_mutex_lock(r->mutex); max = r->max; while (num < max) { elem_ptr = &(r->list[pos]); /* Found free space */ if (elem_ptr->hash == max) { elem_ptr->bin = bin; elem_ptr->hash = hash; break; } /* Found elem */ if ((elem_ptr->hash == hash) && private_compare(&bin, &(elem_ptr->bin))) { num *= -1; break; } pos = (r->next_pos)(r, hash, num); num++; } if ((num >= 0) && (num < max)) (r->count)++; enif_mutex_unlock(r->mutex); /* Error: already added or owerflow */ if (!((num >= 0) && (num < max))) enif_release_binary(&bin); if (num >= max) return ATOM_NO_MORE; 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, max, hash; int num = 1; 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); hash = pos = private_hash(&bin, r->max); enif_mutex_lock(r->mutex); max = r->max; while (num < max) { elem_ptr = &(r->list[pos]); /* Found free space */ if (elem_ptr->hash == max) { break; } /* Found elem */ if ((elem_ptr->hash == hash) && private_compare(&bin, &(elem_ptr->bin))) { num *= -1; break; } pos = (r->next_pos)(r, hash, num); 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[]) { return enif_make_badarg(env); } static ERL_NIF_TERM bsn_all(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { bsn_res* r; unsigned int max, pos = 0; ERL_NIF_TERM head, tail; ErlNifBinary bin; bsn_elem* elem_ptr; if (!enif_get_resource(env, argv[0], bsn_type, (void**) &r)) return enif_make_badarg(env); tail = enif_make_list(env, 0); enif_mutex_lock(r->mutex); max = r->max; elem_ptr = r->list; do { if (elem_ptr->hash != max) { bin = elem_ptr->bin; enif_realloc_binary(&bin, bin.size); head = enif_make_binary(env, &bin); tail = enif_make_list_cell(env, head, tail); } elem_ptr++; pos++; } while (pos < max); enif_mutex_unlock(r->mutex); return tail; } 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); } void private_clear_all(bsn_res* r) { unsigned int max, num; bsn_elem* ptr; num = max = r->max; ptr = r->list; while (num) { if (ptr->hash != max) { enif_release_binary(&(ptr->bin)); } ptr++; num--; } } 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"); ATOM_NO_MORE = enif_make_atom(env, "no_more"); 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}, {"in", 2, bsn_search}, {"clear", 2, bsn_clear}, {"count", 1, bsn_count}, }; ERL_NIF_INIT(bsn_int, nif_functions, &on_load, &on_load, &on_upgrade, NULL);