ffi-radix_tree 0.2.0 → 0.3.0
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.
- checksums.yaml +4 -4
- data/README.md +38 -1
- data/Rakefile +61 -0
- data/ffi-radix_tree.gemspec +1 -0
- data/lib/ffi/radix_tree.rb +103 -1
- data/lib/ffi/radix_tree/version.rb +1 -1
- data/vendor/radixtree/ffi_radix_tree.cpp +94 -3
- data/vendor/radixtree/radix_tree.hpp +61 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b44935a7f81323aa80f656cab493c5637d3029a0
|
4
|
+
data.tar.gz: f2854b861c6c3507110328ce5e291f533938f97c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45f6f4eb51181c92fedeb6fbfa577b67a73a921196edfc4ce937c251dd9a660d9ff6888c1f17f2afe67cd5d2858997ce947946588448e4d2e87810138bae4764
|
7
|
+
data.tar.gz: ecb205e7db9b41cf866fce8709d894eebf1b21472fd5546b2fb529dea605bd9a06494c66d83d5d1401e00b601861d72e1108dd34243320d1059595e5b1d4fcb8
|
data/README.md
CHANGED
@@ -22,7 +22,44 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
|
25
|
+
```ruby
|
26
|
+
# create a new tree
|
27
|
+
rtree = ::FFI::RadixTree::Tree.new
|
28
|
+
|
29
|
+
# add key/value pairs to the tree
|
30
|
+
rtree.push("key1", "value1")
|
31
|
+
rtree.push("key2", ["value2", "value3"])
|
32
|
+
rtree.push("key3", { :attr1 => "value4", :attr2 => "value5" })
|
33
|
+
|
34
|
+
# work with the collection
|
35
|
+
if rtree.has_key?("key1")
|
36
|
+
val = rtree.get("key2")
|
37
|
+
rtree.set("key3", "value6")
|
38
|
+
end
|
39
|
+
|
40
|
+
# search the tree using the keys
|
41
|
+
|
42
|
+
# keys based off the root word: "manage"
|
43
|
+
rtree.push("manage", "base verb")
|
44
|
+
rtree.push("managed", "past tense")
|
45
|
+
rtree.push("manager", "noun")
|
46
|
+
rtree.push("managers", "plural noun")
|
47
|
+
rtree.push("managing", "present tense")
|
48
|
+
|
49
|
+
# Find the key that matches the _most_ of the beggining of the search term
|
50
|
+
rtree.longest_prefix("managerial") # returns "manager"
|
51
|
+
rtree.longest_prefix_and_value("managerial") # returns ["manager", "noun"]
|
52
|
+
rtree.longest_prefix_value("managerial") # returns "noun"
|
53
|
+
|
54
|
+
# Find all values whose keys match the _most_ of the beginning of the search term
|
55
|
+
rtree.greedy_match("managerial") # returns ["noun", "plural noun"]
|
56
|
+
|
57
|
+
# Find all values whose keys are included _anywhere_ in the search term
|
58
|
+
rtree.greedy_substring_match("I managed to jump") # returns ["base verb", "past tense"]
|
59
|
+
|
60
|
+
# cleanup
|
61
|
+
rtree.destroy!
|
62
|
+
```
|
26
63
|
|
27
64
|
## Development
|
28
65
|
|
data/Rakefile
CHANGED
@@ -7,6 +7,67 @@ namespace :radixtree do
|
|
7
7
|
task :compile do
|
8
8
|
Rake::Task[:compile_radixtree].invoke
|
9
9
|
end
|
10
|
+
|
11
|
+
desc "run benchmarks for radixtree lib"
|
12
|
+
task :benchmark do
|
13
|
+
require "benchmark/ips"
|
14
|
+
require "./lib/ffi/radix_tree"
|
15
|
+
|
16
|
+
radix_tree = ::FFI::RadixTree::Tree.new
|
17
|
+
|
18
|
+
(1..1000).each do |number|
|
19
|
+
radix_tree.push(number.to_s, "DERP#{number}" * (number % 10))
|
20
|
+
radix_tree.push(number.to_s * 100, "DERP#{number}" * (number % 10))
|
21
|
+
radix_tree.push(number.to_s * 1000, "DERP#{number}" * (number % 10))
|
22
|
+
end
|
23
|
+
|
24
|
+
::Benchmark.ips do |x|
|
25
|
+
x.config(:warmup => 10)
|
26
|
+
|
27
|
+
x.report("get") do
|
28
|
+
radix_tree.get(rand(1000).to_s * [1, 100, 100].sample)
|
29
|
+
end
|
30
|
+
|
31
|
+
x.report("longest prefix") do
|
32
|
+
radix_tree.longest_prefix(rand(1000).to_s * [1, 100, 100].sample)
|
33
|
+
end
|
34
|
+
|
35
|
+
x.report("get then value") do
|
36
|
+
val = rand(1000).to_s * [1, 100, 100].sample
|
37
|
+
|
38
|
+
radix_tree.longest_prefix(val)
|
39
|
+
radix_tree.longest_prefix_value(val)
|
40
|
+
end
|
41
|
+
|
42
|
+
x.report("prefix and value (combined)") do
|
43
|
+
radix_tree.longest_prefix_and_value(rand(1000).to_s * [1, 100, 100].sample)
|
44
|
+
end
|
45
|
+
|
46
|
+
x.report("longest prefix (miss)") do
|
47
|
+
radix_tree.longest_prefix("DERP DERPIE")
|
48
|
+
end
|
49
|
+
|
50
|
+
x.report("prefix and value (combined/miss)") do
|
51
|
+
radix_tree.longest_prefix_and_value("DERP DERPIE")
|
52
|
+
end
|
53
|
+
|
54
|
+
x.report("greedy match") do
|
55
|
+
radix_tree.greedy_match(rand(1000).to_s * [1, 100, 100].sample)
|
56
|
+
end
|
57
|
+
|
58
|
+
x.report("greedy match (miss)") do
|
59
|
+
radix_tree.greedy_match("DERP DERPIE")
|
60
|
+
end
|
61
|
+
|
62
|
+
x.report("greedy substring match") do
|
63
|
+
radix_tree.greedy_substring_match(rand(1000).to_s * [1, 100, 100].sample)
|
64
|
+
end
|
65
|
+
|
66
|
+
x.report("greedy substring match (miss)") do
|
67
|
+
radix_tree.greedy_substring_match("DERP DERPIE")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
10
71
|
end
|
11
72
|
|
12
73
|
Rake::TestTask.new(:spec) do |t|
|
data/ffi-radix_tree.gemspec
CHANGED
@@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_dependency "msgpack"
|
35
35
|
spec.add_dependency "ffi"
|
36
36
|
|
37
|
+
spec.add_development_dependency "benchmark-ips"
|
37
38
|
spec.add_development_dependency "bundler", "~> 1.15"
|
38
39
|
spec.add_development_dependency "rake", "~> 10.0"
|
39
40
|
spec.add_development_dependency "mocha"
|
data/lib/ffi/radix_tree.rb
CHANGED
@@ -77,9 +77,14 @@ module FFI
|
|
77
77
|
attach_function :erase, [:pointer, :string], :void
|
78
78
|
attach_function :fetch, [:pointer, :string, :pointer], :pointer
|
79
79
|
attach_function :insert, [:pointer, :string, :pointer, :size_t], :void
|
80
|
+
attach_function :update, [:pointer, :string, :pointer, :size_t], :bool
|
80
81
|
attach_function :longest_prefix, [:pointer, :string], :string
|
82
|
+
attach_function :longest_prefix_and_value, [:pointer, :string, :pointer, :pointer], :pointer
|
81
83
|
attach_function :longest_prefix_value, [:pointer, :string, :pointer], :pointer
|
84
|
+
attach_function :greedy_match, [:pointer, :string, :pointer, :pointer], :int
|
85
|
+
attach_function :greedy_substring_match, [:pointer, :string, :pointer, :pointer], :int
|
82
86
|
attach_function :match_free, [:pointer], :void
|
87
|
+
attach_function :multi_match_free, [:pointer, :int], :void
|
83
88
|
attach_function :has_key, [:pointer, :string], :bool
|
84
89
|
|
85
90
|
class Tree
|
@@ -115,6 +120,21 @@ module FFI
|
|
115
120
|
push_response
|
116
121
|
end
|
117
122
|
|
123
|
+
def push_or_update(key, value)
|
124
|
+
response = nil
|
125
|
+
@first_character_present[key[0]] = true
|
126
|
+
storage_data = ::MessagePack.pack(value)
|
127
|
+
bytesize = storage_data.bytesize
|
128
|
+
|
129
|
+
::FFI::MemoryPointer.new(:char, bytesize, true) do |memory_buffer|
|
130
|
+
memory_buffer.put_bytes(0, storage_data)
|
131
|
+
response = ::FFI::RadixTree.update(@ptr, key, memory_buffer, bytesize)
|
132
|
+
response ||= ::FFI::RadixTree.insert(@ptr, key, memory_buffer, bytesize)
|
133
|
+
end
|
134
|
+
|
135
|
+
response
|
136
|
+
end
|
137
|
+
|
118
138
|
def get(key)
|
119
139
|
return nil unless @first_character_present[key[0]]
|
120
140
|
byte_pointer = get_response = nil
|
@@ -122,7 +142,11 @@ module FFI
|
|
122
142
|
::FFI::MemoryPointer.new(:int) do |byte_length|
|
123
143
|
byte_pointer = ::FFI::RadixTree.fetch(@ptr, key, byte_length)
|
124
144
|
bytesize = byte_length.read_int
|
125
|
-
|
145
|
+
|
146
|
+
if bytesize && bytesize > 0
|
147
|
+
bytes = byte_pointer.get_bytes(0, bytesize)
|
148
|
+
get_response = ::MessagePack.unpack(bytes)
|
149
|
+
end
|
126
150
|
end
|
127
151
|
|
128
152
|
get_response
|
@@ -139,6 +163,29 @@ module FFI
|
|
139
163
|
::FFI::RadixTree.match_free(p_out) if p_out
|
140
164
|
end
|
141
165
|
|
166
|
+
def longest_prefix_and_value(string)
|
167
|
+
return [nil, nil] unless @first_character_present[string[0]]
|
168
|
+
byte_pointer = prefix_response = get_response = nil
|
169
|
+
|
170
|
+
::FFI::MemoryPointer.new(:int) do |byte_length|
|
171
|
+
::FFI::MemoryPointer.new(:int) do |prefix_length|
|
172
|
+
byte_pointer = ::FFI::RadixTree.longest_prefix_and_value(@ptr, string, byte_length, prefix_length)
|
173
|
+
bytesize = byte_length.read_int
|
174
|
+
|
175
|
+
if bytesize && bytesize > 0
|
176
|
+
prefix_size = prefix_length.read_int
|
177
|
+
get_response = byte_pointer.get_bytes(0, bytesize)
|
178
|
+
prefix_response = get_response[0..(prefix_size - 1)]
|
179
|
+
get_response = ::MessagePack.unpack(get_response[prefix_size..-1])
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
[prefix_response, get_response]
|
185
|
+
ensure
|
186
|
+
::FFI::RadixTree.match_free(byte_pointer) if byte_pointer
|
187
|
+
end
|
188
|
+
|
142
189
|
def longest_prefix_value(string)
|
143
190
|
return nil unless @first_character_present[string[0]]
|
144
191
|
byte_pointer = get_response = nil
|
@@ -153,6 +200,61 @@ module FFI
|
|
153
200
|
ensure
|
154
201
|
::FFI::RadixTree.match_free(byte_pointer) if byte_pointer
|
155
202
|
end
|
203
|
+
|
204
|
+
def greedy_match(string)
|
205
|
+
return [] unless @first_character_present[string[0]]
|
206
|
+
array_pointer = nil
|
207
|
+
match_sizes_pointer = nil
|
208
|
+
array_size = 0
|
209
|
+
get_response = []
|
210
|
+
|
211
|
+
::FFI::MemoryPointer.new(:pointer) do |match_array|
|
212
|
+
::FFI::MemoryPointer.new(:pointer) do |match_sizes_array|
|
213
|
+
array_size = ::FFI::RadixTree.greedy_match(@ptr, string, match_array, match_sizes_array)
|
214
|
+
if array_size > 0
|
215
|
+
array_sizes_pointer = match_sizes_array.read_pointer
|
216
|
+
match_sizes = array_sizes_pointer.get_array_of_int(0, array_size)
|
217
|
+
array_pointer = match_array.read_pointer
|
218
|
+
char_arrays = array_pointer.get_array_of_pointer(0, array_size)
|
219
|
+
char_arrays.each_with_index do |ptr, index|
|
220
|
+
get_response << ::MessagePack.unpack(ptr.get_bytes(0, match_sizes[index]))
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
get_response
|
227
|
+
ensure
|
228
|
+
::FFI::RadixTree.multi_match_free(array_pointer, array_size) if array_pointer
|
229
|
+
::FFI::RadixTree.match_free(match_sizes_pointer) if match_sizes_pointer
|
230
|
+
end
|
231
|
+
|
232
|
+
def greedy_substring_match(string)
|
233
|
+
array_pointer = nil
|
234
|
+
match_sizes_pointer = nil
|
235
|
+
array_size = 0
|
236
|
+
get_response = []
|
237
|
+
|
238
|
+
::FFI::MemoryPointer.new(:pointer) do |match_array|
|
239
|
+
::FFI::MemoryPointer.new(:pointer) do |match_sizes_array|
|
240
|
+
array_size = ::FFI::RadixTree.greedy_substring_match(@ptr, string, match_array, match_sizes_array)
|
241
|
+
if array_size > 0
|
242
|
+
array_sizes_pointer = match_sizes_array.read_pointer
|
243
|
+
match_sizes = array_sizes_pointer.get_array_of_int(0, array_size)
|
244
|
+
array_pointer = match_array.read_pointer
|
245
|
+
char_arrays = array_pointer.get_array_of_pointer(0, array_size)
|
246
|
+
char_arrays.each_with_index do |ptr, index|
|
247
|
+
get_response << ::MessagePack.unpack(ptr.get_bytes(0, match_sizes[index]))
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
get_response
|
254
|
+
ensure
|
255
|
+
::FFI::RadixTree.multi_match_free(array_pointer, array_size) if array_pointer
|
256
|
+
::FFI::RadixTree.match_free(match_sizes_pointer) if match_sizes_pointer
|
257
|
+
end
|
156
258
|
end
|
157
259
|
end
|
158
260
|
end
|
@@ -38,13 +38,23 @@ void match_free(const char* match) {
|
|
38
38
|
}
|
39
39
|
}
|
40
40
|
|
41
|
+
void multi_match_free(const char** match, int length) {
|
42
|
+
if (match != NULL) {
|
43
|
+
for (int i=0; i<length; ++i) {
|
44
|
+
delete[] match[i];
|
45
|
+
match[i] = NULL;
|
46
|
+
}
|
47
|
+
delete[] match;
|
48
|
+
match = NULL;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
41
52
|
const char* longest_prefix(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key) {
|
42
53
|
std::string string_key(key);
|
43
54
|
auto iter = map_pointer->longest_match(string_key);
|
44
55
|
|
45
56
|
if (iter != map_pointer->end()) {
|
46
57
|
char *val = new char[iter->first.size() + 1]{0};
|
47
|
-
val[iter->first.size()] = '\0';
|
48
58
|
memcpy(val, iter->first.c_str(), iter->first.size());
|
49
59
|
|
50
60
|
return val;
|
@@ -53,6 +63,33 @@ const char* longest_prefix(radix_tree<std::string, std::vector<char>>* map_point
|
|
53
63
|
return NULL;
|
54
64
|
}
|
55
65
|
|
66
|
+
const char* longest_prefix_and_value(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, int* read_size, int* prefix_size) {
|
67
|
+
std::string string_key(key);
|
68
|
+
auto iter = map_pointer->longest_match(string_key);
|
69
|
+
|
70
|
+
if (iter != map_pointer->end()) {
|
71
|
+
long counter = 0;
|
72
|
+
int size_of_response = iter->second.size() + iter->first.size();
|
73
|
+
char *return_val = new char[size_of_response]{0};
|
74
|
+
|
75
|
+
strncpy(return_val, iter->first.c_str(), iter->first.size());
|
76
|
+
counter = iter->first.size();
|
77
|
+
|
78
|
+
for( auto& val3 : iter->second) {
|
79
|
+
return_val[counter] = val3;
|
80
|
+
counter++;
|
81
|
+
}
|
82
|
+
|
83
|
+
*prefix_size = iter->first.size();
|
84
|
+
*read_size = size_of_response;
|
85
|
+
return return_val;
|
86
|
+
}
|
87
|
+
|
88
|
+
*prefix_size = 0;
|
89
|
+
*read_size = 0;
|
90
|
+
return NULL;
|
91
|
+
}
|
92
|
+
|
56
93
|
const char* longest_prefix_value(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, int* read_size) {
|
57
94
|
std::string string_key(key);
|
58
95
|
auto iter = map_pointer->longest_match(string_key);
|
@@ -73,12 +110,12 @@ const char* longest_prefix_value(radix_tree<std::string, std::vector<char>>* map
|
|
73
110
|
return NULL;
|
74
111
|
}
|
75
112
|
|
76
|
-
|
113
|
+
unsigned char* fetch(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, int* read_size) {
|
77
114
|
auto iter = map_pointer->find(std::string(key));
|
78
115
|
long counter = 0;
|
79
116
|
|
80
117
|
if (iter != map_pointer->end()) {
|
81
|
-
char *return_val = new char[iter->second.size()]{0};
|
118
|
+
unsigned char *return_val = new unsigned char[iter->second.size()]{0};
|
82
119
|
for( auto& val : iter->second ) {
|
83
120
|
return_val[counter] = val;
|
84
121
|
counter++;
|
@@ -92,10 +129,64 @@ const char* fetch(radix_tree<std::string, std::vector<char>>* map_pointer, const
|
|
92
129
|
return NULL;
|
93
130
|
}
|
94
131
|
|
132
|
+
int greedy_match(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, const char*** matches, int** match_sizes) {
|
133
|
+
std::string string_key(key);
|
134
|
+
typedef radix_tree<std::string, std::vector<char>>::iterator iterator;
|
135
|
+
std::vector<iterator> vec;
|
136
|
+
map_pointer->greedy_match(string_key, vec);
|
137
|
+
long counter = 0;
|
138
|
+
|
139
|
+
if (vec.size() > 0) {
|
140
|
+
*matches = new const char* [vec.size()]{nullptr};
|
141
|
+
*match_sizes = new int [vec.size()]{0};
|
142
|
+
for (auto& iter : vec) {
|
143
|
+
auto ret_str = new char[iter->second.size()];
|
144
|
+
long char_index = 0;
|
145
|
+
for (auto& val : iter->second) {
|
146
|
+
ret_str[char_index] = val;
|
147
|
+
++char_index;
|
148
|
+
}
|
149
|
+
(*matches)[counter] = ret_str;
|
150
|
+
(*match_sizes)[counter] = iter->second.size();
|
151
|
+
++counter;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
return counter;
|
155
|
+
}
|
156
|
+
|
157
|
+
int greedy_substring_match(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, const char*** matches, int** match_sizes) {
|
158
|
+
std::string string_key(key);
|
159
|
+
typedef radix_tree<std::string, std::vector<char>>::iterator iterator;
|
160
|
+
std::vector<iterator> vec;
|
161
|
+
map_pointer->greedy_substring_match(string_key, vec);
|
162
|
+
long counter = 0;
|
163
|
+
|
164
|
+
if (vec.size() > 0) {
|
165
|
+
*matches = new const char* [vec.size()]{nullptr};
|
166
|
+
*match_sizes = new int [vec.size()]{0};
|
167
|
+
for (auto& iter : vec) {
|
168
|
+
auto ret_str = new char[iter->second.size()];
|
169
|
+
long char_index = 0;
|
170
|
+
for (auto& val : iter->second) {
|
171
|
+
ret_str[char_index] = val;
|
172
|
+
++char_index;
|
173
|
+
}
|
174
|
+
(*matches)[counter] = ret_str;
|
175
|
+
(*match_sizes)[counter] = iter->second.size();
|
176
|
+
++counter;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
return counter;
|
180
|
+
}
|
181
|
+
|
95
182
|
void insert(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, char* value, size_t size) {
|
96
183
|
map_pointer->insert({std::string(key), std::vector<char>(value, value + size)});
|
97
184
|
}
|
98
185
|
|
186
|
+
bool update(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, char* value, size_t size) {
|
187
|
+
return map_pointer->update({std::string(key), std::vector<char>(value, value + size)});
|
188
|
+
}
|
189
|
+
|
99
190
|
void destroy(radix_tree<std::string, std::vector<char>>* map_pointer) {
|
100
191
|
delete map_pointer;
|
101
192
|
map_pointer = NULL;
|
@@ -67,10 +67,12 @@ public:
|
|
67
67
|
iterator end();
|
68
68
|
|
69
69
|
std::pair<iterator, bool> insert(const value_type &val);
|
70
|
+
bool update(const value_type &val);
|
70
71
|
bool erase(const K &key);
|
71
72
|
void erase(iterator it);
|
72
73
|
void prefix_match(const K &key, std::vector<iterator> &vec);
|
73
74
|
void greedy_match(const K &key, std::vector<iterator> &vec);
|
75
|
+
void greedy_substring_match(const K &key, std::vector<iterator> &vec);
|
74
76
|
iterator longest_match(const K &key);
|
75
77
|
|
76
78
|
T& operator[] (const K &lhs);
|
@@ -81,9 +83,11 @@ private:
|
|
81
83
|
|
82
84
|
radix_tree_node<K, T>* begin(radix_tree_node<K, T> *node);
|
83
85
|
radix_tree_node<K, T>* find_node(const K &key, radix_tree_node<K, T> *node, int depth);
|
86
|
+
std::vector<radix_tree_node<K, T>*> find_leaf_nodes_with_substr(const K &key, radix_tree_node<K, T> *node, const K &ancestor_key);
|
84
87
|
radix_tree_node<K, T>* append(radix_tree_node<K, T> *parent, const value_type &val);
|
85
88
|
radix_tree_node<K, T>* prepend(radix_tree_node<K, T> *node, const value_type &val);
|
86
89
|
void greedy_match(radix_tree_node<K, T> *node, std::vector<iterator> &vec);
|
90
|
+
void greedy_substring_match(radix_tree_node<K, T> *node, std::vector<iterator> &vec);
|
87
91
|
|
88
92
|
radix_tree(const radix_tree& other); // delete
|
89
93
|
radix_tree& operator =(const radix_tree other); // delete
|
@@ -232,6 +236,23 @@ void radix_tree<K, T>::greedy_match(radix_tree_node<K, T> *node, std::vector<ite
|
|
232
236
|
}
|
233
237
|
}
|
234
238
|
|
239
|
+
template <typename K, typename T>
|
240
|
+
void radix_tree<K, T>::greedy_substring_match(const K &key, std::vector<iterator> &vec)
|
241
|
+
{
|
242
|
+
std::vector<radix_tree_node<K, T>*> nodes;
|
243
|
+
|
244
|
+
vec.clear();
|
245
|
+
|
246
|
+
if (m_root == NULL)
|
247
|
+
return;
|
248
|
+
|
249
|
+
nodes = find_leaf_nodes_with_substr(key, m_root, "");
|
250
|
+
|
251
|
+
for (auto& node : nodes) {
|
252
|
+
vec.push_back(node);
|
253
|
+
}
|
254
|
+
}
|
255
|
+
|
235
256
|
template <typename K, typename T>
|
236
257
|
void radix_tree<K, T>::erase(iterator it)
|
237
258
|
{
|
@@ -448,6 +469,22 @@ std::pair<typename radix_tree<K, T>::iterator, bool> radix_tree<K, T>::insert(co
|
|
448
469
|
}
|
449
470
|
}
|
450
471
|
|
472
|
+
template <typename K, typename T>
|
473
|
+
bool radix_tree<K, T>::update(const value_type &val)
|
474
|
+
{
|
475
|
+
if (m_root == NULL)
|
476
|
+
return false;
|
477
|
+
|
478
|
+
radix_tree_node<K, T> *node = find_node(val.first, m_root, 0);
|
479
|
+
|
480
|
+
// if the node is a internal node, return false
|
481
|
+
if (! node->m_is_leaf)
|
482
|
+
return false;
|
483
|
+
|
484
|
+
node->m_value = new value_type(val);
|
485
|
+
return true;
|
486
|
+
}
|
487
|
+
|
451
488
|
template <typename K, typename T>
|
452
489
|
typename radix_tree<K, T>::iterator radix_tree<K, T>::find(const K &key)
|
453
490
|
{
|
@@ -495,6 +532,30 @@ radix_tree_node<K, T>* radix_tree<K, T>::find_node(const K &key, radix_tree_node
|
|
495
532
|
return node;
|
496
533
|
}
|
497
534
|
|
535
|
+
template <typename K, typename T>
|
536
|
+
std::vector<radix_tree_node<K, T>*> radix_tree<K, T>::find_leaf_nodes_with_substr(const K &key, radix_tree_node<K, T> *node, const K &ancestor_key)
|
537
|
+
{
|
538
|
+
std::vector<radix_tree_node<K, T>*> nodes;
|
539
|
+
|
540
|
+
if (node->m_children.empty())
|
541
|
+
return nodes;
|
542
|
+
|
543
|
+
typename radix_tree_node<K, T>::it_child it;
|
544
|
+
for (it = node->m_children.begin(); it != node->m_children.end(); ++it) {
|
545
|
+
if (it->second->m_is_leaf) {
|
546
|
+
nodes.push_back(it->second);
|
547
|
+
} else {
|
548
|
+
auto child_key = ancestor_key + it->first;
|
549
|
+
if (key.find(child_key) != std::string::npos ) {
|
550
|
+
auto found_nodes = find_leaf_nodes_with_substr(key, it->second, child_key);
|
551
|
+
nodes.insert(nodes.end(), found_nodes.begin(), found_nodes.end());
|
552
|
+
}
|
553
|
+
}
|
554
|
+
}
|
555
|
+
|
556
|
+
return nodes;
|
557
|
+
}
|
558
|
+
|
498
559
|
/*
|
499
560
|
|
500
561
|
(root)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-radix_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Dewitt
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: benchmark-ips
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|