@ -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). |