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