//
|
|
// Created by fox on 2017/8/10.
|
|
//
|
|
|
|
#ifndef USENIF
|
|
#define USENIF 1
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include "erl_nif.h"
|
|
#include "skiplist.h"
|
|
|
|
// all pointer to skiplist would be stored here
|
|
static skiplist **p_array;
|
|
static size_t p_array_size = 0;
|
|
|
|
#define enif_get_value enif_get_int
|
|
#define enif_make_value enif_make_int
|
|
|
|
// store a new pointer
|
|
int store_new_pointer(skiplist *p)
|
|
{
|
|
if (p_array_size == 0) {
|
|
// init it
|
|
p_array_size = 2;
|
|
p_array = (skiplist **)icalloc(p_array_size * sizeof(skiplist *));
|
|
p_array[0] = p;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// find the empty pos
|
|
for (int i = 0; i < p_array_size; ++i) {
|
|
if (p_array[i] == NULL) {
|
|
p_array[i] = p;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// reallocate array
|
|
size_t old_size = p_array_size;
|
|
p_array_size *= 2;
|
|
|
|
p_array = (skiplist **)realloc(p_array, sizeof(skiplist *) * p_array_size);
|
|
memset(p_array+old_size, 0, sizeof(skiplist *) * old_size);
|
|
|
|
p_array[old_size] = p;
|
|
return (int)old_size;
|
|
}
|
|
|
|
inline skiplist *get_pointer(ErlNifEnv* env, ERL_NIF_TERM arg);
|
|
skiplist *get_pointer(ErlNifEnv* env, ERL_NIF_TERM arg)
|
|
{
|
|
int index;
|
|
if (!enif_get_int(env, arg, &index))
|
|
return NULL;
|
|
|
|
if (0 <= index && index < p_array_size)
|
|
return p_array[index];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static ERL_NIF_TERM new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
skiplist *list = skiplist_init();
|
|
int pointer_pos = store_new_pointer(list);
|
|
|
|
ERL_NIF_TERM res = enif_make_int(env, pointer_pos);
|
|
return res;
|
|
}
|
|
|
|
static ERL_NIF_TERM free1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
int index;
|
|
if (!enif_get_int(env, argv[0], &index))
|
|
return enif_make_badarg(env);
|
|
|
|
if (index < 0 || index >= p_array_size)
|
|
return enif_make_badarg(env);
|
|
|
|
skiplist_free(p_array[index]);
|
|
p_array[index] = NULL;
|
|
|
|
ERL_NIF_TERM res;
|
|
enif_make_existing_atom(env, "ok", &res, ERL_NIF_LATIN1);
|
|
return res;
|
|
}
|
|
|
|
static ERL_NIF_TERM insert(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
skiplist *list = get_pointer(env, argv[0]);
|
|
if (!list)
|
|
return enif_make_badarg(env);
|
|
|
|
int score;
|
|
vtype value;
|
|
if (!enif_get_int(env, argv[1], &score))
|
|
return enif_make_badarg(env);
|
|
if (!enif_get_value(env, argv[2], &value))
|
|
return enif_make_badarg(env);
|
|
|
|
int res0 = skiplist_insert(list, score, value);
|
|
|
|
ERL_NIF_TERM res = enif_make_int(env, res0);
|
|
return res;
|
|
}
|
|
|
|
static ERL_NIF_TERM update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
skiplist *list = get_pointer(env, argv[0]);
|
|
if (!list)
|
|
return enif_make_badarg(env);
|
|
|
|
int score, old_score;
|
|
vtype value;
|
|
if (!enif_get_int(env, argv[1], &score))
|
|
return enif_make_badarg(env);
|
|
if (!enif_get_value(env, argv[2], &value))
|
|
return enif_make_badarg(env);
|
|
if (!enif_get_int(env, argv[3], &old_score))
|
|
return enif_make_badarg(env);
|
|
|
|
int res0 = skiplist_update(list, score, value, old_score);
|
|
|
|
ERL_NIF_TERM res = enif_make_int(env, res0);
|
|
return res;
|
|
}
|
|
|
|
static ERL_NIF_TERM delete(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
skiplist *list = get_pointer(env, argv[0]);
|
|
if (!list)
|
|
return enif_make_badarg(env);
|
|
|
|
int socre;
|
|
vtype value;
|
|
if (!enif_get_int(env, argv[1], &socre))
|
|
return enif_make_badarg(env);
|
|
if (!enif_get_value(env, argv[2], &value))
|
|
return enif_make_badarg(env);
|
|
|
|
int res0 = skiplist_delete(list, socre, value);
|
|
|
|
ERL_NIF_TERM res = enif_make_int(env, res0);
|
|
return res;
|
|
}
|
|
|
|
static ERL_NIF_TERM to_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
skiplist *list = get_pointer(env, argv[0]);
|
|
if (!list)
|
|
return enif_make_badarg(env);
|
|
|
|
snode *x = list->header->level[0].forward;
|
|
snode **nodes = (snode **)malloc(sizeof(snode *) * (list->size + 1));
|
|
|
|
while (x) {
|
|
*nodes = x;
|
|
++nodes;
|
|
x = x->level[0].forward;
|
|
}
|
|
|
|
ERL_NIF_TERM res = enif_make_list(env, 0);
|
|
for (int i = list->size-1; i >= 0; --i) {
|
|
--nodes;
|
|
ERL_NIF_TERM item = enif_make_tuple(
|
|
env,
|
|
2,
|
|
enif_make_int(env, (*nodes)->score),
|
|
enif_make_value(env, (*nodes)->value));
|
|
res = enif_make_list_cell(env, item, res);
|
|
}
|
|
|
|
free(nodes);
|
|
|
|
return res;
|
|
}
|
|
|
|
static ERL_NIF_TERM size1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
skiplist *list = get_pointer(env, argv[0]);
|
|
if (!list)
|
|
return enif_make_badarg(env);
|
|
|
|
ERL_NIF_TERM res = enif_make_int(env, list->size);
|
|
return res;
|
|
}
|
|
|
|
// first index of the score
|
|
static ERL_NIF_TERM index_of_score(ErlNifEnv *env, int argc, const ERL_NIF_TERM *argv)
|
|
{
|
|
skiplist *list = get_pointer(env, argv[0]);
|
|
if (!list)
|
|
return enif_make_badarg(env);
|
|
|
|
int socre;
|
|
if (!enif_get_int(env, argv[1], &socre))
|
|
return enif_make_badarg(env);
|
|
|
|
int res0 = skiplist_index_of_score(list, socre);
|
|
ERL_NIF_TERM res = enif_make_int(env, res0);
|
|
|
|
return res;
|
|
}
|
|
|
|
static ERL_NIF_TERM at(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
skiplist *list = get_pointer(env, argv[0]);
|
|
if (!list)
|
|
return enif_make_badarg(env);
|
|
|
|
int index;
|
|
if (!enif_get_int(env, argv[1], &index))
|
|
return enif_make_badarg(env);
|
|
|
|
snode *x = skiplist_at(list, index);
|
|
|
|
ERL_NIF_TERM res;
|
|
|
|
if (x) {
|
|
res = enif_make_tuple(
|
|
env,
|
|
2,
|
|
enif_make_int(env, x->score),
|
|
enif_make_value(env, x->value));
|
|
} else {
|
|
enif_make_existing_atom(env, "error", &res, ERL_NIF_LATIN1);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static ERL_NIF_TERM range_by_score(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
skiplist *list = get_pointer(env, argv[0]);
|
|
if (!list)
|
|
return enif_make_badarg(env);
|
|
|
|
int score1, score2;
|
|
if (!enif_get_int(env, argv[1], &score1))
|
|
return enif_make_badarg(env);
|
|
if (!enif_get_int(env, argv[2], &score2))
|
|
return enif_make_badarg(env);
|
|
|
|
skiplist_search_ret res0;
|
|
skiplist_search(list, score1, &res0);
|
|
|
|
ERL_NIF_TERM ret_list = enif_make_list(env, 0);
|
|
for (snode *node = res0.node; node && node->score <= score2; node = node->level[0].forward) {
|
|
ret_list = enif_make_list_cell(env,
|
|
enif_make_tuple(env,
|
|
2,
|
|
enif_make_int(env, node->score),
|
|
enif_make_value(env, node->value)),
|
|
ret_list);
|
|
}
|
|
ERL_NIF_TERM ret_list1;
|
|
enif_make_reverse_list(env, ret_list, &ret_list1);
|
|
|
|
return enif_make_tuple(
|
|
env,
|
|
2,
|
|
enif_make_int(env, res0.index),
|
|
ret_list1);
|
|
}
|
|
|
|
static ERL_NIF_TERM range_1(
|
|
ErlNifEnv* env,
|
|
const ERL_NIF_TERM argv[],
|
|
ERL_NIF_TERM (*func_make_item)(ErlNifEnv *, snode *))
|
|
{
|
|
skiplist *list = get_pointer(env, argv[0]);
|
|
if (!list)
|
|
return enif_make_badarg(env);
|
|
|
|
int start, len;
|
|
if (!enif_get_int(env, argv[1], &start))
|
|
return enif_make_badarg(env);
|
|
if (!enif_get_int(env, argv[2], &len))
|
|
return enif_make_badarg(env);
|
|
|
|
int n = list->size - start + 1;
|
|
int alloc_size = n < len ? n : len;
|
|
|
|
ERL_NIF_TERM res = enif_make_list(env, 0);
|
|
if (alloc_size <= 0) {
|
|
return res;
|
|
}
|
|
|
|
snode **nodes = (snode **)malloc(sizeof(snode *) * (alloc_size + 1));
|
|
|
|
// get the nodes
|
|
snode *x = skiplist_at(list, start);
|
|
for (n = 0; n < alloc_size; n++) {
|
|
*nodes = x;
|
|
++nodes;
|
|
x = x->level[0].forward;
|
|
}
|
|
|
|
for (n = 0; n < alloc_size; n++) {
|
|
--nodes;
|
|
res = enif_make_list_cell(env, func_make_item(env, *nodes), res);
|
|
}
|
|
|
|
free(nodes);
|
|
|
|
return res;
|
|
}
|
|
|
|
inline static ERL_NIF_TERM range_make_item(ErlNifEnv* env, snode *node);
|
|
static ERL_NIF_TERM range_make_item(ErlNifEnv* env, snode *node)
|
|
{
|
|
return enif_make_value(env, node->value);
|
|
}
|
|
|
|
/* int p_list: identification of skiplist
|
|
* int start: >= 1, start index of skiplist
|
|
* int len: >= 1, max items to return
|
|
* return: [node]
|
|
*/
|
|
static ERL_NIF_TERM range(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
return range_1(env, argv, range_make_item);
|
|
}
|
|
|
|
inline static ERL_NIF_TERM range_with_score_make_item(ErlNifEnv* env, snode *node);
|
|
static ERL_NIF_TERM range_with_score_make_item(ErlNifEnv* env, snode *node)
|
|
{
|
|
return enif_make_tuple(
|
|
env,
|
|
2,
|
|
enif_make_int(env, node->score),
|
|
enif_make_value(env, node->value));
|
|
}
|
|
|
|
/* int p_list: identification of skiplist
|
|
* int start: >= 1, start index of skiplist
|
|
* int len: >= 1, max items to return
|
|
* return: [{score, node}]
|
|
*/
|
|
static ERL_NIF_TERM range_with_score(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
return range_1(env, argv, range_with_score_make_item);
|
|
}
|
|
|
|
static ErlNifFunc nif_funcs[] = {
|
|
{"new", 0, new},
|
|
{"free", 1, free1},
|
|
{"insert", 3, insert},
|
|
{"update", 4, update},
|
|
{"delete", 3, delete},
|
|
{"to_list", 1, to_list},
|
|
{"size", 1, size1},
|
|
{"index_of_score", 2, index_of_score},
|
|
{"at", 2, at},
|
|
{"range", 3, range},
|
|
{"range_with_score", 3, range_with_score},
|
|
{"range_by_score", 3, range_by_score},
|
|
};
|
|
|
|
ERL_NIF_INIT(skiplist, nif_funcs, NULL, NULL, NULL, NULL)
|
|
|
|
|