├── .gitignore ├── README.md ├── test_ts.lua ├── Makefile ├── skiplist.h ├── test.lua ├── test_sl.lua ├── zset.lua ├── lua-skiplist.c └── skiplist.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## zset 2 | lua的sorted set实现, 其中skiplist的实现基本是从redis源码里面抠出来的, 只有几个接口的约定不太一致。 3 | 4 | ## build & test 5 | ``` 6 | make && lua test_sl.lua && lua test.lua 7 | ``` 8 | 9 | -------------------------------------------------------------------------------- /test_ts.lua: -------------------------------------------------------------------------------- 1 | 2 | local zset = require "zset" 3 | 4 | local total = 100 5 | 6 | local function random_choose(t) 7 | if #t == 0 then 8 | return 9 | end 10 | local i = math.random(#t) 11 | return table.remove(t, i) 12 | end 13 | 14 | local function delete_handler(member) 15 | print("delete", member) 16 | end 17 | 18 | local zs = zset.new(delete_handler) 19 | 20 | local score = 100 21 | for i=1, total do 22 | local name = "a" .. i 23 | local a = i % 2 ~= 0 and score or score/ 2 24 | zs:add(a, name, i) 25 | end 26 | 27 | assert(total == zs:count()) 28 | 29 | print("rank 28:", zs:rank("a28")) 30 | print("rev rank 28:", zs:rev_rank("a28")) 31 | 32 | local t = zs:range(1, 10) 33 | print("rank 1-10:") 34 | for _, name in ipairs(t) do 35 | print(name) 36 | end 37 | 38 | local t = zs:rev_range(1, 10) 39 | print("rev rank 1-10:") 40 | for _, name in ipairs(t) do 41 | print(name) 42 | end 43 | 44 | print("------------------ dump ------------------") 45 | zs:dump() 46 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Compilation options 2 | CC = gcc 3 | CFLAGS = -O3 -Wall -pedantic -DNDEBUG -DUSE_MEM_HOOK 4 | TARGET = skiplist.so 5 | 6 | # Check the OS type 7 | UNAME_S := $(shell uname -s) 8 | ifeq ($(UNAME_S), Darwin) 9 | LDFLAGS = -bundle -undefined dynamic_lookup 10 | # Assuming you installed lua using homebrew 11 | # If not, you need to change to your own lua include path 12 | LUA_VERSION := $(shell lua -v 2>&1 | sed -E 's/.*Lua ([0-9\.]+).*/\1/') 13 | LUA_INCLUDE ?= /opt/homebrew/Cellar/lua/$(LUA_VERSION)/include/lua 14 | else 15 | LDFLAGS = -shared 16 | LUA_INCLUDE ?= /usr/local/include 17 | endif 18 | 19 | # Default target 20 | all: $(TARGET) 21 | 22 | luajit: LUA_INCLUDE = /usr/local/include/luajit-2.1 23 | luajit: $(TARGET) 24 | 25 | # Source files 26 | SRC_FILES = lua-skiplist.c skiplist.c 27 | HEADER_FILES = skiplist.h 28 | 29 | # Compile source files 30 | $(TARGET): $(SRC_FILES) $(HEADER_FILES) 31 | $(CC) $(CFLAGS) -I$(LUA_INCLUDE) -o $@ $(SRC_FILES) $(LDFLAGS) 32 | 33 | test: 34 | lua test.lua 35 | lua test_sl.lua 36 | 37 | clean: 38 | rm -f $(TARGET) 39 | 40 | .PHONY: all clean 41 | 42 | -------------------------------------------------------------------------------- /skiplist.h: -------------------------------------------------------------------------------- 1 | // 2 | #include 3 | 4 | #define SKIPLIST_MAXLEVEL 32 5 | #define SKIPLIST_P 0.25 6 | 7 | typedef struct slobj { 8 | char *ptr; 9 | size_t length; 10 | } slobj; 11 | 12 | typedef struct skiplistNode { 13 | slobj* obj; 14 | double score; 15 | double timestamp; 16 | struct skiplistNode *backward; 17 | struct skiplistLevel { 18 | struct skiplistNode *forward; 19 | unsigned int span; 20 | }level[]; 21 | } skiplistNode; 22 | 23 | typedef struct skiplist { 24 | struct skiplistNode *header, *tail; 25 | unsigned long length; 26 | int level; 27 | } skiplist; 28 | 29 | typedef void (*slDeleteCb) (void *ud, slobj *obj); 30 | slobj* slCreateObj(const char* ptr, size_t length); 31 | void slFreeObj(slobj *obj); 32 | 33 | skiplist *slCreate(void); 34 | void slFree(skiplist *sl); 35 | void slDump(skiplist *sl); 36 | 37 | void slInsert(skiplist *sl, double score, slobj *obj, double timestamp); 38 | int slDelete(skiplist *sl, double score, slobj *obj, double timestamp); 39 | unsigned long slDeleteByScore(skiplist *sl, double min, double max, slDeleteCb cb, void* ud); 40 | unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void* ud); 41 | 42 | unsigned long slGetRank(skiplist *sl, double score, slobj *o, double timestamp); 43 | skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank); 44 | 45 | skiplistNode *slFirstInRange(skiplist *sl, double min, double max); 46 | skiplistNode *slLastInRange(skiplist *sl, double min, double max); 47 | 48 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | local zset = require "zset" 2 | 3 | local total = 100 4 | local all = {} 5 | for i=1, total do 6 | all[#all + 1] = i 7 | end 8 | 9 | local function random_choose(t) 10 | if #t == 0 then 11 | return 12 | end 13 | local i = math.random(#t) 14 | return table.remove(t, i) 15 | end 16 | 17 | local function delete_handler(member) 18 | print("delete", member) 19 | end 20 | 21 | local zs = zset.new(delete_handler) 22 | 23 | while true do 24 | local score = random_choose(all) 25 | if not score then 26 | break 27 | end 28 | local name = "a" .. score 29 | zs:add(score, name) 30 | end 31 | 32 | assert(total == zs:count()) 33 | 34 | print("rank 28:", zs:rank("a28")) 35 | print("rev rank 28:", zs:rev_rank("a28")) 36 | 37 | local t = zs:range(1, 10) 38 | print("rank 1-10:") 39 | for _, name in ipairs(t) do 40 | print(name) 41 | end 42 | 43 | local t = zs:rev_range(1, 10) 44 | print("rev rank 1-10:") 45 | for _, name in ipairs(t) do 46 | print(name) 47 | end 48 | 49 | print("------------------ dump ------------------") 50 | zs:dump() 51 | 52 | print("------------------ dump after limit 10 ------------------") 53 | zs:limit(10) 54 | zs:dump() 55 | 56 | print("------------------ dump after rev limit 5 ------------------") 57 | zs:rev_limit(5) 58 | zs:dump() 59 | 60 | print("------------------ member_by_rank ------------------") 61 | for rank = 1, 10 do 62 | print("rank", rank, zs:member_by_rank(rank)) 63 | end 64 | 65 | print("------------------ member_by_rev_rank ------------------") 66 | for rank = 1, 10 do 67 | print("rank", rank, zs:member_by_rev_rank(rank)) 68 | end 69 | -------------------------------------------------------------------------------- /test_sl.lua: -------------------------------------------------------------------------------- 1 | local c = require "skiplist.c" 2 | 3 | local sl = c() 4 | 5 | local total = 500000 6 | for i=1, total do 7 | sl:insert(i, tostring(i)) 8 | sl:insert(i, tostring(i)) 9 | end 10 | 11 | for i=1, total do 12 | sl:delete(i, tostring(i)) 13 | end 14 | 15 | --[[ 16 | assert(sl:get_rank(1, "1") == 1) 17 | assert(sl:get_rank(total, tostring(total)) == total) 18 | 19 | local rand = math.random(total) 20 | print(rand) 21 | assert(sl:get_rank(rand, tostring(rand)) == rand) 22 | ]] 23 | 24 | local a1, a2 = 100, 100000 25 | local t1 = sl:get_rank_range(a1, a2) 26 | local t2 = sl:get_rank_range(a2, a1) 27 | assert(#t1 == #t2) 28 | for i, name in pairs(t1) do 29 | assert(name == t2[#t2 -i + 1], name) 30 | end 31 | 32 | local a1, a2 = 100, 100000 33 | local t1 = sl:get_score_range(a1, a2) 34 | local t2 = sl:get_score_range(a2, a1) 35 | assert(#t1 == #t2) 36 | for i, name in pairs(t1) do 37 | assert(name == t2[#t2 -i + 1], name) 38 | end 39 | 40 | local function dump_rank_range(sl, r1, r2) 41 | print("rank range:", r1, r2) 42 | local t = sl:get_rank_range(r1, r2) 43 | for i, name in ipairs(t) do 44 | if r1 <= r2 then 45 | print(r1+(i-1), name) 46 | else 47 | print(r1-(i-1), name) 48 | end 49 | end 50 | end 51 | 52 | local r1, r2 = 2, 5 53 | dump_rank_range(sl, r1, r2) 54 | dump_rank_range(sl, r2, r1) 55 | 56 | local function dump_score_range(sl, s1, s2) 57 | print("score range:", s1, s2) 58 | local t = sl:get_score_range(s1, s2) 59 | for _, name in ipairs(t) do 60 | print(name) 61 | end 62 | end 63 | 64 | local s1, s2 = 10, 20 65 | dump_score_range(sl, s1, s2) 66 | dump_score_range(sl, s2, s1) 67 | 68 | local function delete_cb(member) 69 | print("delete:", member) 70 | end 71 | sl:delete_by_rank(15, 10, delete_cb) 72 | sl:delete_by_score(100, 105, delete_cb) 73 | 74 | print("before gc:", collectgarbage("count")) 75 | sl = nil 76 | collectgarbage("collect") 77 | print("after gc:", collectgarbage("count")) 78 | 79 | -------------------------------------------------------------------------------- /zset.lua: -------------------------------------------------------------------------------- 1 | local skiplist = require "skiplist.c" 2 | local mt = {} 3 | mt.__index = mt 4 | 5 | function mt:add(score, member, ts) 6 | local old = self.tbl[member] 7 | if old then 8 | if old == score then 9 | return 10 | end 11 | self.sl:delete(old, member, self.ts[member]) 12 | end 13 | 14 | self.sl:insert(score, member, ts) 15 | self.tbl[member] = score 16 | self.ts[member] = ts 17 | end 18 | 19 | function mt:rem(member) 20 | local score = self.tbl[member] 21 | if score then 22 | self.sl:delete(score, member, self.ts[member]) 23 | self.tbl[member] = nil 24 | self.ts[member] = nil 25 | end 26 | end 27 | 28 | function mt:rem_range_by_score(min, max) 29 | return self.sl:delete_by_score(min, max, self.delete_function) 30 | end 31 | 32 | function mt:count() 33 | return self.sl:get_count() 34 | end 35 | 36 | function mt:_reverse_rank(r) 37 | return self.sl:get_count() - r + 1 38 | end 39 | 40 | function mt:limit(count) 41 | local total = self.sl:get_count() 42 | if total <= count then 43 | return 0 44 | end 45 | return self.sl:delete_by_rank(count+1, total, self.delete_function) 46 | end 47 | 48 | function mt:rev_limit(count) 49 | local total = self.sl:get_count() 50 | if total <= count then 51 | return 0 52 | end 53 | local from = self:_reverse_rank(count+1) 54 | local to = self:_reverse_rank(total) 55 | return self.sl:delete_by_rank(from, to, self.delete_function) 56 | end 57 | 58 | function mt:rev_range(r1, r2) 59 | r1 = self:_reverse_rank(r1) 60 | r2 = self:_reverse_rank(r2) 61 | return self:range(r1, r2) 62 | end 63 | 64 | function mt:range(r1, r2) 65 | if r1 < 1 then 66 | r1 = 1 67 | end 68 | 69 | if r2 < 1 then 70 | r2 = 1 71 | end 72 | return self.sl:get_rank_range(r1, r2) 73 | end 74 | 75 | function mt:rev_rank(member) 76 | local r = self:rank(member) 77 | if r then 78 | return self:_reverse_rank(r) 79 | end 80 | return r 81 | end 82 | 83 | function mt:rank(member) 84 | local score = self.tbl[member] 85 | if not score then 86 | return nil 87 | end 88 | return self.sl:get_rank(score, member, self.ts[member]) 89 | end 90 | 91 | function mt:range_by_score(s1, s2) 92 | return self.sl:get_score_range(s1, s2) 93 | end 94 | 95 | function mt:score(member) 96 | return self.tbl[member] 97 | end 98 | 99 | function mt:member_by_rank(r) 100 | return self.sl:get_member_by_rank(r) 101 | end 102 | 103 | function mt:member_by_rev_rank(r) 104 | r = self:_reverse_rank(r) 105 | if r > 0 then 106 | return self.sl:get_member_by_rank(r) 107 | end 108 | end 109 | 110 | function mt:dump() 111 | self.sl:dump() 112 | end 113 | 114 | local M = {} 115 | function M.new(delete_handler) 116 | local obj = {} 117 | obj.sl = skiplist() 118 | obj.tbl = {} 119 | obj.ts = {} 120 | obj.delete_function = function(member) 121 | obj.tbl[member] = nil 122 | obj.ts[member] = nil 123 | if delete_handler then 124 | delete_handler(member) 125 | end 126 | end 127 | return setmetatable(obj, mt) 128 | end 129 | return M 130 | 131 | -------------------------------------------------------------------------------- /lua-skiplist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * author: xjdrew 3 | * date: 2014-06-03 20:38 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #include "lua.h" 10 | #include "lauxlib.h" 11 | #include "skiplist.h" 12 | 13 | static inline skiplist* 14 | _to_skiplist(lua_State *L) { 15 | skiplist **sl = lua_touserdata(L, 1); 16 | if(sl==NULL) { 17 | luaL_error(L, "must be skiplist object"); 18 | } 19 | return *sl; 20 | } 21 | 22 | static int 23 | _insert(lua_State *L) { 24 | skiplist *sl = _to_skiplist(L); 25 | double score = luaL_checknumber(L, 2); 26 | luaL_checktype(L, 3, LUA_TSTRING); 27 | size_t len; 28 | const char* ptr = lua_tolstring(L, 3, &len); 29 | slobj *obj = slCreateObj(ptr, len); 30 | double timestamp = 0; 31 | if (lua_gettop(L) >= 4) { 32 | timestamp = lua_tonumber(L, -1); 33 | } 34 | slInsert(sl, score, obj, timestamp); 35 | return 0; 36 | } 37 | 38 | static int 39 | _delete(lua_State *L) { 40 | skiplist *sl = _to_skiplist(L); 41 | double score = luaL_checknumber(L, 2); 42 | luaL_checktype(L, 3, LUA_TSTRING); 43 | slobj obj; 44 | obj.ptr = (char *)lua_tolstring(L, 3, &obj.length); 45 | double timestamp = 0; 46 | if (lua_gettop(L) >= 4) { 47 | timestamp = lua_tonumber(L, -1); 48 | } 49 | lua_pushboolean(L, slDelete(sl, score, &obj, timestamp)); 50 | return 1; 51 | } 52 | 53 | static void 54 | _delete_cb(void* ud, slobj *obj) { 55 | lua_State *L = (lua_State*)ud; 56 | lua_pushvalue(L, 4); 57 | lua_pushlstring(L, obj->ptr, obj->length); 58 | lua_call(L, 1, 0); 59 | } 60 | 61 | static int 62 | _delete_by_score(lua_State *L) { 63 | skiplist *sl = _to_skiplist(L); 64 | double min = luaL_checkinteger(L, 2); 65 | double max = luaL_checkinteger(L, 3); 66 | luaL_checktype(L, 4, LUA_TFUNCTION); 67 | if (min > max) { 68 | double tmp = min; 69 | min = max; 70 | max = tmp; 71 | } 72 | 73 | lua_pushinteger(L, slDeleteByScore(sl, min, max, _delete_cb, L)); 74 | return 1; 75 | } 76 | 77 | static int 78 | _delete_by_rank(lua_State *L) { 79 | skiplist *sl = _to_skiplist(L); 80 | unsigned int start = luaL_checkinteger(L, 2); 81 | unsigned int end = luaL_checkinteger(L, 3); 82 | luaL_checktype(L, 4, LUA_TFUNCTION); 83 | if (start > end) { 84 | unsigned int tmp = start; 85 | start = end; 86 | end = tmp; 87 | } 88 | 89 | lua_pushinteger(L, slDeleteByRank(sl, start, end, _delete_cb, L)); 90 | return 1; 91 | } 92 | 93 | static int 94 | _get_count(lua_State *L) { 95 | skiplist *sl = _to_skiplist(L); 96 | lua_pushinteger(L, sl->length); 97 | return 1; 98 | } 99 | 100 | static int 101 | _get_rank(lua_State *L) { 102 | skiplist *sl = _to_skiplist(L); 103 | double score = luaL_checknumber(L, 2); 104 | luaL_checktype(L, 3, LUA_TSTRING); 105 | slobj obj; 106 | obj.ptr = (char *)lua_tolstring(L, 3, &obj.length); 107 | double timestamp = 0; 108 | if (lua_gettop(L) >= 4) { 109 | timestamp = lua_tonumber(L, -1); 110 | } 111 | 112 | unsigned long rank = slGetRank(sl, score, &obj, timestamp); 113 | if(rank == 0) { 114 | return 0; 115 | } 116 | 117 | lua_pushinteger(L, rank); 118 | 119 | return 1; 120 | } 121 | 122 | static int 123 | _get_rank_range(lua_State *L) { 124 | skiplist *sl = _to_skiplist(L); 125 | unsigned long r1 = luaL_checkinteger(L, 2); 126 | unsigned long r2 = luaL_checkinteger(L, 3); 127 | int reverse, rangelen; 128 | if(r1 <= r2) { 129 | reverse = 0; 130 | rangelen = r2 - r1 + 1; 131 | } else { 132 | reverse = 1; 133 | rangelen = r1 - r2 + 1; 134 | } 135 | 136 | skiplistNode* node = slGetNodeByRank(sl, r1); 137 | lua_createtable(L, rangelen, 0); 138 | int n = 0; 139 | while(node && n < rangelen) { 140 | n++; 141 | 142 | lua_pushlstring(L, node->obj->ptr, node->obj->length); 143 | lua_rawseti(L, -2, n); 144 | node = reverse? node->backward : node->level[0].forward; 145 | } 146 | return 1; 147 | } 148 | 149 | static int 150 | _get_score_range(lua_State *L) { 151 | skiplist *sl = _to_skiplist(L); 152 | double s1 = luaL_checknumber(L, 2); 153 | double s2 = luaL_checknumber(L, 3); 154 | int reverse; 155 | skiplistNode *node; 156 | 157 | if(s1 <= s2) { 158 | reverse = 0; 159 | node = slFirstInRange(sl, s1, s2); 160 | } else { 161 | reverse = 1; 162 | node = slLastInRange(sl, s2, s1); 163 | } 164 | 165 | lua_newtable(L); 166 | int n = 0; 167 | while(node) { 168 | if(reverse) { 169 | if(node->score < s2) break; 170 | } else { 171 | if(node->score > s2) break; 172 | } 173 | n++; 174 | 175 | lua_pushlstring(L, node->obj->ptr, node->obj->length); 176 | lua_rawseti(L, -2, n); 177 | 178 | node = reverse? node->backward:node->level[0].forward; 179 | } 180 | return 1; 181 | } 182 | 183 | static int 184 | _get_member_by_rank(lua_State *L){ 185 | skiplist *sl = _to_skiplist(L); 186 | unsigned long r = luaL_checkinteger(L, 2); 187 | skiplistNode *node = slGetNodeByRank(sl, r); 188 | if (node) { 189 | lua_pushlstring(L, node->obj->ptr, node->obj->length); 190 | return 1; 191 | } 192 | return 0; 193 | } 194 | 195 | static int 196 | _dump(lua_State *L) { 197 | skiplist *sl = _to_skiplist(L); 198 | slDump(sl); 199 | return 0; 200 | } 201 | 202 | static int 203 | _new(lua_State *L) { 204 | skiplist *psl = slCreate(); 205 | 206 | skiplist **sl = (skiplist**) lua_newuserdata(L, sizeof(skiplist*)); 207 | *sl = psl; 208 | lua_pushvalue(L, lua_upvalueindex(1)); 209 | lua_setmetatable(L, -2); 210 | return 1; 211 | } 212 | 213 | static int 214 | _release(lua_State *L) { 215 | skiplist *sl = _to_skiplist(L); 216 | printf("collect sl:%p\n", sl); 217 | slFree(sl); 218 | return 0; 219 | } 220 | 221 | int luaopen_skiplist_c(lua_State *L) { 222 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM > 501 223 | luaL_checkversion(L); 224 | #endif 225 | 226 | luaL_Reg l[] = { 227 | {"insert", _insert}, 228 | {"delete", _delete}, 229 | {"delete_by_score", _delete_by_score}, 230 | {"delete_by_rank", _delete_by_rank}, 231 | 232 | {"get_count", _get_count}, 233 | {"get_rank", _get_rank}, 234 | {"get_rank_range", _get_rank_range}, 235 | {"get_score_range", _get_score_range}, 236 | {"get_member_by_rank", _get_member_by_rank}, 237 | 238 | {"dump", _dump}, 239 | {NULL, NULL} 240 | }; 241 | 242 | lua_createtable(L, 0, 2); 243 | 244 | luaL_newlib(L, l); 245 | lua_setfield(L, -2, "__index"); 246 | lua_pushcfunction(L, _release); 247 | lua_setfield(L, -2, "__gc"); 248 | 249 | lua_pushcclosure(L, _new, 1); 250 | return 1; 251 | } 252 | 253 | -------------------------------------------------------------------------------- /skiplist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * author: xjdrew 3 | * date: 2014-06-03 20:38 4 | */ 5 | 6 | // skiplist similar with the version in redis 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "skiplist.h" 13 | 14 | 15 | skiplistNode *slCreateNode(int level, double score, slobj *obj, double timestamp) { 16 | skiplistNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel)); 17 | n->score = score; 18 | n->obj = obj; 19 | n->timestamp = timestamp; 20 | return n; 21 | } 22 | 23 | skiplist *slCreate(void) { 24 | int j; 25 | skiplist *sl; 26 | 27 | sl = malloc(sizeof(*sl)); 28 | sl->level = 1; 29 | sl->length = 0; 30 | sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0, NULL, 0); 31 | for (j=0; j < SKIPLIST_MAXLEVEL; j++) { 32 | sl->header->level[j].forward = NULL; 33 | sl->header->level[j].span = 0; 34 | } 35 | sl->header->backward = NULL; 36 | sl->tail = NULL; 37 | return sl; 38 | } 39 | 40 | slobj* slCreateObj(const char* ptr, size_t length) { 41 | slobj *obj = malloc(sizeof(*obj)); 42 | obj->ptr = malloc(length + 1); 43 | 44 | if(ptr) { 45 | memcpy(obj->ptr, ptr, length); 46 | } 47 | obj->ptr[length] = '\0'; 48 | 49 | obj->length = length; 50 | return obj; 51 | } 52 | 53 | void slFreeObj(slobj *obj) { 54 | free(obj->ptr); 55 | free(obj); 56 | } 57 | 58 | void slFreeNode(skiplistNode *node) { 59 | slFreeObj(node->obj); 60 | free(node); 61 | } 62 | 63 | void slFree(skiplist *sl) { 64 | skiplistNode *node = sl->header->level[0].forward, *next; 65 | 66 | free(sl->header); 67 | while(node) { 68 | next = node->level[0].forward; 69 | slFreeNode(node); 70 | node = next; 71 | } 72 | free(sl); 73 | } 74 | 75 | int slRandomLevel(void) { 76 | int level = 1; 77 | while((random() & 0xffff) < (SKIPLIST_P * 0xffff)) 78 | level += 1; 79 | return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL; 80 | } 81 | 82 | int compareslObj(slobj *a, slobj *b) { 83 | int cmp = memcmp(a->ptr, b->ptr, a->length <= b->length ? a->length : b->length); 84 | if(cmp == 0) return a->length - b->length; 85 | return cmp; 86 | } 87 | 88 | int equalslObj(slobj *a, slobj *b) { 89 | return compareslObj(a, b) == 0; 90 | } 91 | 92 | int compare(skiplistNode *node, double score, slobj *obj, double timestamp) { 93 | if (score != node->score) { 94 | return (score > node->score) ? -1 : 1; 95 | } 96 | if (timestamp != node->timestamp) { 97 | return (timestamp < node->timestamp) ? -1 : 1; 98 | } 99 | return compareslObj(node->obj,obj); 100 | } 101 | 102 | void slInsert(skiplist *sl, double score, slobj *obj, double timestamp) { 103 | skiplistNode *update[SKIPLIST_MAXLEVEL], *x; 104 | unsigned int rank[SKIPLIST_MAXLEVEL]; 105 | int i, level; 106 | 107 | x = sl->header; 108 | for (i = sl->level-1; i >= 0; i--) { 109 | /* store rank that is crossed to reach the insert position */ 110 | rank[i] = i == (sl->level-1) ? 0 : rank[i+1]; 111 | while (x->level[i].forward && compare(x->level[i].forward, score, obj, timestamp) < 0) { 112 | rank[i] += x->level[i].span; 113 | x = x->level[i].forward; 114 | } 115 | update[i] = x; 116 | } 117 | /* we assume the key is not already inside, since we allow duplicated 118 | * scores, and the re-insertion of score and redis object should never 119 | * happen since the caller of slInsert() should test in the hash table 120 | * if the element is already inside or not. */ 121 | level = slRandomLevel(); 122 | if (level > sl->level) { 123 | for (i = sl->level; i < level; i++) { 124 | rank[i] = 0; 125 | update[i] = sl->header; 126 | update[i]->level[i].span = sl->length; 127 | } 128 | sl->level = level; 129 | } 130 | x = slCreateNode(level,score,obj,timestamp); 131 | for (i = 0; i < level; i++) { 132 | x->level[i].forward = update[i]->level[i].forward; 133 | update[i]->level[i].forward = x; 134 | 135 | /* update span covered by update[i] as x is inserted here */ 136 | x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]); 137 | update[i]->level[i].span = (rank[0] - rank[i]) + 1; 138 | } 139 | 140 | /* increment span for untouched levels */ 141 | for (i = level; i < sl->level; i++) { 142 | update[i]->level[i].span++; 143 | } 144 | 145 | x->backward = (update[0] == sl->header) ? NULL : update[0]; 146 | if (x->level[0].forward) 147 | x->level[0].forward->backward = x; 148 | else 149 | sl->tail = x; 150 | sl->length++; 151 | } 152 | 153 | /* Internal function used by slDelete, slDeleteByScore */ 154 | void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) { 155 | int i; 156 | for (i = 0; i < sl->level; i++) { 157 | if (update[i]->level[i].forward == x) { 158 | update[i]->level[i].span += x->level[i].span - 1; 159 | update[i]->level[i].forward = x->level[i].forward; 160 | } else { 161 | update[i]->level[i].span -= 1; 162 | } 163 | } 164 | if (x->level[0].forward) { 165 | x->level[0].forward->backward = x->backward; 166 | } else { 167 | sl->tail = x->backward; 168 | } 169 | while(sl->level > 1 && sl->header->level[sl->level-1].forward == NULL) 170 | sl->level--; 171 | sl->length--; 172 | } 173 | 174 | /* Delete an element with matching score/object from the skiplist. */ 175 | int slDelete(skiplist *sl, double score, slobj *obj, double timestamp) { 176 | skiplistNode *update[SKIPLIST_MAXLEVEL], *x; 177 | int i; 178 | 179 | x = sl->header; 180 | for (i = sl->level-1; i >= 0; i--) { 181 | while (x->level[i].forward && compare(x->level[i].forward, score, obj, timestamp) < 0) 182 | x = x->level[i].forward; 183 | update[i] = x; 184 | } 185 | /* We may have multiple elements with the same score, what we need 186 | * is to find the element with both the right score and object. */ 187 | x = x->level[0].forward; 188 | if (x && score == x->score && equalslObj(x->obj,obj)) { 189 | slDeleteNode(sl, x, update); 190 | slFreeNode(x); 191 | return 1; 192 | } else { 193 | return 0; /* not found */ 194 | } 195 | return 0; /* not found */ 196 | } 197 | 198 | /* Delete all the elements with score between min and max from the skiplist. 199 | * Both min and max are inclusive. */ 200 | unsigned long slDeleteByScore(skiplist *sl, double min, double max, slDeleteCb cb, void* ud) { 201 | skiplistNode *update[SKIPLIST_MAXLEVEL], *x; 202 | unsigned long removed = 0; 203 | int i; 204 | 205 | x = sl->header; 206 | for (i = sl->level-1; i >= 0; i--) { 207 | while (x->level[i].forward && x->level[i].forward->score < min) 208 | x = x->level[i].forward; 209 | update[i] = x; 210 | } 211 | 212 | /* Current node is the last with score < min. */ 213 | x = x->level[0].forward; 214 | 215 | /* Delete nodes while in range. */ 216 | while (x && x->score <= max) { 217 | skiplistNode *next = x->level[0].forward; 218 | slDeleteNode(sl,x,update); 219 | cb(ud, x->obj); 220 | slFreeNode(x); /* Here is where x->ele is actually released. */ 221 | removed++; 222 | x = next; 223 | } 224 | return removed; 225 | } 226 | 227 | /* Delete all the elements with rank between start and end from the skiplist. 228 | * Start and end are inclusive. Note that start and end need to be 1-based */ 229 | unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void* ud) { 230 | skiplistNode *update[SKIPLIST_MAXLEVEL], *x; 231 | unsigned long traversed = 0, removed = 0; 232 | int i; 233 | 234 | x = sl->header; 235 | for (i = sl->level-1; i >= 0; i--) { 236 | while (x->level[i].forward && (traversed + x->level[i].span) < start) { 237 | traversed += x->level[i].span; 238 | x = x->level[i].forward; 239 | } 240 | update[i] = x; 241 | } 242 | 243 | traversed++; 244 | x = x->level[0].forward; 245 | while (x && traversed <= end) { 246 | skiplistNode *next = x->level[0].forward; 247 | slDeleteNode(sl,x,update); 248 | cb(ud, x->obj); 249 | slFreeNode(x); 250 | removed++; 251 | traversed++; 252 | x = next; 253 | } 254 | return removed; 255 | } 256 | 257 | /* Find the rank for an element by both score and key. 258 | * Returns 0 when the element cannot be found, rank otherwise. 259 | * Note that the rank is 1-based due to the span of sl->header to the 260 | * first element. */ 261 | unsigned long slGetRank(skiplist *sl, double score, slobj *o, double timestamp) { 262 | skiplistNode *x; 263 | unsigned long rank = 0; 264 | int i; 265 | 266 | x = sl->header; 267 | for (i = sl->level-1; i >= 0; i--) { 268 | while (x->level[i].forward && compare(x->level[i].forward, score, o, timestamp) <= 0) { 269 | rank += x->level[i].span; 270 | x = x->level[i].forward; 271 | } 272 | 273 | /* x might be equal to sl->header, so test if obj is non-NULL */ 274 | if (x->obj && equalslObj(x->obj, o)) { 275 | return rank; 276 | } 277 | } 278 | return 0; 279 | } 280 | 281 | /* Finds an element by its rank. The rank argument needs to be 1-based. */ 282 | skiplistNode* slGetNodeByRank(skiplist *sl, unsigned long rank) { 283 | if(rank == 0 || rank > sl->length) { 284 | return NULL; 285 | } 286 | 287 | skiplistNode *x; 288 | unsigned long traversed = 0; 289 | int i; 290 | 291 | x = sl->header; 292 | for (i = sl->level-1; i >= 0; i--) { 293 | while (x->level[i].forward && (traversed + x->level[i].span) <= rank) 294 | { 295 | traversed += x->level[i].span; 296 | x = x->level[i].forward; 297 | } 298 | if (traversed == rank) { 299 | return x; 300 | } 301 | } 302 | 303 | return NULL; 304 | } 305 | 306 | /* range [min, max], left & right both include */ 307 | /* Returns if there is a part of the zset is in range. */ 308 | int slIsInRange(skiplist *sl, double min, double max) { 309 | skiplistNode *x; 310 | 311 | /* Test for ranges that will always be empty. */ 312 | if(min > max) { 313 | return 0; 314 | } 315 | x = sl->tail; 316 | if (x == NULL || x->score < min) 317 | return 0; 318 | 319 | x = sl->header->level[0].forward; 320 | if (x == NULL || x->score > max) 321 | return 0; 322 | return 1; 323 | } 324 | 325 | /* Find the first node that is contained in the specified range. 326 | * Returns NULL when no element is contained in the range. */ 327 | skiplistNode *slFirstInRange(skiplist *sl, double min, double max) { 328 | skiplistNode *x; 329 | int i; 330 | 331 | /* If everything is out of range, return early. */ 332 | if (!slIsInRange(sl,min, max)) return NULL; 333 | 334 | x = sl->header; 335 | for (i = sl->level-1; i >= 0; i--) { 336 | /* Go forward while *OUT* of range. */ 337 | while (x->level[i].forward && x->level[i].forward->score < min) 338 | x = x->level[i].forward; 339 | } 340 | 341 | /* This is an inner range, so the next node cannot be NULL. */ 342 | x = x->level[0].forward; 343 | return x; 344 | } 345 | 346 | /* Find the last node that is contained in the specified range. 347 | * Returns NULL when no element is contained in the range. */ 348 | skiplistNode *slLastInRange(skiplist *sl, double min, double max) { 349 | skiplistNode *x; 350 | int i; 351 | 352 | /* If everything is out of range, return early. */ 353 | if (!slIsInRange(sl, min, max)) return NULL; 354 | 355 | x = sl->header; 356 | for (i = sl->level-1; i >= 0; i--) { 357 | /* Go forward while *IN* range. */ 358 | while (x->level[i].forward && 359 | x->level[i].forward->score <= max) 360 | x = x->level[i].forward; 361 | } 362 | 363 | /* This is an inner range, so this node cannot be NULL. */ 364 | return x; 365 | } 366 | 367 | void slDump(skiplist *sl) { 368 | skiplistNode *x; 369 | int i; 370 | 371 | x = sl->header; 372 | i = 0; 373 | while(x->level[0].forward) { 374 | x = x->level[0].forward; 375 | i++; 376 | printf("node %d: score:%f, member:%s ts:%f\n", i, x->score, x->obj->ptr, x->timestamp); 377 | } 378 | } 379 | 380 | --------------------------------------------------------------------------------