You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
8.0 KiB
C
395 lines
8.0 KiB
C
#define LUA_LIB
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "lauxlib.h"
|
|
#include "lua.h"
|
|
|
|
// https://github.com/hongling0/lua-zset
|
|
|
|
#define SKIPLIST_MAXLEVEL 32
|
|
#define SKIPLIST_P 0.25
|
|
|
|
typedef struct skipsetNode
|
|
{
|
|
int64_t obj;
|
|
struct skipsetNode *backward;
|
|
struct skiplistLevel
|
|
{
|
|
struct skipsetNode *forward;
|
|
unsigned int span;
|
|
} level[];
|
|
} skipsetNode;
|
|
|
|
typedef struct skipset
|
|
{
|
|
struct skipsetNode *header, *tail;
|
|
unsigned long length;
|
|
int level;
|
|
} skipset;
|
|
|
|
static int slRandomLevel(void)
|
|
{
|
|
int level = 1;
|
|
while ((random() & 0xffff) < (SKIPLIST_P * 0xffff))
|
|
level += 1;
|
|
return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL;
|
|
}
|
|
|
|
static skipsetNode *slCreateNode(int level, int64_t obj)
|
|
{
|
|
skipsetNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel));
|
|
n->obj = obj;
|
|
return n;
|
|
}
|
|
|
|
static int slInsert(skipset *sl, int64_t obj)
|
|
{
|
|
skipsetNode *update[SKIPLIST_MAXLEVEL], *x;
|
|
unsigned int rank[SKIPLIST_MAXLEVEL];
|
|
int i, level;
|
|
|
|
x = sl->header;
|
|
for (i = sl->level - 1; i >= 0; i--)
|
|
{
|
|
rank[i] = i == (sl->level - 1) ? 0 : rank[i + 1];
|
|
while (x->level[i].forward && x->level[i].forward->obj < obj)
|
|
{
|
|
rank[i] += x->level[i].span;
|
|
x = x->level[i].forward;
|
|
}
|
|
update[i] = x;
|
|
}
|
|
x = x->level[0].forward;
|
|
if (x && x->obj == obj)
|
|
return 0;
|
|
|
|
level = slRandomLevel();
|
|
if (level > sl->level)
|
|
{
|
|
for (i = sl->level; i < level; i++)
|
|
{
|
|
rank[i] = 0;
|
|
update[i] = sl->header;
|
|
update[i]->level[i].span = sl->length;
|
|
}
|
|
sl->level = level;
|
|
}
|
|
x = slCreateNode(level, obj);
|
|
for (i = 0; i < level; i++)
|
|
{
|
|
x->level[i].forward = update[i]->level[i].forward;
|
|
update[i]->level[i].forward = x;
|
|
|
|
x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
|
|
update[i]->level[i].span = (rank[0] - rank[i]) + 1;
|
|
}
|
|
|
|
for (i = level; i < sl->level; i++)
|
|
{
|
|
update[i]->level[i].span++;
|
|
}
|
|
|
|
x->backward = (update[0] == sl->header) ? NULL : update[0];
|
|
if (x->level[0].forward)
|
|
x->level[0].forward->backward = x;
|
|
else
|
|
sl->tail = x;
|
|
sl->length++;
|
|
return 1;
|
|
}
|
|
|
|
static void slDeleteNode(skipset *sl, skipsetNode *x, skipsetNode **update)
|
|
{
|
|
int i;
|
|
for (i = 0; i < sl->level; i++)
|
|
{
|
|
if (update[i]->level[i].forward == x)
|
|
{
|
|
update[i]->level[i].span += x->level[i].span - 1;
|
|
update[i]->level[i].forward = x->level[i].forward;
|
|
}
|
|
else
|
|
{
|
|
update[i]->level[i].span -= 1;
|
|
}
|
|
}
|
|
if (x->level[0].forward)
|
|
{
|
|
x->level[0].forward->backward = x->backward;
|
|
}
|
|
else
|
|
{
|
|
sl->tail = x->backward;
|
|
}
|
|
while (sl->level > 1 && sl->header->level[sl->level - 1].forward == NULL)
|
|
sl->level--;
|
|
sl->length--;
|
|
}
|
|
|
|
static void slFreeNode(skipsetNode *node)
|
|
{
|
|
free(node);
|
|
}
|
|
|
|
static int slDelete(skipset *sl, int64_t obj)
|
|
{
|
|
skipsetNode *update[SKIPLIST_MAXLEVEL], *x;
|
|
int i;
|
|
|
|
x = sl->header;
|
|
for (i = sl->level - 1; i >= 0; i--)
|
|
{
|
|
while (x->level[i].forward && x->level[i].forward->obj < obj)
|
|
x = x->level[i].forward;
|
|
update[i] = x;
|
|
}
|
|
|
|
x = x->level[0].forward;
|
|
if (x && (x->obj == obj))
|
|
{
|
|
slDeleteNode(sl, x, update);
|
|
slFreeNode(x);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0; /* not found */
|
|
}
|
|
return 0; /* not found */
|
|
}
|
|
|
|
static unsigned long slGetRank(skipset *sl, int64_t obj)
|
|
{
|
|
skipsetNode *x;
|
|
unsigned long rank = 0;
|
|
int i;
|
|
|
|
x = sl->header;
|
|
for (i = sl->level - 1; i >= 0; i--)
|
|
{
|
|
while (x->level[i].forward && x->level[i].forward->obj <= obj)
|
|
{
|
|
rank += x->level[i].span;
|
|
x = x->level[i].forward;
|
|
}
|
|
if (x->obj && (x->obj == obj))
|
|
{
|
|
return rank;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank)
|
|
{
|
|
if (rank == 0 || rank > sl->length)
|
|
return NULL;
|
|
|
|
skipsetNode *x;
|
|
unsigned long traversed = 0;
|
|
int i;
|
|
|
|
x = sl->header;
|
|
for (i = sl->level - 1; i >= 0; i--)
|
|
{
|
|
while (x->level[i].forward && (traversed + x->level[i].span) <= rank)
|
|
{
|
|
traversed += x->level[i].span;
|
|
x = x->level[i].forward;
|
|
}
|
|
if (traversed == rank)
|
|
{
|
|
return x;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
unsigned long slDeleteByRank(skipset *sl, unsigned int rank)
|
|
{
|
|
skipsetNode *update[SKIPLIST_MAXLEVEL], *x;
|
|
unsigned long traversed = 0;
|
|
int i;
|
|
|
|
x = sl->header;
|
|
for (i = sl->level - 1; i >= 0; i--)
|
|
{
|
|
while (x->level[i].forward && (traversed + x->level[i].span) < rank)
|
|
{
|
|
traversed += x->level[i].span;
|
|
x = x->level[i].forward;
|
|
}
|
|
update[i] = x;
|
|
}
|
|
|
|
x = x->level[0].forward;
|
|
if (x)
|
|
{
|
|
slDeleteNode(sl, x, update);
|
|
slFreeNode(x);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static skipset *slCreate(void)
|
|
{
|
|
int j;
|
|
skipset *sl;
|
|
|
|
sl = malloc(sizeof(*sl));
|
|
sl->level = 1;
|
|
sl->length = 0;
|
|
sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0);
|
|
for (j = 0; j < SKIPLIST_MAXLEVEL; j++)
|
|
{
|
|
sl->header->level[j].forward = NULL;
|
|
sl->header->level[j].span = 0;
|
|
}
|
|
sl->header->backward = NULL;
|
|
sl->tail = NULL;
|
|
return sl;
|
|
}
|
|
|
|
static void slFree(skipset *sl)
|
|
{
|
|
skipsetNode *node = sl->header->level[0].forward, *next;
|
|
free(sl->header);
|
|
while (node)
|
|
{
|
|
next = node->level[0].forward;
|
|
slFreeNode(node);
|
|
node = next;
|
|
}
|
|
free(sl);
|
|
}
|
|
|
|
static inline skipset *
|
|
_to_skipset(lua_State *L, int idx)
|
|
{
|
|
skipset **sl = lua_touserdata(L, idx);
|
|
if (sl == NULL)
|
|
{
|
|
luaL_error(L, "must be skipset object");
|
|
}
|
|
return *sl;
|
|
}
|
|
|
|
static int
|
|
_insert(lua_State *L)
|
|
{
|
|
skipset *sl = _to_skipset(L, 1);
|
|
lua_Integer obj = luaL_checkinteger(L, 2);
|
|
lua_pushboolean(L, slInsert(sl, obj));
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_delete(lua_State *L)
|
|
{
|
|
skipset *sl = _to_skipset(L, 1);
|
|
lua_Integer obj = luaL_checkinteger(L, 2);
|
|
lua_pushboolean(L, slDelete(sl, obj));
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_get_count(lua_State *L)
|
|
{
|
|
skipset *sl = _to_skipset(L, 1);
|
|
lua_pushinteger(L, sl->length);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_get_rank(lua_State *L)
|
|
{
|
|
skipset *sl = _to_skipset(L, 1);
|
|
lua_Integer obj = luaL_checkinteger(L, 2);
|
|
|
|
unsigned long rank = slGetRank(sl, obj);
|
|
if (rank == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
lua_pushinteger(L, rank);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_get_by_rank(lua_State *L)
|
|
{
|
|
skipset *sl = _to_skipset(L, 1);
|
|
unsigned long r1 = luaL_checkinteger(L, 2);
|
|
|
|
skipsetNode *node = slGetNodeByRank(sl, r1);
|
|
if (node)
|
|
{
|
|
lua_pushinteger(L, node->obj);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_delete_by_rank(lua_State *L)
|
|
{
|
|
skipset *sl = _to_skipset(L, 1);
|
|
unsigned int rank = luaL_checkinteger(L, 2);
|
|
lua_pushinteger(L, slDeleteByRank(sl, rank));
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_new(lua_State *L)
|
|
{
|
|
skipset *psl = slCreate();
|
|
|
|
skipset **sl = (skipset **)lua_newuserdata(L, sizeof(skipset *));
|
|
*sl = psl;
|
|
lua_pushvalue(L, lua_upvalueindex(1));
|
|
lua_setmetatable(L, -2);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_release(lua_State *L)
|
|
{
|
|
skipset *sl = _to_skipset(L, 1);
|
|
slFree(sl);
|
|
return 0;
|
|
}
|
|
|
|
LUAMOD_API int
|
|
luaopen_skipset_c(lua_State *L)
|
|
{
|
|
luaL_checkversion(L);
|
|
|
|
luaL_Reg l[] = {
|
|
{"insert", _insert},
|
|
{"delete", _delete},
|
|
{"delete_byrank", _delete_by_rank},
|
|
|
|
{"count", _get_count},
|
|
{"rank", _get_rank},
|
|
{"byrank", _get_by_rank},
|
|
|
|
{NULL, NULL}};
|
|
|
|
lua_createtable(L, 0, 2);
|
|
|
|
luaL_newlib(L, l);
|
|
lua_setfield(L, -2, "__index");
|
|
lua_pushcfunction(L, _release);
|
|
lua_setfield(L, -2, "__gc");
|
|
lua_pushcfunction(L, _get_count);
|
|
lua_setfield(L, -2, "__len");
|
|
|
|
lua_pushcclosure(L, _new, 1);
|
|
return 1;
|
|
}
|