@ -0,0 +1,266 @@ | |||||
/* ex: ts=4 sw=4 et | |||||
* | |||||
* Copyright (c) 2011-2016 Sergey Urbanovich | |||||
* http://github.com/urbanserj/cbase64-erlang-nif | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
* of this software and associated documentation files (the "Software"), to deal | |||||
* in the Software without restriction, including without limitation the rights | |||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
* copies of the Software, and to permit persons to whom the Software is | |||||
* furnished to do so, subject to the following conditions: | |||||
* | |||||
* The above copyright notice and this permission notice shall be included in | |||||
* all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
* THE SOFTWARE. | |||||
*/ | |||||
#include "stdint.h" | |||||
#include "erl_nif.h" | |||||
#define min(X, Y) ((X) < (Y) ? (X) : (Y)) | |||||
#define TIMESLICE 10 | |||||
#define SPEEDUP 30 | |||||
#define REDUCTIONS 2000 | |||||
#define ITER (REDUCTIONS * SPEEDUP / TIMESLICE) | |||||
static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |||||
inline void encodeblock(const uint8_t in[3], uint8_t out[4], size_t len) | |||||
{ | |||||
out[0] = cb64[ (in[0] >> 2) & 0x3f ]; | |||||
out[1] = cb64[ ((in[0] << 4) + (--len ? in[1] >> 4 : 0)) & 0x3f ]; | |||||
out[2] = len ? cb64[ ((in[1] << 2) + (--len ? in[2] >> 6 : 0)) & 0x3f ] : '='; | |||||
out[3] = len ? cb64[ in[2] & 0x3f ] : '='; | |||||
} | |||||
static ERL_NIF_TERM encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) | |||||
{ | |||||
ERL_NIF_TERM tdst, tsrc; | |||||
ErlNifBinary src, dst; | |||||
if ( enif_inspect_binary(env, argv[0], &src) ) { | |||||
tsrc = argv[0]; | |||||
} else if ( enif_inspect_iolist_as_binary(env, argv[0], &src) ) { | |||||
tsrc = enif_make_binary(env, &src); | |||||
} else { | |||||
return enif_make_badarg(env); | |||||
} | |||||
if (src.size == 0) { | |||||
return argv[0]; | |||||
} | |||||
uint8_t *data; | |||||
size_t size = (src.size + 2) / 3 * 4; | |||||
if (argc == 1) { | |||||
data = enif_make_new_binary(env, size, &tdst); | |||||
} else { | |||||
tdst = argv[1]; | |||||
if ( !enif_inspect_binary(env, argv[1], &dst) ) { | |||||
return enif_make_badarg(env); | |||||
} | |||||
data = dst.data; | |||||
} | |||||
int i = size / 4 - 1; | |||||
if (argc == 3) { | |||||
if ( !enif_get_int(env, argv[2], &i) ) { | |||||
return enif_make_badarg(env); | |||||
} | |||||
} else { | |||||
/* last block */ | |||||
encodeblock(src.data + i * 3, data + i * 4, src.size - i * 3); | |||||
i--; | |||||
} | |||||
int reductions = 0; | |||||
for (; i >= 0; i--) { | |||||
encodeblock(src.data + i * 3, data + i * 4, 3); | |||||
reductions += 3; | |||||
if (reductions >= ITER) { | |||||
if (enif_consume_timeslice(env, TIMESLICE)) { | |||||
ERL_NIF_TERM schargv[3] = {tsrc, tdst, enif_make_int(env, i)}; | |||||
return enif_schedule_nif(env, "encode", 0, &encode, 3, schargv); | |||||
} | |||||
reductions = 0; | |||||
} | |||||
} | |||||
return tdst; | |||||
} | |||||
#define DECODE_ERROR 0xff | |||||
#define DECODE_OK 0x00 | |||||
#define B64(_) \ | |||||
( (_) >= 'A' && (_) <= 'Z' ? 25 - 'Z' + (_) : \ | |||||
(_) >= 'a' && (_) <= 'z' ? 51 - 'z' + (_) : \ | |||||
(_) >= '0' && (_) <= '9' ? 61 - '9' + (_) : \ | |||||
(_) == '+' ? 62 : (_) ? 63 : DECODE_ERROR ) | |||||
static const int cd64[256] = { | |||||
B64( 0), B64( 1), B64( 2), B64( 3), B64( 4), B64( 5), B64( 6), B64( 7), | |||||
B64( 8), B64( 9), B64( 10), B64( 11), B64( 12), B64( 13), B64( 14), B64( 15), | |||||
B64( 16), B64( 17), B64( 18), B64( 19), B64( 20), B64( 21), B64( 22), B64( 23), | |||||
B64( 24), B64( 25), B64( 26), B64( 27), B64( 28), B64( 29), B64( 30), B64( 31), | |||||
B64( 32), B64( 33), B64( 34), B64( 35), B64( 36), B64( 37), B64( 38), B64( 39), | |||||
B64( 40), B64( 41), B64( 42), B64( 43), B64( 44), B64( 45), B64( 46), B64( 47), | |||||
B64( 48), B64( 49), B64( 50), B64( 51), B64( 52), B64( 53), B64( 54), B64( 55), | |||||
B64( 56), B64( 57), B64( 58), B64( 59), B64( 60), B64( 61), B64( 62), B64( 63), | |||||
B64( 64), B64( 65), B64( 66), B64( 67), B64( 68), B64( 69), B64( 70), B64( 71), | |||||
B64( 72), B64( 73), B64( 74), B64( 75), B64( 76), B64( 77), B64( 78), B64( 79), | |||||
B64( 80), B64( 81), B64( 82), B64( 83), B64( 84), B64( 85), B64( 86), B64( 87), | |||||
B64( 88), B64( 89), B64( 90), B64( 91), B64( 92), B64( 93), B64( 94), B64( 95), | |||||
B64( 96), B64( 97), B64( 98), B64( 99), B64(100), B64(101), B64(102), B64(103), | |||||
B64(104), B64(105), B64(106), B64(107), B64(108), B64(109), B64(110), B64(111), | |||||
B64(112), B64(113), B64(114), B64(115), B64(116), B64(117), B64(118), B64(119), | |||||
B64(120), B64(121), B64(122), B64(123), B64(124), B64(125), B64(126), B64(127), | |||||
B64(128), B64(129), B64(130), B64(131), B64(132), B64(133), B64(134), B64(135), | |||||
B64(136), B64(137), B64(138), B64(139), B64(140), B64(141), B64(142), B64(143), | |||||
B64(144), B64(145), B64(146), B64(147), B64(148), B64(149), B64(150), B64(151), | |||||
B64(152), B64(153), B64(154), B64(155), B64(156), B64(157), B64(158), B64(159), | |||||
B64(160), B64(161), B64(162), B64(163), B64(164), B64(165), B64(166), B64(167), | |||||
B64(168), B64(169), B64(170), B64(171), B64(172), B64(173), B64(174), B64(175), | |||||
B64(176), B64(177), B64(178), B64(179), B64(180), B64(181), B64(182), B64(183), | |||||
B64(184), B64(185), B64(186), B64(187), B64(188), B64(189), B64(190), B64(191), | |||||
B64(192), B64(193), B64(194), B64(195), B64(196), B64(197), B64(198), B64(199), | |||||
B64(200), B64(201), B64(202), B64(203), B64(204), B64(205), B64(206), B64(207), | |||||
B64(208), B64(209), B64(210), B64(211), B64(212), B64(213), B64(214), B64(215), | |||||
B64(216), B64(217), B64(218), B64(219), B64(220), B64(221), B64(222), B64(223), | |||||
B64(224), B64(225), B64(226), B64(227), B64(228), B64(229), B64(230), B64(231), | |||||
B64(232), B64(233), B64(234), B64(235), B64(236), B64(237), B64(238), B64(239), | |||||
B64(240), B64(241), B64(242), B64(243), B64(244), B64(245), B64(246), B64(247), | |||||
B64(248), B64(249), B64(250), B64(251), B64(252), B64(253), B64(254), B64(255) | |||||
}; | |||||
inline uint8_t decodeblock(const uint8_t in[4], uint8_t out[3]) | |||||
{ | |||||
int code; | |||||
size_t it = 0; | |||||
unsigned int val = 0; | |||||
for (;it < 4; ++it) { | |||||
if ( (code = cd64[in[it]]) == DECODE_ERROR ) | |||||
return DECODE_ERROR; | |||||
val = (val << 6) + code; | |||||
} | |||||
out[0] = (val >> 16) & 0xff; | |||||
out[1] = (val >> 8) & 0xff; | |||||
out[2] = val & 0xff; | |||||
return DECODE_OK; | |||||
} | |||||
inline uint8_t decodeblock_tail(const uint8_t in[4], uint8_t out[3]) | |||||
{ | |||||
int code; | |||||
size_t it = 0; | |||||
unsigned int val = 0; | |||||
if ( in[0] == '=' || in[1] == '=' || | |||||
(in[2] == '=' && in[3] != '=') ) | |||||
return DECODE_ERROR; | |||||
for (;it < 4; ++it) { | |||||
val <<= 6; | |||||
if ( in[it] == '=' ) | |||||
continue; | |||||
if ( (code = cd64[in[it]]) == DECODE_ERROR ) | |||||
return DECODE_ERROR; | |||||
val += code; | |||||
} | |||||
out[0] = (val >> 16) & 0xff; | |||||
if ( in[2] != '=' ) | |||||
out[1] = (val >> 8) & 0xff; | |||||
if ( in[3] != '=' ) | |||||
out[2] = val & 0xff; | |||||
return DECODE_OK; | |||||
} | |||||
static ERL_NIF_TERM decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) | |||||
{ | |||||
ERL_NIF_TERM tdst, tsrc; | |||||
ErlNifBinary src, dst; | |||||
if ( enif_inspect_binary(env, argv[0], &src) ) { | |||||
tsrc = argv[0]; | |||||
} else if ( enif_inspect_iolist_as_binary(env, argv[0], &src) ) { | |||||
tsrc = enif_make_binary(env, &src); | |||||
} else { | |||||
return enif_make_badarg(env); | |||||
} | |||||
if (src.size == 0) { | |||||
return argv[0]; | |||||
} | |||||
if (src.size % 4 != 0) { | |||||
return enif_make_badarg(env); | |||||
} | |||||
uint8_t *data; | |||||
size_t size = | |||||
src.size/4*3 | |||||
- (src.data[src.size - 1] == '=' ? 1 : 0) | |||||
- (src.data[src.size - 2] == '=' ? 1 : 0); | |||||
if (argc == 1) { | |||||
data = enif_make_new_binary(env, size, &tdst); | |||||
} else { | |||||
tdst = argv[1]; | |||||
if ( !enif_inspect_binary(env, argv[1], &dst) ) { | |||||
return enif_make_badarg(env); | |||||
} | |||||
data = dst.data; | |||||
} | |||||
int i = src.size / 4 - 1; | |||||
if (argc == 3) { | |||||
if ( !enif_get_int(env, argv[2], &i) ) { | |||||
return enif_make_badarg(env); | |||||
} | |||||
} else { | |||||
/* last block */ | |||||
if ( decodeblock_tail(src.data + i*4, data + i*3) ) { | |||||
return enif_make_badarg(env); | |||||
} | |||||
i--; | |||||
} | |||||
int reductions = 0; | |||||
for (; i >= 0; i--) { | |||||
if ( decodeblock(src.data + i*4, data + i*3) ) { | |||||
return enif_make_badarg(env); | |||||
} | |||||
reductions += 4; | |||||
if (reductions >= ITER) { | |||||
if (enif_consume_timeslice(env, TIMESLICE)) { | |||||
ERL_NIF_TERM schargv[3] = {tsrc, tdst, enif_make_int(env, i)}; | |||||
return enif_schedule_nif(env, "decode", 0, &decode, 3, schargv); | |||||
} | |||||
reductions = 0; | |||||
} | |||||
} | |||||
return tdst; | |||||
} | |||||
static ErlNifFunc nif_funcs[] = | |||||
{ | |||||
{"encode", 1, encode}, | |||||
{"decode", 1, decode} | |||||
}; | |||||
ERL_NIF_INIT(cbase64, nif_funcs, NULL, NULL, NULL, NULL) |
@ -0,0 +1,9 @@ | |||||
{port_specs, [ | |||||
{"../../priv/cbase64.so", ["*.c"]} | |||||
]}. | |||||
{port_env, [{".*", "CFLAGS", "$CFLAGS -Wall -O2"}]}. | |||||
@ -0,0 +1,688 @@ | |||||
/* | |||||
* @author Evgeny Khramtsov <ekhramtsov@process-one.net> | |||||
* @copyright (C) 2002-2020 ProcessOne, SARL. All Rights Reserved. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
* | |||||
*/ | |||||
#include <erl_nif.h> | |||||
#include <stdio.h> | |||||
#include <errno.h> | |||||
#include "uthash.h" | |||||
void __free(void *ptr, size_t size) { | |||||
enif_free(ptr); | |||||
} | |||||
#undef uthash_malloc | |||||
#undef uthash_free | |||||
#define uthash_malloc enif_alloc | |||||
#define uthash_free __free | |||||
/**************************************************************** | |||||
* Structures/Globals definitions * | |||||
****************************************************************/ | |||||
typedef struct __tree_t { | |||||
char *key; | |||||
char *val; | |||||
int refc; | |||||
struct __tree_t *sub; | |||||
UT_hash_handle hh; | |||||
} tree_t; | |||||
typedef struct { | |||||
tree_t *tree; | |||||
char *name; | |||||
ErlNifRWLock *lock; | |||||
} state_t; | |||||
typedef struct { | |||||
char *name; | |||||
state_t *state; | |||||
UT_hash_handle hh; | |||||
} registry_t; | |||||
static ErlNifResourceType *tree_state_t = NULL; | |||||
static registry_t *registry = NULL; | |||||
static ErlNifRWLock *registry_lock = NULL; | |||||
/**************************************************************** | |||||
* MQTT Tree Manipulation * | |||||
****************************************************************/ | |||||
tree_t *tree_new(char *key, size_t len) { | |||||
tree_t *tree = enif_alloc(sizeof(tree_t)); | |||||
if (tree) { | |||||
memset(tree, 0, sizeof(tree_t)); | |||||
if (key && len) { | |||||
tree->key = enif_alloc(len); | |||||
if (tree->key) { | |||||
memcpy(tree->key, key, len); | |||||
} else { | |||||
enif_free(tree); | |||||
tree = NULL; | |||||
} | |||||
} | |||||
} | |||||
return tree; | |||||
} | |||||
void tree_free(tree_t *t) { | |||||
tree_t *found, *iter; | |||||
if (t) { | |||||
enif_free(t->key); | |||||
enif_free(t->val); | |||||
HASH_ITER(hh, t->sub, found, iter) { | |||||
HASH_DEL(t->sub, found); | |||||
tree_free(found); | |||||
} | |||||
memset(t, 0, sizeof(tree_t)); | |||||
enif_free(t); | |||||
} | |||||
} | |||||
void tree_clear(tree_t *root) { | |||||
tree_t *found, *iter; | |||||
HASH_ITER(hh, root->sub, found, iter) { | |||||
HASH_DEL(root->sub, found); | |||||
tree_free(found); | |||||
} | |||||
} | |||||
int tree_add(tree_t *root, char *path, size_t size) { | |||||
int i = 0; | |||||
size_t len; | |||||
tree_t *t = root; | |||||
tree_t *found, *new; | |||||
while (i<=size) { | |||||
len = strlen(path+i) + 1; | |||||
HASH_FIND_STR(t->sub, path+i, found); | |||||
if (found) { | |||||
i += len; | |||||
t = found; | |||||
} else { | |||||
new = tree_new(path+i, len); | |||||
if (new) { | |||||
HASH_ADD_STR(t->sub, key, new); | |||||
i += len; | |||||
t = new; | |||||
} else | |||||
return ENOMEM; | |||||
} | |||||
} | |||||
if (!t->val) { | |||||
t->val = enif_alloc(size+1); | |||||
if (t->val) { | |||||
t->val[size] = 0; | |||||
for (i=0; i<size; i++) { | |||||
char c = path[i]; | |||||
t->val[i] = c ? c : '/'; | |||||
} | |||||
} else | |||||
return ENOMEM; | |||||
} | |||||
t->refc++; | |||||
return 0; | |||||
} | |||||
int tree_del(tree_t *root, char *path, size_t i, size_t size) { | |||||
tree_t *found; | |||||
if (i<=size) { | |||||
HASH_FIND_STR(root->sub, path+i, found); | |||||
if (found) { | |||||
i += strlen(path+i) + 1; | |||||
int deleted = tree_del(found, path, i, size); | |||||
if (deleted) { | |||||
HASH_DEL(root->sub, found); | |||||
tree_free(found); | |||||
} | |||||
} | |||||
} else if (root->refc) { | |||||
root->refc--; | |||||
if (!root->refc) { | |||||
enif_free(root->val); | |||||
root->val = NULL; | |||||
} | |||||
} | |||||
return !root->refc && !root->sub; | |||||
} | |||||
void tree_size(tree_t *tree, size_t *size) { | |||||
tree_t *found, *iter; | |||||
HASH_ITER(hh, tree->sub, found, iter) { | |||||
if (found->refc) (*size)++; | |||||
tree_size(found, size); | |||||
} | |||||
} | |||||
int tree_refc(tree_t *tree, char *path, size_t i, size_t size) { | |||||
tree_t *found; | |||||
if (i<=size) { | |||||
HASH_FIND_STR(tree->sub, path+i, found); | |||||
if (found) { | |||||
i += strlen(path+i) + 1; | |||||
return tree_refc(found, path, i, size); | |||||
} else { | |||||
return 0; | |||||
} | |||||
} else | |||||
return tree->refc; | |||||
} | |||||
/**************************************************************** | |||||
* Registration * | |||||
****************************************************************/ | |||||
void delete_registry_entry(registry_t *entry) { | |||||
/* registry_lock must be RW-locked! */ | |||||
HASH_DEL(registry, entry); | |||||
entry->state->name = NULL; | |||||
enif_release_resource(entry->state); | |||||
enif_free(entry->name); | |||||
enif_free(entry); | |||||
} | |||||
int register_tree(char *name, state_t *state) { | |||||
registry_t *entry, *found; | |||||
entry = enif_alloc(sizeof(registry_t)); | |||||
if (!entry) return ENOMEM; | |||||
entry->name = enif_alloc(strlen(name) + 1); | |||||
if (!entry->name) { | |||||
free(entry); | |||||
return ENOMEM; | |||||
} | |||||
entry->state = state; | |||||
strcpy(entry->name, name); | |||||
enif_rwlock_rwlock(registry_lock); | |||||
HASH_FIND_STR(registry, name, found); | |||||
if (found) { | |||||
enif_rwlock_rwunlock(registry_lock); | |||||
enif_free(entry->name); | |||||
enif_free(entry); | |||||
return EINVAL; | |||||
} else { | |||||
if (state->name) { | |||||
/* Unregistering previously registered name */ | |||||
HASH_FIND_STR(registry, state->name, found); | |||||
if (found) | |||||
delete_registry_entry(found); | |||||
} | |||||
enif_keep_resource(state); | |||||
HASH_ADD_STR(registry, name, entry); | |||||
state->name = entry->name; | |||||
enif_rwlock_rwunlock(registry_lock); | |||||
return 0; | |||||
} | |||||
} | |||||
int unregister_tree(char *name) { | |||||
registry_t *entry; | |||||
int ret; | |||||
enif_rwlock_rwlock(registry_lock); | |||||
HASH_FIND_STR(registry, name, entry); | |||||
if (entry) { | |||||
delete_registry_entry(entry); | |||||
ret = 0; | |||||
} else { | |||||
ret = EINVAL; | |||||
} | |||||
enif_rwlock_rwunlock(registry_lock); | |||||
return ret; | |||||
} | |||||
/**************************************************************** | |||||
* NIF helpers * | |||||
****************************************************************/ | |||||
static ERL_NIF_TERM cons(ErlNifEnv *env, char *str, ERL_NIF_TERM tail) | |||||
{ | |||||
if (str) { | |||||
size_t len = strlen(str); | |||||
ERL_NIF_TERM head; | |||||
unsigned char *buf = enif_make_new_binary(env, len, &head); | |||||
if (buf) { | |||||
memcpy(buf, str, len); | |||||
return enif_make_list_cell(env, head, tail); | |||||
} | |||||
} | |||||
return tail; | |||||
} | |||||
static void match(ErlNifEnv *env, tree_t *root, | |||||
char *path, size_t i, size_t size, ERL_NIF_TERM *acc) | |||||
{ | |||||
tree_t *found; | |||||
size_t len = 0; | |||||
if (i<=size) { | |||||
HASH_FIND_STR(root->sub, path+i, found); | |||||
if (found) { | |||||
len = strlen(path+i) + 1; | |||||
match(env, found, path, i+len, size, acc); | |||||
}; | |||||
if (i || path[0] != '$') { | |||||
HASH_FIND_STR(root->sub, "+", found); | |||||
if (found) { | |||||
len = strlen(path+i) + 1; | |||||
match(env, found, path, i+len, size, acc); | |||||
} | |||||
HASH_FIND_STR(root->sub, "#", found); | |||||
if (found) { | |||||
*acc = cons(env, found->val, *acc); | |||||
} | |||||
} | |||||
} else { | |||||
*acc = cons(env, root->val, *acc); | |||||
HASH_FIND_STR(root->sub, "#", found); | |||||
if (found) | |||||
*acc = cons(env, found->val, *acc); | |||||
} | |||||
} | |||||
static void to_list(ErlNifEnv *env, tree_t *root, ERL_NIF_TERM *acc) | |||||
{ | |||||
tree_t *found, *iter; | |||||
HASH_ITER(hh, root->sub, found, iter) { | |||||
if (found->val) { | |||||
size_t len = strlen(found->val); | |||||
ERL_NIF_TERM refc = enif_make_int(env, found->refc); | |||||
ERL_NIF_TERM val; | |||||
unsigned char *buf = enif_make_new_binary(env, len, &val); | |||||
if (buf) { | |||||
memcpy(buf, found->val, len); | |||||
*acc = enif_make_list_cell(env, enif_make_tuple2(env, val, refc), *acc); | |||||
} | |||||
}; | |||||
to_list(env, found, acc); | |||||
} | |||||
} | |||||
static ERL_NIF_TERM dump(ErlNifEnv *env, tree_t *tree) | |||||
{ | |||||
tree_t *found, *iter; | |||||
ERL_NIF_TERM tail, head; | |||||
tail = enif_make_list(env, 0); | |||||
HASH_ITER(hh, tree->sub, found, iter) { | |||||
head = dump(env, found); | |||||
tail = enif_make_list_cell(env, head, tail); | |||||
} | |||||
if (tree->key) { | |||||
ERL_NIF_TERM part, path; | |||||
part = enif_make_string(env, tree->key, ERL_NIF_LATIN1); | |||||
if (tree->val) | |||||
path = enif_make_string(env, tree->val, ERL_NIF_LATIN1); | |||||
else | |||||
path = enif_make_atom(env, "none"); | |||||
return enif_make_tuple4(env, part, path, enif_make_int(env, tree->refc), tail); | |||||
} else | |||||
return tail; | |||||
} | |||||
static ERL_NIF_TERM raise(ErlNifEnv *env, int err) | |||||
{ | |||||
switch (err) { | |||||
case ENOMEM: | |||||
return enif_raise_exception(env, enif_make_atom(env, "enomem")); | |||||
default: | |||||
return enif_make_badarg(env); | |||||
} | |||||
} | |||||
void prep_path(char *path, ErlNifBinary *bin) { | |||||
int i; | |||||
unsigned char c; | |||||
path[bin->size] = 0; | |||||
for (i=0; i<bin->size; i++) { | |||||
c = bin->data[i]; | |||||
path[i] = (c == '/') ? 0 : c; | |||||
} | |||||
} | |||||
/**************************************************************** | |||||
* Constructors/Destructors * | |||||
****************************************************************/ | |||||
static state_t *init_tree_state(ErlNifEnv *env) { | |||||
state_t *state = enif_alloc_resource(tree_state_t, sizeof(state_t)); | |||||
if (state) { | |||||
memset(state, 0, sizeof(state_t)); | |||||
state->tree = tree_new(NULL, 0); | |||||
state->lock = enif_rwlock_create("mqtree_lock"); | |||||
if (state->tree && state->lock) | |||||
return state; | |||||
else | |||||
enif_release_resource(state); | |||||
} | |||||
return NULL; | |||||
} | |||||
static void destroy_tree_state(ErlNifEnv *env, void *data) { | |||||
state_t *state = (state_t *) data; | |||||
if (state) { | |||||
tree_free(state->tree); | |||||
if (state->lock) enif_rwlock_destroy(state->lock); | |||||
} | |||||
memset(state, 0, sizeof(state_t)); | |||||
} | |||||
/**************************************************************** | |||||
* NIF definitions * | |||||
****************************************************************/ | |||||
static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM max) { | |||||
registry_lock = enif_rwlock_create("mqtree_registry"); | |||||
if (registry_lock) { | |||||
ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; | |||||
tree_state_t = enif_open_resource_type(env, NULL, "mqtree_state", | |||||
destroy_tree_state, | |||||
flags, NULL); | |||||
return 0; | |||||
} | |||||
return ENOMEM; | |||||
} | |||||
static void unload(ErlNifEnv* env, void* priv) { | |||||
if (registry_lock) { | |||||
enif_rwlock_destroy(registry_lock); | |||||
registry_lock = NULL; | |||||
} | |||||
} | |||||
static ERL_NIF_TERM new_0(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
ERL_NIF_TERM result; | |||||
state_t *state = init_tree_state(env); | |||||
if (state) { | |||||
result = enif_make_resource(env, state); | |||||
enif_release_resource(state); | |||||
} else | |||||
result = raise(env, ENOMEM); | |||||
return result; | |||||
} | |||||
static ERL_NIF_TERM insert_2(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
ErlNifBinary path_bin; | |||||
if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) || | |||||
!enif_inspect_iolist_as_binary(env, argv[1], &path_bin)) | |||||
return raise(env, EINVAL); | |||||
if (!path_bin.size) | |||||
return enif_make_atom(env, "ok"); | |||||
char path[path_bin.size+1]; | |||||
prep_path(path, &path_bin); | |||||
enif_rwlock_rwlock(state->lock); | |||||
int ret = tree_add(state->tree, path, path_bin.size); | |||||
enif_rwlock_rwunlock(state->lock); | |||||
if (!ret) | |||||
return enif_make_atom(env, "ok"); | |||||
else | |||||
return raise(env, ret); | |||||
} | |||||
static ERL_NIF_TERM delete_2(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
ErlNifBinary path_bin; | |||||
if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) || | |||||
!enif_inspect_iolist_as_binary(env, argv[1], &path_bin)) | |||||
return raise(env, EINVAL); | |||||
if (!path_bin.size) | |||||
return enif_make_atom(env, "ok"); | |||||
char path[path_bin.size+1]; | |||||
prep_path(path, &path_bin); | |||||
enif_rwlock_rwlock(state->lock); | |||||
tree_del(state->tree, path, 0, path_bin.size); | |||||
enif_rwlock_rwunlock(state->lock); | |||||
return enif_make_atom(env, "ok"); | |||||
} | |||||
static ERL_NIF_TERM match_2(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
ErlNifBinary path_bin; | |||||
ERL_NIF_TERM result = enif_make_list(env, 0); | |||||
if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) || | |||||
!enif_inspect_iolist_as_binary(env, argv[1], &path_bin)) | |||||
return raise(env, EINVAL); | |||||
if (!path_bin.size) | |||||
return result; | |||||
char path[path_bin.size+1]; | |||||
prep_path(path, &path_bin); | |||||
enif_rwlock_rlock(state->lock); | |||||
match(env, state->tree, path, 0, path_bin.size, &result); | |||||
enif_rwlock_runlock(state->lock); | |||||
return result; | |||||
} | |||||
static ERL_NIF_TERM refc_2(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
ErlNifBinary path_bin; | |||||
if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) || | |||||
!enif_inspect_iolist_as_binary(env, argv[1], &path_bin)) | |||||
return raise(env, EINVAL); | |||||
if (!path_bin.size) | |||||
return enif_make_int(env, 0); | |||||
char path[path_bin.size+1]; | |||||
prep_path(path, &path_bin); | |||||
enif_rwlock_rlock(state->lock); | |||||
int refc = tree_refc(state->tree, path, 0, path_bin.size); | |||||
enif_rwlock_runlock(state->lock); | |||||
return enif_make_int(env, refc); | |||||
} | |||||
static ERL_NIF_TERM clear_1(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) | |||||
return raise(env, EINVAL); | |||||
enif_rwlock_rwlock(state->lock); | |||||
tree_clear(state->tree); | |||||
enif_rwlock_rwunlock(state->lock); | |||||
return enif_make_atom(env, "ok"); | |||||
} | |||||
static ERL_NIF_TERM size_1(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
size_t size = 0; | |||||
if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) | |||||
return raise(env, EINVAL); | |||||
enif_rwlock_rlock(state->lock); | |||||
tree_size(state->tree, &size); | |||||
enif_rwlock_runlock(state->lock); | |||||
return enif_make_uint64(env, (ErlNifUInt64) size); | |||||
} | |||||
static ERL_NIF_TERM is_empty_1(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) | |||||
return raise(env, EINVAL); | |||||
enif_rwlock_rlock(state->lock); | |||||
char *ret = state->tree->sub ? "false" : "true"; | |||||
enif_rwlock_runlock(state->lock); | |||||
return enif_make_atom(env, ret); | |||||
} | |||||
static ERL_NIF_TERM to_list_1(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
ERL_NIF_TERM result = enif_make_list(env, 0); | |||||
if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) | |||||
return raise(env, EINVAL); | |||||
enif_rwlock_rlock(state->lock); | |||||
to_list(env, state->tree, &result); | |||||
enif_rwlock_runlock(state->lock); | |||||
return result; | |||||
} | |||||
static ERL_NIF_TERM dump_1(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state)) | |||||
return raise(env, EINVAL); | |||||
enif_rwlock_rlock(state->lock); | |||||
ERL_NIF_TERM result = dump(env, state->tree); | |||||
enif_rwlock_runlock(state->lock); | |||||
return result; | |||||
} | |||||
static ERL_NIF_TERM register_2(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
state_t *state; | |||||
unsigned int len; | |||||
int ret; | |||||
if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1) || | |||||
!enif_get_resource(env, argv[1], tree_state_t, (void *) &state)) | |||||
return raise(env, EINVAL); | |||||
char name[len+1]; | |||||
enif_get_atom(env, argv[0], name, len+1, ERL_NIF_LATIN1); | |||||
if (!strcmp(name, "undefined")) | |||||
return raise(env, EINVAL); | |||||
ret = register_tree(name, state); | |||||
if (ret) | |||||
return raise(env, ret); | |||||
else | |||||
return enif_make_atom(env, "ok"); | |||||
} | |||||
static ERL_NIF_TERM unregister_1(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
unsigned int len; | |||||
int ret; | |||||
if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1)) | |||||
return raise(env, EINVAL); | |||||
char name[len+1]; | |||||
enif_get_atom(env, argv[0], name, len+1, ERL_NIF_LATIN1); | |||||
ret = unregister_tree(name); | |||||
if (ret) | |||||
return raise(env, ret); | |||||
else | |||||
return enif_make_atom(env, "ok"); | |||||
} | |||||
static ERL_NIF_TERM whereis_1(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
unsigned int len; | |||||
registry_t *entry; | |||||
ERL_NIF_TERM result; | |||||
if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1)) | |||||
return raise(env, EINVAL); | |||||
char name[len+1]; | |||||
enif_get_atom(env, argv[0], name, len+1, ERL_NIF_LATIN1); | |||||
enif_rwlock_rlock(registry_lock); | |||||
HASH_FIND_STR(registry, name, entry); | |||||
if (entry) | |||||
result = enif_make_resource(env, entry->state); | |||||
else | |||||
result = enif_make_atom(env, "undefined"); | |||||
enif_rwlock_runlock(registry_lock); | |||||
return result; | |||||
} | |||||
static ERL_NIF_TERM registered_0(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
registry_t *entry, *iter; | |||||
ERL_NIF_TERM result = enif_make_list(env, 0); | |||||
enif_rwlock_rlock(registry_lock); | |||||
HASH_ITER(hh, registry, entry, iter) { | |||||
result = enif_make_list_cell(env, enif_make_atom(env, entry->name), result); | |||||
} | |||||
enif_rwlock_runlock(registry_lock); | |||||
return result; | |||||
} | |||||
static ErlNifFunc nif_funcs[] = | |||||
{ | |||||
{"new", 0, new_0}, | |||||
{"insert", 2, insert_2}, | |||||
{"delete", 2, delete_2}, | |||||
{"match", 2, match_2}, | |||||
{"refc", 2, refc_2}, | |||||
{"clear", 1, clear_1}, | |||||
{"size", 1, size_1}, | |||||
{"is_empty", 1, is_empty_1}, | |||||
{"to_list", 1, to_list_1}, | |||||
{"dump", 1, dump_1}, | |||||
{"register", 2, register_2}, | |||||
{"unregister", 1, unregister_1}, | |||||
{"whereis", 1, whereis_1}, | |||||
{"registered", 0, registered_0} | |||||
}; | |||||
ERL_NIF_INIT(mqtree, nif_funcs, load, NULL, NULL, unload) |
@ -0,0 +1,12 @@ | |||||
{port_specs, [ | |||||
{"../../priv/mqtree.so", ["*.c"]} | |||||
]}. | |||||
{port_env, [ | |||||
{"CFLAGS", "$CFLAGS -std=c99 -g -O2 -Wall"}, | |||||
{"LDFLAGS", "$LDFLAGS -lpthread"} | |||||
]}. | |||||
@ -0,0 +1,111 @@ | |||||
#include "erl_nif.h" | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
static ErlNifResourceType* test_RESOURCE = NULL; | |||||
// Prototypes | |||||
static ERL_NIF_TERM get_bin_address(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]); | |||||
static ERL_NIF_TERM new_array(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]); | |||||
static ERL_NIF_TERM size_array(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]); | |||||
static ERL_NIF_TERM put_array(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]); | |||||
static ERL_NIF_TERM get_array(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]); | |||||
static ErlNifFunc nif_funcs[] = | |||||
{ | |||||
{"get_bin_address", 1, get_bin_address}, | |||||
{"new_array", 1, new_array}, | |||||
{"size_array", 1, size_array}, | |||||
{"put_array", 3, put_array}, | |||||
{"get_array", 2, get_array} | |||||
}; | |||||
static ERL_NIF_TERM get_bin_address(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
ErlNifBinary bin; | |||||
enif_inspect_binary(env, argv[0], &bin); | |||||
char buf[256]; | |||||
sprintf(buf, "bin: size=%zu, ptr=%p", bin.size, bin.data); | |||||
return enif_make_string(env, buf, ERL_NIF_LATIN1); | |||||
} | |||||
static ERL_NIF_TERM new_array(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
ErlNifBinary bin; | |||||
unsigned long size; | |||||
// unsigned char* data; | |||||
enif_get_ulong(env, argv[0], &size); | |||||
// enif_inspect_binary(env, argv[1], &bin); | |||||
enif_alloc_binary(size * sizeof(long), &bin); | |||||
return enif_make_binary(env, &bin); | |||||
} | |||||
static ERL_NIF_TERM size_array(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
ErlNifBinary bin; | |||||
enif_inspect_binary(env, argv[0], &bin); | |||||
return enif_make_int64(env, bin.size); | |||||
} | |||||
static ERL_NIF_TERM put_array(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
ErlNifBinary bin; | |||||
unsigned long* array; | |||||
unsigned long pos, value; | |||||
enif_get_ulong(env, argv[0], &pos); | |||||
enif_get_ulong(env, argv[1], &value); | |||||
enif_inspect_binary(env, argv[2], &bin); | |||||
array = (unsigned long*)bin.data; | |||||
array[pos] = value; | |||||
return enif_make_atom(env, "ok"); | |||||
} | |||||
static ERL_NIF_TERM get_array(ErlNifEnv* env, int argc, | |||||
const ERL_NIF_TERM argv[]) | |||||
{ | |||||
ErlNifBinary bin; | |||||
unsigned long* array; | |||||
unsigned long pos; | |||||
enif_get_ulong(env, argv[0], &pos); | |||||
enif_inspect_binary(env, argv[1], &bin); | |||||
array = (unsigned long*)bin.data; | |||||
return enif_make_int64(env, *(array + pos)); | |||||
} | |||||
static void test_resource_cleanup(ErlNifEnv* env, void* arg) | |||||
{ | |||||
/* Delete any dynamically allocated memory stored in test_handle */ | |||||
/* test_handle* handle = (test_handle*)arg; */ | |||||
} | |||||
static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) | |||||
{ | |||||
ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; | |||||
ErlNifResourceType* rt = enif_open_resource_type(env, NULL, | |||||
"test_resource", | |||||
&test_resource_cleanup, | |||||
flags, NULL); | |||||
if (rt == NULL) | |||||
return -1; | |||||
test_RESOURCE = rt; | |||||
return 0; | |||||
} | |||||
ERL_NIF_INIT(binary_tools, nif_funcs, &on_load, NULL, NULL, NULL); |
@ -0,0 +1,13 @@ | |||||
{port_specs, [ | |||||
{"../../priv/binary_tools.so", ["*.c"]} | |||||
]}. | |||||
{port_env, [ | |||||
{"linux", "CFLAGS", "$CFLAGS -Wall -O2 -Wpointer-sign"} | |||||
]}. | |||||
@ -0,0 +1,364 @@ | |||||
// | |||||
// 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) | |||||
@ -0,0 +1,8 @@ | |||||
{port_specs, [ | |||||
{"../../priv/nif_skiplist.so", ["*.c"]} | |||||
]}. | |||||
@ -0,0 +1,417 @@ | |||||
// | |||||
// Created by fox on 2017/10/10. | |||||
// Skip Lists: A Probabilistic Alternative to Balanced Trees | |||||
// indexable skip list | |||||
// duplicated score and node | |||||
#include <stdlib.h> | |||||
#include <stdio.h> | |||||
#include <limits.h> | |||||
#include <string.h> | |||||
#include "skiplist.h" | |||||
#include <time.h> | |||||
// from 0 to n | |||||
#define SKIPLIST_MAX_LEVEL 15 | |||||
#define LEVEL_SIZE (SKIPLIST_MAX_LEVEL+1) | |||||
#define RAND_UNIFORM(x) (int)((double)rand() / RAND_MAX * (x)) | |||||
/* Create a skiplist node with the specified number of levels. */ | |||||
snode *skiplistCreateNode(int level, int score, vtype value) { | |||||
snode *zn = | |||||
(snode *)icalloc(sizeof(*zn)+level*sizeof(struct skiplistLevel)); | |||||
zn->score = score; | |||||
zn->value = value; | |||||
return zn; | |||||
} | |||||
skiplist *skiplist_init(void) | |||||
{ | |||||
time_t t; | |||||
srand((unsigned)(time(&t))); | |||||
snode *header = skiplistCreateNode(LEVEL_SIZE, INT_MAX, INT_MAX); | |||||
for (int i = 0; i < LEVEL_SIZE; ++i) { | |||||
header->level[i].span = 1; | |||||
} | |||||
skiplist *list = (skiplist *)imalloc(sizeof(skiplist)); | |||||
list->header = header; | |||||
list->level = 0; | |||||
list->size = 0; | |||||
return list; | |||||
} | |||||
static int rand_level() | |||||
{ | |||||
int level = 0; | |||||
while (rand() < RAND_MAX / 2 && level < SKIPLIST_MAX_LEVEL) | |||||
// while (arc4random() < INT_MAX && level < SKIPLIST_MAX_LEVEL) | |||||
level++; | |||||
return level; | |||||
} | |||||
// insert score,node into a skiplist, return 0 | |||||
int skiplist_insert(skiplist *list, int score, vtype value) | |||||
{ | |||||
snode **update = (snode **)imalloc(sizeof(snode*) * (list->level + 1)); | |||||
unsigned int *fore_width = (unsigned int *)imalloc(sizeof(unsigned int) * (list->level + 1)); | |||||
snode *x = list->header; | |||||
int i; | |||||
for (i = list->level; i >= 0; i--) { | |||||
fore_width[i] = 0; | |||||
while (x->level[i].forward && x->level[i].forward->score <= score) { | |||||
fore_width[i] += x->level[i].span; | |||||
x = x->level[i].forward; | |||||
} | |||||
update[i] = x; | |||||
} | |||||
// assert(x->score <= score); | |||||
int level = rand_level(); | |||||
list->size = list->size+1; | |||||
x = skiplistCreateNode(level+1, score, value); | |||||
// the lowest layer is one by one | |||||
x->level[0].forward = update[0]->level[0].forward; | |||||
x->level[0].span = 1; | |||||
update[0]->level[0].forward = x; | |||||
unsigned int temp_width; | |||||
for (i = 1; i <= (list->level < level ? list->level : level); i++) { | |||||
temp_width = fore_width[i-1] + update[i-1]->level[i-1].span; | |||||
x->level[i].forward = update[i]->level[i].forward; | |||||
x->level[i].span = update[i]->level[i].span + 1 - temp_width; | |||||
update[i]->level[i].forward = x; | |||||
update[i]->level[i].span = temp_width; | |||||
} | |||||
if (level > list->level) { | |||||
// complete the new level | |||||
temp_width = fore_width[list->level] + update[list->level]->level[list->level].span; | |||||
for (i = list->level+1; i <= level; i++) { | |||||
list->header->level[i].span = temp_width; | |||||
list->header->level[i].forward = x; | |||||
x->level[i].forward = NULL; | |||||
x->level[i].span = list->size + 1 - temp_width; | |||||
} | |||||
list->level = level; | |||||
} | |||||
else { | |||||
// complete the unreached level | |||||
for (; i <= list->level; ++i) { | |||||
update[i]->level[i].span++; | |||||
} | |||||
} | |||||
ifree(update); | |||||
ifree(fore_width); | |||||
return 0; | |||||
} | |||||
int skiplist_update(skiplist *list, int score, vtype value, int old_score) | |||||
{ | |||||
skiplist_delete(list, old_score, value); | |||||
return skiplist_insert(list, score, value); | |||||
} | |||||
// search score in skiplist. | |||||
// set the struct with index and first node if exists, otherwise set index 0 | |||||
void skiplist_search(skiplist *list, int score, skiplist_search_ret *ret) | |||||
{ | |||||
snode *x = list->header; | |||||
int temp_width = 1; | |||||
for (int i = list->level; i >= 0; i--) { | |||||
while (x->level[i].forward && x->level[i].forward->score < score) { | |||||
temp_width += x->level[i].span; | |||||
x = x->level[i].forward; | |||||
} | |||||
} | |||||
x = x->level[0].forward; | |||||
ret->index = temp_width; | |||||
ret->node = x; | |||||
} | |||||
// first index of score | |||||
int skiplist_index_of_score(skiplist *list, int score) | |||||
{ | |||||
snode *x = list->header; | |||||
int temp_width = 1; | |||||
for (int i = list->level; i >= 0; i--) { | |||||
while (x->level[i].forward && x->level[i].forward->score < score) { | |||||
temp_width += x->level[i].span; | |||||
x = x->level[i].forward; | |||||
} | |||||
} | |||||
x = x->level[0].forward; | |||||
// check if existed score | |||||
if (x && x->score == score) { | |||||
return temp_width; | |||||
} | |||||
return 0; | |||||
} | |||||
// search skiplist by index. Return the node if exists, otherwise NULL | |||||
snode *skiplist_at(skiplist *list, int index) | |||||
{ | |||||
snode *x = list->header; | |||||
for (int i = list->level; i >= 0; i--) { | |||||
while (x->level[i].forward) { | |||||
if (x->level[i].span == index) { | |||||
return x->level[i].forward; | |||||
} | |||||
if (x->level[i].span < index) { | |||||
index -= x->level[i].span; | |||||
x = x->level[i].forward; | |||||
} | |||||
else { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
return NULL; | |||||
} | |||||
static void skiplist_node_free(snode *x) | |||||
{ | |||||
if (x) { | |||||
ifree(x); | |||||
} | |||||
} | |||||
// delete by score,node. Return 0 if success, 1 if fail. | |||||
int skiplist_delete(skiplist *list, int score, vtype value) | |||||
{ | |||||
int i; | |||||
snode **update = (snode **)imalloc(sizeof(snode*) * (list->level + 1)); | |||||
snode *x = list->header; | |||||
// find every level before the specified node | |||||
for (i = list->level; i >= 0; --i) { | |||||
while (1) { | |||||
if (!(x->level[i].forward) || x->level[i].forward->score > score) { | |||||
update[i] = x; | |||||
break; | |||||
} | |||||
if (x->level[i].forward->score < score) { | |||||
x = x->level[i].forward; | |||||
continue; | |||||
} | |||||
// find the first node with same score | |||||
int j; | |||||
update[i] = x; | |||||
for (j = i-1; j >= 0; --j) { | |||||
while (x->level[j].forward->score < score) | |||||
x = x->level[j].forward; | |||||
update[j] = x; | |||||
} | |||||
x = x->level[0].forward; | |||||
snode *x_start_search = x; | |||||
// find the first node with same score and node | |||||
while (x && x->value != value && x->score == score) { | |||||
x = x->level[0].forward; | |||||
} | |||||
if (x && x->score == score) { | |||||
// now x is the node to find | |||||
// find nodes for every level before the node to find | |||||
if (x == x_start_search) { | |||||
// done | |||||
i = 0; | |||||
break; | |||||
} | |||||
// j is used to judge if change the x_start_search | |||||
j = 0; | |||||
snode *iter; | |||||
for (; i >= 0; --i) | |||||
{ | |||||
if (j) { | |||||
update[i] = x_start_search; | |||||
iter = x_start_search->level[0].forward; | |||||
} else{ | |||||
iter = x_start_search; | |||||
} | |||||
while (iter != x) { | |||||
if (iter == update[i]->level[i].forward) { | |||||
j = 1; | |||||
x_start_search = iter; | |||||
iter = iter->level[0].forward; | |||||
update[i] = update[i]->level[i].forward; | |||||
continue; | |||||
} | |||||
iter = iter->level[0].forward; | |||||
} | |||||
} | |||||
i = 0; | |||||
break; | |||||
} | |||||
else { | |||||
// not found the node | |||||
ifree(update); | |||||
return 1; | |||||
} | |||||
} | |||||
} | |||||
if (x->score != score) { | |||||
// not found the node | |||||
ifree(update); | |||||
return 1; | |||||
} | |||||
for (i = 0; i <= list->level && update[i]->level[i].forward == x; i++) { | |||||
update[i]->level[i].forward = x->level[i].forward; | |||||
update[i]->level[i].span += x->level[i].span - 1; | |||||
} | |||||
for (; i <= list->level; i++) { | |||||
--(update[i]->level[i].span); | |||||
} | |||||
skiplist_node_free(x); | |||||
while (list->level > 0 && list->header->level[list->level].forward == NULL) | |||||
list->level--; | |||||
list->size--; | |||||
ifree(update); | |||||
return 0; | |||||
} | |||||
// ifree the skiplist | |||||
void skiplist_free(skiplist *list) | |||||
{ | |||||
snode *current_node = list->header->level[0].forward; | |||||
while(current_node != NULL) { | |||||
snode *next_node = current_node->level[0].forward; | |||||
skiplist_node_free(current_node); | |||||
current_node = next_node; | |||||
} | |||||
ifree(list); | |||||
} | |||||
// print the skip list, just for test. | |||||
static void skiplist_dump(skiplist *list) | |||||
{ | |||||
int *width = (int *)imalloc(sizeof(int) * (list->level + 1) * list->size); | |||||
memset(width, 0, sizeof(int) * (list->level + 1) * list->size); | |||||
snode **tempn = (snode **)imalloc(sizeof(snode*) * (list->level + 1)); | |||||
int i = 0, j; | |||||
snode *x = list->header->level[0].forward; | |||||
for (j = 0; j <= list->level; ++j) { | |||||
tempn[j] = list->header->level[j].forward; | |||||
} | |||||
while (tempn[0] != NULL) { | |||||
for (j = 1; j <= list->level; ++j) { | |||||
if (tempn[j] == tempn[0]) { | |||||
width[list->size * j + i] = tempn[j]->level[j].span; | |||||
tempn[j] = tempn[j]->level[j].forward; | |||||
} else { | |||||
break; | |||||
} | |||||
} | |||||
tempn[0] = tempn[0]->level[0].forward; | |||||
++i; | |||||
} | |||||
for (j = list->level; j > 0; --j) { | |||||
for (i = 0; i < list->size; ++i) { | |||||
if (width[j * list->size + i] == 0) | |||||
printf(" "); | |||||
else | |||||
printf("%d ", width[j * list->size + i]); | |||||
} | |||||
printf("\n"); | |||||
} | |||||
while (x != NULL) { | |||||
printf("%d:%d->", x->score, x->value); | |||||
x = x->level[0].forward; | |||||
} | |||||
printf("NIL\n"); | |||||
ifree(width); | |||||
ifree(tempn); | |||||
} | |||||
void test_skiplist(void) { | |||||
time_t t; | |||||
srand((unsigned)(time(&t))); | |||||
int arr[][2] = { {3, 1}, {3,2}, {6,6}, {9,9}, {3, 3}, {1, 1}, {4, 4}, {8, 8}, {7, 7}, {5,5}}, i; | |||||
// int arr[] = { 3, 6, 9}, i; | |||||
skiplist_search_ret tempx; | |||||
skiplist *list = skiplist_init(); | |||||
printf("search empty:--------------------\n"); | |||||
skiplist_search(list, 5, &tempx); | |||||
if (tempx.index > 0) { | |||||
printf("error, found not existed item!\n"); | |||||
} | |||||
printf("delete empty:--------------------\n"); | |||||
skiplist_delete(list, 5, 2); | |||||
printf("Insert:--------------------\n"); | |||||
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { | |||||
skiplist_insert(list, arr[i][0], arr[i][1]); | |||||
} | |||||
skiplist_dump(list); | |||||
printf("search empty:--------------------\n"); | |||||
skiplist_search(list, 5, &tempx); | |||||
printf("index = %d\n", tempx.index); | |||||
printf("Search by index:-----------\n"); | |||||
int indexes[] = { 11, 3, 10 }; | |||||
for (i = 0; i < sizeof(indexes) / sizeof(indexes[0]); i++) { | |||||
snode *tempnode = skiplist_at(list, indexes[i]); | |||||
if (tempnode) { | |||||
printf("index = %d, score = %d, value = %d\n", indexes[i], tempnode->score, tempnode->value); | |||||
} else { | |||||
printf("no index = %d\n", indexes[i]); | |||||
} | |||||
} | |||||
printf("Delete:--------------------\n"); | |||||
skiplist_delete(list, 3, 2); | |||||
skiplist_delete(list, 3, 1); | |||||
skiplist_delete(list, 6, 6); | |||||
skiplist_dump(list); | |||||
clock_t start, finish; | |||||
start = clock(); | |||||
for (i = 0; i < 30*1000; ++i) { | |||||
if (rand() < RAND_MAX / 5 * 3) { | |||||
skiplist_insert(list, RAND_UNIFORM(100), RAND_UNIFORM(20)); | |||||
} | |||||
else { | |||||
skiplist_delete(list, RAND_UNIFORM(100), RAND_UNIFORM(20)); | |||||
} | |||||
} | |||||
finish = clock(); | |||||
double duration = (double)(finish - start) / CLOCKS_PER_SEC; | |||||
printf( "%f seconds\n", duration ); | |||||
printf("Search:--------------------\n"); | |||||
int keys[] = { 0, 3, 7, 100, 11 }; | |||||
for (i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) { | |||||
printf("index = %d, score = %d\n", skiplist_index_of_score(list, keys[i]), keys[i]); | |||||
} | |||||
skiplist_free(list); | |||||
}; |
@ -0,0 +1,55 @@ | |||||
// | |||||
// Created by fox on 2017/10/10. | |||||
// | |||||
#ifndef NIF_SKIPLIST_SKIPLIST_H | |||||
#define NIF_SKIPLIST_SKIPLIST_H | |||||
#ifdef USENIF | |||||
#define imalloc enif_alloc | |||||
#define icalloc enif_alloc | |||||
#define irealloc enif_realloc | |||||
#define ifree enif_free | |||||
#else | |||||
#define imalloc malloc | |||||
#define icalloc(x) calloc(x, 1) | |||||
#define irealloc realloc | |||||
#define ifree free | |||||
#endif | |||||
typedef int vtype; | |||||
typedef struct snode { | |||||
int score; | |||||
int value; | |||||
struct skiplistLevel { | |||||
struct snode *forward; | |||||
unsigned int span; | |||||
} level[]; | |||||
} snode; | |||||
typedef struct skiplist { | |||||
int level; | |||||
int size; | |||||
struct snode *header; | |||||
} skiplist; | |||||
typedef struct skiplist_search_ret { | |||||
int index; // start from 1 | |||||
snode *node; | |||||
} skiplist_search_ret; | |||||
void test_skiplist(void); | |||||
// functions | |||||
skiplist *skiplist_init(void); | |||||
void skiplist_free(skiplist *list); | |||||
int skiplist_insert(skiplist *list, int score, vtype value); | |||||
int skiplist_update(skiplist *list, int score, vtype value, int old_score); | |||||
int skiplist_delete(skiplist *list, int score, vtype value); | |||||
void skiplist_search(skiplist *list, int score, skiplist_search_ret *ret); | |||||
int skiplist_index_of_score(skiplist *list, int score); | |||||
snode *skiplist_at(skiplist *list, int index); | |||||
#endif //NIF_SKIPLIST_SKIPLIST_H |
@ -0,0 +1,48 @@ | |||||
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- | |||||
%% ex: ts=4 sw=4 et | |||||
%% Copyright (c) 2011-2016 Sergey Urbanovich | |||||
%% http://github.com/urbanserj/cbase64-erlang-nif | |||||
%% | |||||
%% Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
%% of this software and associated documentation files (the "Software"), to deal | |||||
%% in the Software without restriction, including without limitation the rights | |||||
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
%% copies of the Software, and to permit persons to whom the Software is | |||||
%% furnished to do so, subject to the following conditions: | |||||
%% | |||||
%% The above copyright notice and this permission notice shall be included in | |||||
%% all copies or substantial portions of the Software. | |||||
%% | |||||
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
%% THE SOFTWARE. | |||||
-module(cbase64). | |||||
-export([encode/1, decode/1]). | |||||
-on_load(on_load/0). | |||||
on_load() -> | |||||
BaseDir = | |||||
case code:priv_dir(?MODULE) of | |||||
{error, bad_name} -> | |||||
EbinDir = filename:dirname(code:which(?MODULE)), | |||||
AppPath = filename:dirname(EbinDir), | |||||
filename:join(AppPath, "priv"); | |||||
Dir -> | |||||
Dir | |||||
end, | |||||
SoName = filename:join(BaseDir, atom_to_list(?MODULE)), | |||||
erlang:load_nif(SoName, 0). | |||||
-spec encode(binary() | iolist()) -> binary(). | |||||
encode(_Data) -> | |||||
erlang:nif_error(not_loaded, [{module, ?MODULE}, {line, ?LINE}]). | |||||
-spec decode(binary() | iolist()) -> binary(). | |||||
decode(_Data) -> | |||||
erlang:nif_error(not_loaded, [{module, ?MODULE}, {line, ?LINE}]). |
@ -0,0 +1,184 @@ | |||||
-module(neural). | |||||
-export([new/2, empty/1, drain/1, dump/1, % Table operations | |||||
garbage/1, garbage_size/1, | |||||
key_pos/1]). | |||||
-export([lookup/2]). % Getters | |||||
-export([insert/2, insert_new/2, delete/2]). % Setters | |||||
-export([increment/3, unshift/3, shift/3, swap/3]). % Delta operations | |||||
-on_load(init/0). | |||||
-record(table_opts, { | |||||
keypos = 1 :: integer() | |||||
}). | |||||
-define(nif_stub, nif_stub_error(?LINE)). | |||||
nif_stub_error(Line) -> | |||||
erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}). | |||||
-ifdef(TEST). | |||||
-include_lib("eunit/include/eunit.hrl"). | |||||
-endif. | |||||
init() -> | |||||
PrivDir = case code:priv_dir(?MODULE) of | |||||
{error, bad_name} -> | |||||
EbinDir = filename:dirname(code:which(?MODULE)), | |||||
AppPath = filename:dirname(EbinDir), | |||||
filename:join(AppPath, "priv"); | |||||
Path -> | |||||
Path | |||||
end, | |||||
erlang:load_nif(filename:join(PrivDir, ?MODULE), 0). | |||||
new(Table, Opts) -> | |||||
new(Table, Opts, #table_opts{}). | |||||
new(Table, [{key_pos, KeyPos}|Opts], TableOpts) -> | |||||
new(Table, Opts, TableOpts#table_opts{keypos = KeyPos}); | |||||
new(Table, [], _TableOpts = #table_opts{keypos = KeyPos}) when is_integer(KeyPos) -> | |||||
make_table(Table, KeyPos). | |||||
make_table(_Table, _KeyPos) -> | |||||
?nif_stub. | |||||
insert(Table, Object) when is_atom(Table), is_tuple(Object) -> | |||||
Key = element(key_pos(Table), Object), | |||||
insert(Table, erlang:phash2(Key), Object). | |||||
insert(_Table, _Key, _Object) -> | |||||
?nif_stub. | |||||
insert_new(Table, Object) when is_atom(Table), is_tuple(Object) -> | |||||
Key = element(key_pos(Table), Object), | |||||
insert_new(Table, erlang:phash2(Key), Object). | |||||
insert_new(_Table, _Key, _Object) -> | |||||
?nif_stub. | |||||
increment(Table, Key, Value) when is_integer(Value) -> | |||||
[N] = increment(Table, Key, [{key_pos(Table) + 1, Value}]), | |||||
N; | |||||
increment(Table, Key, Op = {Position, Value}) when is_integer(Position), is_integer(Value) -> | |||||
[N] = increment(Table, Key, [Op]), | |||||
N; | |||||
increment(Table, Key, Op = [_|_]) when is_atom(Table) -> | |||||
case lists:all(fun is_incr_op/1, Op) of | |||||
true -> | |||||
lists:reverse(do_increment(Table, erlang:phash2(Key), Op)); | |||||
false -> | |||||
error(badarg) | |||||
end. | |||||
shift(Table, Key, Value) when is_integer(Value) -> | |||||
[R] = shift(Table, Key, [{key_pos(Table) + 1, Value}]), | |||||
R; | |||||
shift(Table, Key, Op = {Position, Value}) when is_integer(Position), is_integer(Value) -> | |||||
[R] = shift(Table, Key, [Op]), | |||||
R; | |||||
shift(Table, Key, Op = [_|_]) when is_atom(Table) -> | |||||
case lists:all(fun is_shift_op/1, Op) of | |||||
true -> | |||||
lists:reverse(do_shift(Table, erlang:phash2(Key), Op)); | |||||
false -> | |||||
error(badarg) | |||||
end. | |||||
unshift(Table, Key, Op = {Position, Value}) when is_integer(Position), is_list(Value) -> | |||||
[R] = unshift(Table, Key, [Op]), | |||||
R; | |||||
unshift(Table, Key, Op = [_|_]) when is_atom(Table) -> | |||||
case lists:all(fun is_unshift_op/1, Op) of | |||||
true -> | |||||
lists:reverse(do_unshift(Table, erlang:phash2(Key), Op), []); | |||||
false -> | |||||
error(badarg) | |||||
end. | |||||
swap(Table, Key, Op = {Position, _Value}) when is_integer(Position) -> | |||||
[R] = swap(Table, Key, [Op]), | |||||
R; | |||||
swap(Table, Key, Op = [_|_]) when is_atom(Table) -> | |||||
case lists:all(fun is_swap_op/1, Op) of | |||||
true -> | |||||
lists:reverse(do_swap(Table, erlang:phash2(Key), Op)); | |||||
false -> | |||||
error(badarg) | |||||
end. | |||||
is_incr_op({P,V}) when is_integer(P), is_integer(V) -> true; | |||||
is_incr_op(_) -> false. | |||||
is_shift_op({P,V}) when is_integer(P), is_integer(V) -> true; | |||||
is_shift_op(_) -> false. | |||||
is_unshift_op({P,L}) when is_integer(P), is_list(L) -> true; | |||||
is_unshift_op(_) -> false. | |||||
is_swap_op({P,V}) when is_integer(P) -> true; | |||||
is_swap_op(_) -> false. | |||||
do_increment(_Table, _Key, _Op) -> | |||||
?nif_stub. | |||||
do_shift(_Table, _Key, _Op) -> | |||||
?nif_stub. | |||||
do_unshift(_Table, _Key, _Op) -> | |||||
?nif_stub. | |||||
do_swap(_Table, _Key, _Op) -> | |||||
?nif_stub. | |||||
lookup(Table, Key) when is_atom(Table) -> | |||||
do_fetch(Table, erlang:phash2(Key)). | |||||
do_fetch(_Table, _Key) -> | |||||
?nif_stub. | |||||
delete(Table, Key) when is_atom(Table) -> | |||||
do_delete(Table, erlang:phash2(Key)). | |||||
do_delete(_Table, _key) -> | |||||
?nif_stub. | |||||
garbage(_Table) -> | |||||
?nif_stub. | |||||
garbage_size(_Table) -> | |||||
?nif_stub. | |||||
empty(_Table) -> | |||||
?nif_stub. | |||||
drain(Table) -> | |||||
'$neural_batch_wait' = do_drain(Table), | |||||
wait_batch_response(). | |||||
do_drain(_Table) -> | |||||
?nif_stub. | |||||
dump(Table) -> | |||||
'$neural_batch_wait' = do_dump(Table), | |||||
wait_batch_response(). | |||||
do_dump(_Table) -> | |||||
?nif_stub. | |||||
key_pos(_Table) -> | |||||
?nif_stub. | |||||
wait_batch_response() -> | |||||
receive | |||||
{'$neural_batch_response', Response} -> Response | |||||
end. | |||||
%% =================================================================== | |||||
%% EUnit tests | |||||
%% =================================================================== | |||||
-ifdef(TEST). | |||||
basic_test() -> | |||||
{ok, Ref} = new(), | |||||
?assertEqual(ok, myfunction(Ref)). | |||||
-endif. |
@ -0,0 +1,16 @@ | |||||
-module(neural_app). | |||||
-behaviour(application). | |||||
%% Application callbacks | |||||
-export([start/2, stop/1]). | |||||
%% =================================================================== | |||||
%% Application callbacks | |||||
%% =================================================================== | |||||
start(_StartType, _StartArgs) -> | |||||
neural_sup:start_link(). | |||||
stop(_State) -> | |||||
ok. |
@ -0,0 +1,31 @@ | |||||
-module(neural_sup). | |||||
-behaviour(supervisor). | |||||
%% API | |||||
-export([start_link/0]). | |||||
%% Supervisor callbacks | |||||
-export([init/1]). | |||||
%% Helper macro for declaring children of supervisor | |||||
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). | |||||
%% =================================================================== | |||||
%% API functions | |||||
%% =================================================================== | |||||
start_link() -> | |||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []). | |||||
%% =================================================================== | |||||
%% Supervisor callbacks | |||||
%% =================================================================== | |||||
init([]) -> | |||||
{ok, { | |||||
{one_for_one, 5, 10}, | |||||
[] | |||||
}}. | |||||
@ -0,0 +1,60 @@ | |||||
-module(binary_tools). | |||||
-compile(export_all). | |||||
-on_load(init/0). | |||||
-define(nif_stub, nif_stub_error(?LINE)). | |||||
nif_stub_error(Line) -> | |||||
erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}). | |||||
-ifdef(TEST). | |||||
-include_lib("eunit/include/eunit.hrl"). | |||||
-endif. | |||||
init() -> | |||||
PrivDir = case code:priv_dir(?MODULE) of | |||||
{error, bad_name} -> | |||||
EbinDir = filename:dirname(code:which(?MODULE)), | |||||
AppPath = filename:dirname(EbinDir), | |||||
filename:join(AppPath, "priv"); | |||||
Path -> | |||||
Path | |||||
end, | |||||
erlang:load_nif(filename:join(PrivDir, ?MODULE), 0). | |||||
get_bin_address(_Ref) -> | |||||
?nif_stub. | |||||
new_array(_Size) -> | |||||
?nif_stub. | |||||
size_array(_Bin) -> | |||||
?nif_stub. | |||||
put_array(_Pos, _Value, _Bin) -> | |||||
?nif_stub. | |||||
get_array(_Pos, _Bin) -> | |||||
?nif_stub. | |||||
test() -> | |||||
List = lists:seq(1, 10000), | |||||
A = new_array(10000), | |||||
EA = array:from_list(List), | |||||
[put_array(S - 1, S, A) || S <- List], %array begin from 0,list begin from 1 | |||||
G = [random:uniform(9999) || _ <- List], | |||||
{LT, LR} = timer:tc(fun()-> [lists:nth(Pos, List) || Pos <- G] end), | |||||
{AT, AR} = timer:tc(fun()-> [get_array(Pos - 1, A) || Pos <- G] end), | |||||
{EAT, EAR} = timer:tc(fun()-> [array:get(Pos - 1, EA) || Pos <- G] end), | |||||
LR = AR, | |||||
LR = EAR, | |||||
io:format("List random nth : ~p~n", [LT]), | |||||
io:format("Array random get : ~p~n", [AT]), | |||||
io:format("Erlang Array random get : ~p~n", [EAT]). | |||||
%% =================================================================== | |||||
%% EUnit tests | |||||
%% =================================================================== |
@ -0,0 +1,70 @@ | |||||
%%%------------------------------------------------------------------- | |||||
%%% @author fox | |||||
%%% @copyright (C) 2017, <COMPANY> | |||||
%%% @doc | |||||
%%% nif | |||||
%%% @end | |||||
%%% Created : 16. 十月 2017 16:05 | |||||
%%%------------------------------------------------------------------- | |||||
-module(nif). | |||||
-author("fox"). | |||||
%% API | |||||
-export([init/0, test/0]). | |||||
init() -> | |||||
skiplist:init("./nif_skiplist"), | |||||
ok. | |||||
test() -> | |||||
init(), | |||||
A = skiplist:new(), | |||||
skiplist:insert(A, 10, 10), | |||||
skiplist:insert(A, 5, 5), | |||||
skiplist:insert(A, 8, 8), | |||||
skiplist:insert(A, 1, 1), | |||||
skiplist:insert(A, 4, 4), | |||||
skiplist:insert(A, 7, 7), | |||||
io:format("~p~n", [skiplist:to_list(A)]), | |||||
skiplist:delete(A, 7, 7), | |||||
io:format("~p~n", [skiplist:to_list(A)]), | |||||
io:format("get=~p~n", [skiplist:index_of_score(A, 5)]), | |||||
io:format("get=~p~n", [skiplist:index_of_score(A, 6)]), | |||||
io:format("get=~p~n", [skiplist:index_of_score(A, 15)]), | |||||
io:format("at=~p~n", [skiplist:at(A, 1)]), | |||||
io:format("at=~p~n", [skiplist:at(A, 5)]), | |||||
io:format("at=~p~n", [skiplist:at(A, 6)]), | |||||
io:format("range=~p~n", [skiplist:range(A, 1, 10)]), | |||||
io:format("range=~p~n", [skiplist:range(A, 2, 3)]), | |||||
io:format("range=~p~n", [skiplist:range(A, 8, 10)]), | |||||
io:format("range_with_score=~p~n", [skiplist:range_with_score(A, 1, 10)]), | |||||
io:format("range_with_score=~p~n", [skiplist:range_with_score(A, 2, 3)]), | |||||
io:format("range_with_score=~p~n", [skiplist:range_with_score(A, 8, 10)]), | |||||
erlang:statistics(wall_clock), | |||||
loop_test(30*1000, A), | |||||
{_, Time} = erlang:statistics(wall_clock), | |||||
io:format("time=~p~n", [Time]), | |||||
%% io:format("range_by_score=~p~n", [skiplist:range_by_score(A, -10, 10)]), | |||||
%% io:format("range_by_score=~p~n", [skiplist:range_by_score(A, 2, 3)]), | |||||
io:format("range_by_score=~p~n", [skiplist:range_by_score(A, 101, 10)]), | |||||
skiplist:free(A). | |||||
loop_test(0, _) -> | |||||
ok; | |||||
loop_test(N, L) -> | |||||
case rand:uniform(500) < 300 of | |||||
true -> | |||||
skiplist:insert(L, rand:uniform(100), rand:uniform(20)); | |||||
_ -> | |||||
skiplist:delete(L, rand:uniform(100), rand:uniform(20)) | |||||
end, | |||||
loop_test(N - 1, L). | |||||
@ -0,0 +1,81 @@ | |||||
%%%------------------------------------------------------------------- | |||||
%%% @author fox | |||||
%%% @copyright (C) 2017, <COMPANY> | |||||
%%% @doc | |||||
%%% indexable skip list, can insert duplicated value | |||||
%%% @end | |||||
%%% Created : 16. 十月 2017 14:16 | |||||
%%%------------------------------------------------------------------- | |||||
-module(skiplist). | |||||
-author("fox"). | |||||
%% API | |||||
-export([ | |||||
init/1, | |||||
new/0, free/1, | |||||
insert/3, delete/3, update/4, | |||||
to_list/1, | |||||
range/3, range_with_score/3, range_by_score/3, | |||||
index_of_score/2, at/2, | |||||
size/1]). | |||||
-type skiplist() :: non_neg_integer(). | |||||
-type score() :: integer(). | |||||
-type value() :: integer(). | |||||
-spec init(Path) -> ok | Error when | |||||
Path :: string(), | |||||
Error :: any(). | |||||
init(Path) -> | |||||
erlang:load_nif(Path, 0). | |||||
-spec new() -> skiplist(). | |||||
new() -> | |||||
erlang:nif_error(undef). | |||||
-spec free(skiplist()) -> ok. | |||||
free(_List) -> | |||||
erlang:nif_error(undef). | |||||
-spec insert(skiplist(), score(), value()) -> 0. | |||||
insert(_List, _Score, _Value) -> | |||||
erlang:nif_error(undef). | |||||
-spec delete(skiplist(), score(), value()) -> | |||||
0 | 1. %% 0 success, 1 fail | |||||
delete(_List, _Score, _Value) -> | |||||
erlang:nif_error(undef). | |||||
-spec update(skiplist(), score(), value(), score()) -> 0. | |||||
update(_List, _Score, _Value, _OldScore) -> | |||||
erlang:nif_error(undef). | |||||
-spec to_list(skiplist()) -> [{score(), value()}]. | |||||
to_list(_List) -> | |||||
erlang:nif_error(undef). | |||||
-spec size(skiplist()) -> non_neg_integer(). | |||||
size(_List) -> | |||||
erlang:nif_error(undef). | |||||
-spec index_of_score(skiplist(), score()) -> non_neg_integer(). | |||||
index_of_score(_List, _Score) -> | |||||
erlang:nif_error(undef). | |||||
-spec at(skiplist(), non_neg_integer()) -> error | {score(), value()}. | |||||
at(_List, _Index) -> | |||||
erlang:nif_error(undef). | |||||
-spec range(skiplist(), non_neg_integer(), non_neg_integer()) -> [value()]. | |||||
range(_List, _Start, _Len) -> | |||||
erlang:nif_error(undef). | |||||
-spec range_with_score(skiplist(), non_neg_integer(), non_neg_integer()) -> | |||||
[{score(), value()}]. | |||||
range_with_score(_List, _Start, _Len) -> | |||||
erlang:nif_error(undef). | |||||
-spec range_by_score(skiplist(), score(), score()) -> | |||||
{StartIndex :: non_neg_integer(), [{score(), value()}]}. | |||||
range_by_score(_List, _Score1, _Score2) -> | |||||
erlang:nif_error(undef). |