rax-radix-tree 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/rax/rax.h ADDED
@@ -0,0 +1,217 @@
1
+ /* Rax -- A radix tree implementation.
2
+ *
3
+ * Copyright (c) 2017-2018, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are met:
8
+ *
9
+ * * Redistributions of source code must retain the above copyright notice,
10
+ * this list of conditions and the following disclaimer.
11
+ * * Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ * * Neither the name of Redis nor the names of its contributors may be used
15
+ * to endorse or promote products derived from this software without
16
+ * specific prior written permission.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ * POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #ifndef RAX_H
32
+ #define RAX_H
33
+
34
+ #include <stdint.h>
35
+
36
+ /* Representation of a radix tree as implemented in this file, that contains
37
+ * the strings "foo", "foobar" and "footer" after the insertion of each
38
+ * word. When the node represents a key inside the radix tree, we write it
39
+ * between [], otherwise it is written between ().
40
+ *
41
+ * This is the vanilla representation:
42
+ *
43
+ * (f) ""
44
+ * \
45
+ * (o) "f"
46
+ * \
47
+ * (o) "fo"
48
+ * \
49
+ * [t b] "foo"
50
+ * / \
51
+ * "foot" (e) (a) "foob"
52
+ * / \
53
+ * "foote" (r) (r) "fooba"
54
+ * / \
55
+ * "footer" [] [] "foobar"
56
+ *
57
+ * However, this implementation implements a very common optimization where
58
+ * successive nodes having a single child are "compressed" into the node
59
+ * itself as a string of characters, each representing a next-level child,
60
+ * and only the link to the node representing the last character node is
61
+ * provided inside the representation. So the above representation is turned
62
+ * into:
63
+ *
64
+ * ["foo"] ""
65
+ * |
66
+ * [t b] "foo"
67
+ * / \
68
+ * "foot" ("er") ("ar") "foob"
69
+ * / \
70
+ * "footer" [] [] "foobar"
71
+ *
72
+ * However this optimization makes the implementation a bit more complex.
73
+ * For instance if a key "first" is added in the above radix tree, a
74
+ * "node splitting" operation is needed, since the "foo" prefix is no longer
75
+ * composed of nodes having a single child one after the other. This is the
76
+ * above tree and the resulting node splitting after this event happens:
77
+ *
78
+ *
79
+ * (f) ""
80
+ * /
81
+ * (i o) "f"
82
+ * / \
83
+ * "firs" ("rst") (o) "fo"
84
+ * / \
85
+ * "first" [] [t b] "foo"
86
+ * / \
87
+ * "foot" ("er") ("ar") "foob"
88
+ * / \
89
+ * "footer" [] [] "foobar"
90
+ *
91
+ * Similarly after deletion, if a new chain of nodes having a single child
92
+ * is created (the chain must also not include nodes that represent keys),
93
+ * it must be compressed back into a single node.
94
+ *
95
+ */
96
+
97
+ #define RAX_NODE_MAX_SIZE ((1<<29)-1)
98
+ typedef struct raxNode {
99
+ uint32_t iskey:1; /* Does this node contain a key? */
100
+ uint32_t isnull:1; /* Associated value is NULL (don't store it). */
101
+ uint32_t iscompr:1; /* Node is compressed. */
102
+ uint32_t size:29; /* Number of children, or compressed string len. */
103
+ /* Data layout is as follows:
104
+ *
105
+ * If node is not compressed we have 'size' bytes, one for each children
106
+ * character, and 'size' raxNode pointers, point to each child node.
107
+ * Note how the character is not stored in the children but in the
108
+ * edge of the parents:
109
+ *
110
+ * [header iscompr=0][abc][a-ptr][b-ptr][c-ptr](value-ptr?)
111
+ *
112
+ * if node is compressed (iscompr bit is 1) the node has 1 children.
113
+ * In that case the 'size' bytes of the string stored immediately at
114
+ * the start of the data section, represent a sequence of successive
115
+ * nodes linked one after the other, for which only the last one in
116
+ * the sequence is actually represented as a node, and pointed to by
117
+ * the current compressed node.
118
+ *
119
+ * [header iscompr=1][xyz][z-ptr](value-ptr?)
120
+ *
121
+ * Both compressed and not compressed nodes can represent a key
122
+ * with associated data in the radix tree at any level (not just terminal
123
+ * nodes).
124
+ *
125
+ * If the node has an associated key (iskey=1) and is not NULL
126
+ * (isnull=0), then after the raxNode pointers poiting to the
127
+ * children, an additional value pointer is present (as you can see
128
+ * in the representation above as "value-ptr" field).
129
+ */
130
+ unsigned char data[];
131
+ } raxNode;
132
+
133
+ typedef struct rax {
134
+ raxNode *head;
135
+ uint64_t numele;
136
+ uint64_t numnodes;
137
+ } rax;
138
+
139
+ /* Stack data structure used by raxLowWalk() in order to, optionally, return
140
+ * a list of parent nodes to the caller. The nodes do not have a "parent"
141
+ * field for space concerns, so we use the auxiliary stack when needed. */
142
+ #define RAX_STACK_STATIC_ITEMS 32
143
+ typedef struct raxStack {
144
+ void **stack; /* Points to static_items or an heap allocated array. */
145
+ size_t items, maxitems; /* Number of items contained and total space. */
146
+ /* Up to RAXSTACK_STACK_ITEMS items we avoid to allocate on the heap
147
+ * and use this static array of pointers instead. */
148
+ void *static_items[RAX_STACK_STATIC_ITEMS];
149
+ int oom; /* True if pushing into this stack failed for OOM at some point. */
150
+ } raxStack;
151
+
152
+ /* Optional callback used for iterators and be notified on each rax node,
153
+ * including nodes not representing keys. If the callback returns true
154
+ * the callback changed the node pointer in the iterator structure, and the
155
+ * iterator implementation will have to replace the pointer in the radix tree
156
+ * internals. This allows the callback to reallocate the node to perform
157
+ * very special operations, normally not needed by normal applications.
158
+ *
159
+ * This callback is used to perform very low level analysis of the radix tree
160
+ * structure, scanning each possible node (but the root node), or in order to
161
+ * reallocate the nodes to reduce the allocation fragmentation (this is the
162
+ * Redis application for this callback).
163
+ *
164
+ * This is currently only supported in forward iterations (raxNext) */
165
+ typedef int (*raxNodeCallback)(raxNode **noderef);
166
+
167
+ /* Radix tree iterator state is encapsulated into this data structure. */
168
+ #define RAX_ITER_STATIC_LEN 128
169
+ #define RAX_ITER_JUST_SEEKED (1<<0) /* Iterator was just seeked. Return current
170
+ element for the first iteration and
171
+ clear the flag. */
172
+ #define RAX_ITER_EOF (1<<1) /* End of iteration reached. */
173
+ #define RAX_ITER_SAFE (1<<2) /* Safe iterator, allows operations while
174
+ iterating. But it is slower. */
175
+ typedef struct raxIterator {
176
+ int flags;
177
+ rax *rt; /* Radix tree we are iterating. */
178
+ unsigned char *key; /* The current string. */
179
+ void *data; /* Data associated to this key. */
180
+ size_t key_len; /* Current key length. */
181
+ size_t key_max; /* Max key len the current key buffer can hold. */
182
+ unsigned char key_static_string[RAX_ITER_STATIC_LEN];
183
+ raxNode *node; /* Current node. Only for unsafe iteration. */
184
+ raxStack stack; /* Stack used for unsafe iteration. */
185
+ raxNodeCallback node_cb; /* Optional node callback. Normally set to NULL. */
186
+ } raxIterator;
187
+
188
+ /* A special pointer returned for not found items. */
189
+ extern void *raxNotFound;
190
+
191
+ /* Exported API. */
192
+ rax *raxNew(void);
193
+ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);
194
+ int raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);
195
+ int raxRemove(rax *rax, unsigned char *s, size_t len, void **old);
196
+ void *raxFind(rax *rax, unsigned char *s, size_t len);
197
+ void raxFree(rax *rax);
198
+ void raxFreeWithCallback(rax *rax, void (*free_callback)(void*));
199
+ void raxStart(raxIterator *it, rax *rt);
200
+ int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len);
201
+ int raxNext(raxIterator *it);
202
+ int raxPrev(raxIterator *it);
203
+ int raxUp(raxIterator *it);
204
+ int raxRandomWalk(raxIterator *it, size_t steps);
205
+ int raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len);
206
+ void raxStop(raxIterator *it);
207
+ int raxEOF(raxIterator *it);
208
+ void raxShow(rax *rax);
209
+ uint64_t raxSize(rax *rax);
210
+ unsigned long raxTouch(raxNode *n);
211
+ void raxSetDebugMsg(int onoff);
212
+
213
+ /* Internal API. May be used by the node callback in order to access rax nodes
214
+ * in a low level way, so this function is exported as well. */
215
+ void raxSetData(raxNode *n, void *data);
216
+
217
+ #endif
@@ -0,0 +1,43 @@
1
+ /* Rax -- A radix tree implementation.
2
+ *
3
+ * Copyright (c) 2017, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are met:
8
+ *
9
+ * * Redistributions of source code must retain the above copyright notice,
10
+ * this list of conditions and the following disclaimer.
11
+ * * Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ * * Neither the name of Redis nor the names of its contributors may be used
15
+ * to endorse or promote products derived from this software without
16
+ * specific prior written permission.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ * POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ /* Allocator selection.
32
+ *
33
+ * This file is used in order to change the Rax allocator at compile time.
34
+ * Just define the following defines to what you want to use. Also add
35
+ * the include of your alternate allocator if needed (not needed in order
36
+ * to use the default libc allocator). */
37
+
38
+ #ifndef RAX_ALLOC_H
39
+ #define RAX_ALLOC_H
40
+ #define rax_malloc malloc
41
+ #define rax_realloc realloc
42
+ #define rax_free free
43
+ #endif
@@ -0,0 +1,63 @@
1
+ /* Rax -- A radix tree implementation.
2
+ *
3
+ * Copyright (c) 2017, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are met:
8
+ *
9
+ * * Redistributions of source code must retain the above copyright notice,
10
+ * this list of conditions and the following disclaimer.
11
+ * * Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ * * Neither the name of Redis nor the names of its contributors may be used
15
+ * to endorse or promote products derived from this software without
16
+ * specific prior written permission.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ * POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ /* Allocator selection.
32
+ *
33
+ * This file is used in order to change the Rax allocator at compile time.
34
+ * Just define the following defines to what you want to use. Also add
35
+ * the include of your alternate allocator if needed (not needed in order
36
+ * to use the default libc allocator). */
37
+
38
+ #ifndef RAX_ALLOC_H
39
+ #define RAX_ALLOC_H
40
+
41
+ #include <sys/types.h>
42
+ #include <stdlib.h>
43
+
44
+ void *oom_malloc(char *file, int line, size_t size) {
45
+ if ((rand() % 20) == 0) {
46
+ printf("oom_malloc(): returning NULL at %s:%d\n", file, line);
47
+ return NULL;
48
+ }
49
+ return malloc(size);
50
+ }
51
+
52
+ void *oom_realloc(char *file, int line, void *ptr, size_t size) {
53
+ if ((rand() % 20) == 0) {
54
+ printf("oom_remalloc(): returning NULL at %s:%d\n", file, line);
55
+ return NULL;
56
+ }
57
+ return realloc(ptr,size);
58
+ }
59
+
60
+ #define rax_malloc(sz) oom_malloc(__FILE__,__LINE__,(sz))
61
+ #define rax_realloc(ptr,sz) oom_realloc(__FILE__,__LINE__,(ptr),(sz))
62
+ #define rax_free free
63
+ #endif
@@ -0,0 +1,77 @@
1
+ /* A simple RC4 based RNG that sucks less than the certain libc rand()
2
+ * implementations, notably the incredibly bad MacOS() implementation.
3
+ *
4
+ * Copyright (c) 2018, Salvatore Sanfilippo <antirez at gmail dot com>
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * * Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ * * Redistributions in binary form must reproduce the above copyright
13
+ * notice, this list of conditions and the following disclaimer in the
14
+ * documentation and/or other materials provided with the distribution.
15
+ *
16
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ * POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ #include <stdint.h>
30
+
31
+ /* Initialize the sbox with an initial seed. */
32
+ static unsigned char rc4_sbox[256] = {244, 223, 67, 241, 181, 90, 95, 76, 248, 141, 130, 109, 92, 230, 238, 131, 111, 26, 75, 196, 186, 185, 51, 4, 40, 156, 11, 23, 15, 101, 144, 128, 173, 121, 123, 124, 218, 89, 187, 29, 178, 7, 0, 1, 217, 113, 235, 33, 182, 158, 94, 221, 100, 53, 140, 172, 139, 24, 162, 143, 229, 202, 36, 37, 39, 35, 45, 231, 87, 103, 198, 14, 255, 83, 93, 136, 165, 64, 3, 16, 49, 219, 155, 183, 233, 73, 215, 10, 159, 247, 212, 99, 106, 74, 154, 72, 22, 19, 65, 52, 46, 195, 134, 197, 69, 209, 34, 91, 137, 115, 102, 135, 63, 211, 47, 56, 127, 161, 27, 9, 118, 220, 191, 54, 21, 216, 18, 253, 81, 148, 42, 213, 114, 12, 190, 245, 129, 8, 41, 201, 168, 177, 204, 32, 242, 82, 13, 117, 2, 152, 60, 66, 166, 193, 164, 79, 142, 133, 126, 30, 85, 252, 184, 20, 17, 205, 236, 174, 226, 138, 38, 62, 222, 157, 77, 145, 250, 6, 249, 78, 192, 132, 170, 189, 176, 86, 153, 97, 125, 50, 254, 44, 61, 240, 203, 225, 246, 88, 234, 150, 179, 149, 122, 25, 104, 214, 105, 199, 228, 200, 163, 5, 112, 84, 55, 206, 251, 31, 98, 167, 108, 116, 175, 194, 171, 188, 58, 210, 237, 96, 147, 208, 110, 80, 169, 71, 180, 243, 239, 59, 28, 227, 160, 146, 48, 107, 68, 151, 224, 119, 43, 57, 207, 120, 232, 70};
33
+
34
+ void rc4srand(uint64_t seed) {
35
+ /* To seed the RNG in a reproducible way we use a simple
36
+ * hashing function, together with the swapping idea of the
37
+ * original RC4 key scheduling algorithm. */
38
+ for (int j = 0; j < 255; j++) rc4_sbox[j] = j;
39
+
40
+ seed ^= 5381;
41
+ for (int j = 0; j < 256; j++) {
42
+ seed = ((seed << 5) + seed) + j + 1;
43
+ int i = seed & 0xff;
44
+
45
+ /* Swap sbox[i] and sbox[j]. */
46
+ unsigned char t = rc4_sbox[j];
47
+ rc4_sbox[j] = rc4_sbox[i];
48
+ rc4_sbox[i] = t;
49
+ }
50
+ }
51
+
52
+ static void rc4(unsigned char *buf, unsigned long len) {
53
+ static unsigned int i = 0, j = 0;
54
+ unsigned int si, sj, x;
55
+
56
+ for (x = 0; x < len; x++) {
57
+ i = (i+1) & 0xff;
58
+ si = rc4_sbox[i];
59
+ j = (j + si) & 0xff;
60
+ sj = rc4_sbox[j];
61
+ rc4_sbox[i] = sj;
62
+ rc4_sbox[j] = si;
63
+ *buf++ = rc4_sbox[(si+sj)&0xff];
64
+ }
65
+ }
66
+
67
+ uint32_t rc4rand(void) {
68
+ uint32_t r;
69
+ rc4((unsigned char*)&r,sizeof(r));
70
+ return r;
71
+ }
72
+
73
+ uint64_t rc4rand64(void) {
74
+ uint64_t r;
75
+ rc4((unsigned char*)&r,sizeof(r));
76
+ return r;
77
+ }
@@ -0,0 +1,9 @@
1
+ #ifndef RC4RAND_H
2
+ #define RC4RAND_H
3
+
4
+ #include <stdint.h>
5
+ void rc4srand(uint64_t seed);
6
+ uint32_t rc4rand(void);
7
+ uint64_t rc4rand64(void);
8
+
9
+ #endif
@@ -0,0 +1,238 @@
1
+ #include <napi.h>
2
+ #include <string>
3
+ #include <iostream>
4
+
5
+ extern "C" {
6
+ #include "radix_tree.h"
7
+ }
8
+
9
+ class RaxIterator : public Napi::ObjectWrap<RaxIterator> {
10
+ public:
11
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
12
+ RaxIterator(const Napi::CallbackInfo& info);
13
+
14
+ private:
15
+ static Napi::FunctionReference constructor;
16
+ radix_iterator* iterator;
17
+ radix_tree* tree;
18
+ rt_str_t path;
19
+
20
+ Napi::Value Next(const Napi::CallbackInfo& info);
21
+ Napi::Value Prev(const Napi::CallbackInfo& info);
22
+ Napi::Value Up(const Napi::CallbackInfo& info);
23
+ Napi::Value Stop(const Napi::CallbackInfo& info);
24
+
25
+ friend class RadixTree;
26
+ };
27
+
28
+ class RadixTree : public Napi::ObjectWrap<RadixTree> {
29
+ public:
30
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
31
+ RadixTree(const Napi::CallbackInfo& info);
32
+
33
+ private:
34
+ static Napi::FunctionReference constructor;
35
+ radix_tree* tree;
36
+ radix_iterator* treeIt;
37
+
38
+ Napi::Value Insert(const Napi::CallbackInfo& info);
39
+ Napi::Value Remove(const Napi::CallbackInfo& info);
40
+ Napi::Value Find(const Napi::CallbackInfo& info);
41
+ Napi::Value Search(const Napi::CallbackInfo& info);
42
+ void Cleanup(const Napi::CallbackInfo& info);
43
+ };
44
+
45
+ Napi::FunctionReference RaxIterator::constructor;
46
+ Napi::FunctionReference RadixTree::constructor;
47
+
48
+ // RaxIterator implementation
49
+ Napi::Object RaxIterator::Init(Napi::Env env, Napi::Object exports) {
50
+ Napi::HandleScope scope(env);
51
+
52
+ Napi::Function func = DefineClass(env, "RaxIterator", {
53
+ InstanceMethod("next", &RaxIterator::Next),
54
+ InstanceMethod("prev", &RaxIterator::Prev),
55
+ InstanceMethod("up", &RaxIterator::Up),
56
+ InstanceMethod("stop", &RaxIterator::Stop),
57
+ });
58
+
59
+ constructor = Napi::Persistent(func);
60
+ constructor.SuppressDestruct();
61
+
62
+ exports.Set("RaxIterator", func);
63
+ return exports;
64
+ }
65
+
66
+ RaxIterator::RaxIterator(const Napi::CallbackInfo& info) : Napi::ObjectWrap<RaxIterator>(info) {
67
+ Napi::Env env = info.Env();
68
+ if (info.Length() < 3 || !info[0].IsExternal() || !info[1].IsExternal() || !info[2].IsString()) {
69
+ Napi::TypeError::New(env, "Invalid arguments").ThrowAsJavaScriptException();
70
+ return;
71
+ }
72
+
73
+ this->tree = info[0].As<Napi::External<radix_tree>>().Data();
74
+ this->iterator = info[1].As<Napi::External<radix_iterator>>().Data();
75
+
76
+ // Initialize path
77
+ std::string pathStr = info[2].As<Napi::String>().Utf8Value();
78
+ this->path.buf = new unsigned char[pathStr.length()];
79
+ memcpy(this->path.buf, pathStr.c_str(), pathStr.length());
80
+ this->path.len = pathStr.length();
81
+ }
82
+
83
+ Napi::Value RaxIterator::Next(const Napi::CallbackInfo& info) {
84
+ Napi::Env env = info.Env();
85
+ int result = raxtree_next(this->iterator, &this->path);
86
+ return Napi::Number::New(env, result);
87
+ }
88
+
89
+ Napi::Value RaxIterator::Prev(const Napi::CallbackInfo& info) {
90
+ Napi::Env env = info.Env();
91
+ int result = raxtree_prev(this->iterator, &this->path);
92
+ return Napi::Number::New(env, result);
93
+ }
94
+
95
+ Napi::Value RaxIterator::Up(const Napi::CallbackInfo& info) {
96
+ Napi::Env env = info.Env();
97
+ int result = raxtree_up(this->iterator, &this->path);
98
+ return Napi::Number::New(env, result);
99
+ }
100
+
101
+ Napi::Value RaxIterator::Stop(const Napi::CallbackInfo& info) {
102
+ Napi::Env env = info.Env();
103
+ raxtree_stop(this->iterator);
104
+ return env.Undefined();
105
+ }
106
+
107
+ // RadixTree implementation
108
+ Napi::Object RadixTree::Init(Napi::Env env, Napi::Object exports) {
109
+ Napi::HandleScope scope(env);
110
+
111
+ Napi::Function func = DefineClass(env, "RadixTree", {
112
+ InstanceMethod("insert", &RadixTree::Insert),
113
+ InstanceMethod("remove", &RadixTree::Remove),
114
+ InstanceMethod("find", &RadixTree::Find),
115
+ InstanceMethod("search", &RadixTree::Search),
116
+ InstanceMethod("cleanup", &RadixTree::Cleanup),
117
+ });
118
+
119
+ constructor = Napi::Persistent(func);
120
+ constructor.SuppressDestruct();
121
+
122
+ exports.Set("RadixTree", func);
123
+ return exports;
124
+ }
125
+
126
+ RadixTree::RadixTree(const Napi::CallbackInfo& info) : Napi::ObjectWrap<RadixTree>(info) {
127
+ this->tree = raxtree_new();
128
+ this->treeIt = raxtree_new_it(this->tree);
129
+ }
130
+
131
+ Napi::Value RadixTree::Insert(const Napi::CallbackInfo& info) {
132
+ Napi::Env env = info.Env();
133
+ if (info.Length() < 2) {
134
+ Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
135
+ return env.Null();
136
+ }
137
+
138
+ std::string key = info[0].As<Napi::String>().Utf8Value();
139
+
140
+ if (!info[1].IsNumber()) {
141
+ Napi::TypeError::New(env, "Second argument must be an integer").ThrowAsJavaScriptException();
142
+ return env.Null();
143
+ }
144
+
145
+ int64_t value = info[1].As<Napi::Number>().Int64Value();
146
+
147
+ if (value > INT_MAX || value < INT_MIN) {
148
+ Napi::RangeError::New(env, "Value is out of range for int").ThrowAsJavaScriptException();
149
+ return env.Null();
150
+ }
151
+
152
+ rt_str_t path;
153
+ path.buf = (unsigned char*)key.c_str();
154
+ path.len = key.length();
155
+
156
+ int result = raxtree_insert(this->tree, &path, static_cast<int>(value));
157
+ return Napi::Number::New(env, result);
158
+ }
159
+
160
+ Napi::Value RadixTree::Remove(const Napi::CallbackInfo& info) {
161
+ Napi::Env env = info.Env();
162
+ if (info.Length() < 1) {
163
+ Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
164
+ return env.Null();
165
+ }
166
+
167
+ std::string key = info[0].As<Napi::String>().Utf8Value();
168
+
169
+ rt_str_t path;
170
+ path.buf = (unsigned char*)key.c_str();
171
+ path.len = key.length();
172
+
173
+ int result = raxtree_remove(this->tree, &path);
174
+
175
+ return Napi::Number::New(env, result);
176
+ }
177
+
178
+ Napi::Value RadixTree::Find(const Napi::CallbackInfo& info) {
179
+ Napi::Env env = info.Env();
180
+ if (info.Length() < 1) {
181
+ Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
182
+ return env.Null();
183
+ }
184
+
185
+ std::string key = info[0].As<Napi::String>().Utf8Value();
186
+
187
+ rt_str_t path;
188
+ path.buf = (unsigned char*)key.c_str();
189
+ path.len = key.length();
190
+
191
+ void* result = raxtree_find(this->tree, &path);
192
+
193
+ if (result == NULL) {
194
+ return env.Null();
195
+ }
196
+
197
+ int value = reinterpret_cast<intptr_t>(result);
198
+ return Napi::Number::New(env, value);
199
+ }
200
+
201
+ Napi::Value RadixTree::Search(const Napi::CallbackInfo& info) {
202
+ Napi::Env env = info.Env();
203
+ if (info.Length() < 1) {
204
+ Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
205
+ return env.Null();
206
+ }
207
+
208
+ std::string ele = info[0].As<Napi::String>().Utf8Value();
209
+ rt_str_t path;
210
+ path.buf = (unsigned char*)ele.c_str();
211
+ path.len = ele.length();
212
+
213
+ radix_iterator* result = (radix_iterator*)raxtree_search(this->tree, this->treeIt, &path);
214
+ if (result == NULL) {
215
+ return env.Null();
216
+ }
217
+
218
+ auto iteratorInstance = RaxIterator::constructor.New({
219
+ Napi::External<radix_tree>::New(env, this->tree),
220
+ Napi::External<radix_iterator>::New(env, result),
221
+ Napi::String::New(env, ele)
222
+ });
223
+
224
+ return iteratorInstance;
225
+ }
226
+
227
+ void RadixTree::Cleanup(const Napi::CallbackInfo& info) {
228
+ raxtree_stop(this->treeIt);
229
+ raxtree_destroy(this->tree);
230
+ }
231
+
232
+ Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
233
+ RadixTree::Init(env, exports);
234
+ RaxIterator::Init(env, exports);
235
+ return exports;
236
+ }
237
+
238
+ NODE_API_MODULE(radix_tree, InitAll)