ffi-radix_tree 0.2.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +35 -1
- data/Rakefile +61 -0
- data/ffi-radix_tree.gemspec +2 -1
- data/lib/ffi/radix_tree/version.rb +1 -1
- data/lib/ffi/radix_tree.rb +111 -11
- data/vendor/radixtree/ffi_radix_tree.cpp +93 -3
- data/vendor/radixtree/radix_tree.hpp +61 -0
- metadata +24 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 63946ad680524b97f53dd06a3bb4514ce3ab4b12a2f2d5a23fef32e36941bfba
|
4
|
+
data.tar.gz: 4235440c2f4f348c3d9f9e496ba5dbf1b0c5c4645b674cd04086ce60f57da650
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ac02fdf65f154c8c3746a565b2b6d3afe738643b9227f82313ddc7bc12cfce6f2f617ac535a876fa2b6da60588fe8564b9d24d86212f71f568fcf2d08ce9ff3
|
7
|
+
data.tar.gz: ee16b0514b5a1efef08d3a95c7646e78e9ab44d2f110a3b928aa5335964f63a86b1fb064847c9ba9ac417c4c951bc72259e8039a07556b8c0739806affe9bd37
|
data/README.md
CHANGED
@@ -22,7 +22,41 @@ 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
|
+
```
|
26
60
|
|
27
61
|
## Development
|
28
62
|
|
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,7 +34,8 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_dependency "msgpack"
|
35
35
|
spec.add_dependency "ffi"
|
36
36
|
|
37
|
-
spec.add_development_dependency "
|
37
|
+
spec.add_development_dependency "benchmark-ips"
|
38
|
+
spec.add_development_dependency "bundler"
|
38
39
|
spec.add_development_dependency "rake", "~> 10.0"
|
39
40
|
spec.add_development_dependency "mocha"
|
40
41
|
spec.add_development_dependency "pry"
|
data/lib/ffi/radix_tree.rb
CHANGED
@@ -77,30 +77,29 @@ 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 :match_sizes_free, [:pointer], :void
|
88
|
+
attach_function :multi_match_free, [:pointer, :int], :void
|
83
89
|
attach_function :has_key, [:pointer, :string], :bool
|
84
90
|
|
85
91
|
class Tree
|
86
|
-
def self.destroy!(tree)
|
87
|
-
tree.destroy! unless tree.nil?
|
88
|
-
end
|
89
|
-
|
90
92
|
def initialize
|
91
|
-
@ptr = ::FFI::RadixTree.create
|
93
|
+
@ptr = ::FFI::AutoPointer.new(::FFI::RadixTree.create, ::FFI::RadixTree.method(:destroy))
|
92
94
|
@first_character_present = {}
|
93
95
|
end
|
94
96
|
|
95
|
-
def destroy!
|
96
|
-
::FFI::RadixTree.destroy(@ptr) unless @ptr.nil?
|
97
|
-
@ptr = nil
|
98
|
-
end
|
99
|
-
|
100
97
|
def has_key?(key)
|
101
98
|
::FFI::RadixTree.has_key(@ptr, key)
|
102
99
|
end
|
103
100
|
|
101
|
+
alias_method :key?, :has_key?
|
102
|
+
|
104
103
|
def push(key, value)
|
105
104
|
push_response = nil
|
106
105
|
@first_character_present[key[0]] = true
|
@@ -115,6 +114,25 @@ module FFI
|
|
115
114
|
push_response
|
116
115
|
end
|
117
116
|
|
117
|
+
def push_or_update(key, value)
|
118
|
+
response = nil
|
119
|
+
@first_character_present[key[0]] = true
|
120
|
+
storage_data = ::MessagePack.pack(value)
|
121
|
+
bytesize = storage_data.bytesize
|
122
|
+
|
123
|
+
::FFI::MemoryPointer.new(:char, bytesize, true) do |memory_buffer|
|
124
|
+
memory_buffer.put_bytes(0, storage_data)
|
125
|
+
response = ::FFI::RadixTree.update(@ptr, key, memory_buffer, bytesize)
|
126
|
+
response ||= ::FFI::RadixTree.insert(@ptr, key, memory_buffer, bytesize)
|
127
|
+
end
|
128
|
+
|
129
|
+
response
|
130
|
+
end
|
131
|
+
|
132
|
+
def erase(key)
|
133
|
+
::FFI::RadixTree.erase(@ptr, key)
|
134
|
+
end
|
135
|
+
|
118
136
|
def get(key)
|
119
137
|
return nil unless @first_character_present[key[0]]
|
120
138
|
byte_pointer = get_response = nil
|
@@ -122,7 +140,11 @@ module FFI
|
|
122
140
|
::FFI::MemoryPointer.new(:int) do |byte_length|
|
123
141
|
byte_pointer = ::FFI::RadixTree.fetch(@ptr, key, byte_length)
|
124
142
|
bytesize = byte_length.read_int
|
125
|
-
|
143
|
+
|
144
|
+
if bytesize && bytesize > 0
|
145
|
+
bytes = byte_pointer.get_bytes(0, bytesize)
|
146
|
+
get_response = ::MessagePack.unpack(bytes)
|
147
|
+
end
|
126
148
|
end
|
127
149
|
|
128
150
|
get_response
|
@@ -139,6 +161,29 @@ module FFI
|
|
139
161
|
::FFI::RadixTree.match_free(p_out) if p_out
|
140
162
|
end
|
141
163
|
|
164
|
+
def longest_prefix_and_value(string)
|
165
|
+
return [nil, nil] unless @first_character_present[string[0]]
|
166
|
+
byte_pointer = prefix_response = get_response = nil
|
167
|
+
|
168
|
+
::FFI::MemoryPointer.new(:int) do |byte_length|
|
169
|
+
::FFI::MemoryPointer.new(:int) do |prefix_length|
|
170
|
+
byte_pointer = ::FFI::RadixTree.longest_prefix_and_value(@ptr, string, byte_length, prefix_length)
|
171
|
+
bytesize = byte_length.read_int
|
172
|
+
|
173
|
+
if bytesize && bytesize > 0
|
174
|
+
prefix_size = prefix_length.read_int
|
175
|
+
get_response = byte_pointer.get_bytes(0, bytesize)
|
176
|
+
prefix_response = get_response[0..(prefix_size - 1)]
|
177
|
+
get_response = ::MessagePack.unpack(get_response[prefix_size..-1])
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
[prefix_response, get_response]
|
183
|
+
ensure
|
184
|
+
::FFI::RadixTree.match_free(byte_pointer) if byte_pointer
|
185
|
+
end
|
186
|
+
|
142
187
|
def longest_prefix_value(string)
|
143
188
|
return nil unless @first_character_present[string[0]]
|
144
189
|
byte_pointer = get_response = nil
|
@@ -153,6 +198,61 @@ module FFI
|
|
153
198
|
ensure
|
154
199
|
::FFI::RadixTree.match_free(byte_pointer) if byte_pointer
|
155
200
|
end
|
201
|
+
|
202
|
+
def greedy_match(string)
|
203
|
+
return [] unless @first_character_present[string[0]]
|
204
|
+
array_pointer = nil
|
205
|
+
match_sizes_pointer = nil
|
206
|
+
array_size = 0
|
207
|
+
get_response = []
|
208
|
+
|
209
|
+
::FFI::MemoryPointer.new(:pointer) do |match_array|
|
210
|
+
::FFI::MemoryPointer.new(:pointer) do |match_sizes_array|
|
211
|
+
array_size = ::FFI::RadixTree.greedy_match(@ptr, string, match_array, match_sizes_array)
|
212
|
+
if array_size > 0
|
213
|
+
match_sizes_pointer = match_sizes_array.read_pointer
|
214
|
+
match_sizes = match_sizes_pointer.get_array_of_int(0, array_size)
|
215
|
+
array_pointer = match_array.read_pointer
|
216
|
+
char_arrays = array_pointer.get_array_of_pointer(0, array_size)
|
217
|
+
char_arrays.each_with_index do |ptr, index|
|
218
|
+
get_response << ::MessagePack.unpack(ptr.get_bytes(0, match_sizes[index]))
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
get_response
|
225
|
+
ensure
|
226
|
+
::FFI::RadixTree.multi_match_free(array_pointer, array_size) if array_pointer
|
227
|
+
::FFI::RadixTree.match_sizes_free(match_sizes_pointer) if match_sizes_pointer
|
228
|
+
end
|
229
|
+
|
230
|
+
def greedy_substring_match(string)
|
231
|
+
array_pointer = nil
|
232
|
+
match_sizes_pointer = nil
|
233
|
+
array_size = 0
|
234
|
+
get_response = []
|
235
|
+
|
236
|
+
::FFI::MemoryPointer.new(:pointer) do |match_array|
|
237
|
+
::FFI::MemoryPointer.new(:pointer) do |match_sizes_array|
|
238
|
+
array_size = ::FFI::RadixTree.greedy_substring_match(@ptr, string, match_array, match_sizes_array)
|
239
|
+
if array_size > 0
|
240
|
+
match_sizes_pointer = match_sizes_array.read_pointer
|
241
|
+
match_sizes = match_sizes_pointer.get_array_of_int(0, array_size)
|
242
|
+
array_pointer = match_array.read_pointer
|
243
|
+
char_arrays = array_pointer.get_array_of_pointer(0, array_size)
|
244
|
+
char_arrays.each_with_index do |ptr, index|
|
245
|
+
get_response << ::MessagePack.unpack(ptr.get_bytes(0, match_sizes[index]))
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
get_response
|
252
|
+
ensure
|
253
|
+
::FFI::RadixTree.multi_match_free(array_pointer, array_size) if array_pointer
|
254
|
+
::FFI::RadixTree.match_sizes_free(match_sizes_pointer) if match_sizes_pointer
|
255
|
+
end
|
156
256
|
end
|
157
257
|
end
|
158
258
|
end
|
@@ -32,9 +32,19 @@ bool has_key(radix_tree<std::string, std::vector<char>>* map_pointer, const char
|
|
32
32
|
}
|
33
33
|
|
34
34
|
void match_free(const char* match) {
|
35
|
+
delete[] match;
|
36
|
+
}
|
37
|
+
|
38
|
+
void match_sizes_free(const int* match_sizes) {
|
39
|
+
delete[] match_sizes;
|
40
|
+
}
|
41
|
+
|
42
|
+
void multi_match_free(const char** match, int length) {
|
35
43
|
if (match != NULL) {
|
44
|
+
for (int i=0; i<length; ++i) {
|
45
|
+
delete[] match[i];
|
46
|
+
}
|
36
47
|
delete[] match;
|
37
|
-
match = NULL;
|
38
48
|
}
|
39
49
|
}
|
40
50
|
|
@@ -44,7 +54,6 @@ const char* longest_prefix(radix_tree<std::string, std::vector<char>>* map_point
|
|
44
54
|
|
45
55
|
if (iter != map_pointer->end()) {
|
46
56
|
char *val = new char[iter->first.size() + 1]{0};
|
47
|
-
val[iter->first.size()] = '\0';
|
48
57
|
memcpy(val, iter->first.c_str(), iter->first.size());
|
49
58
|
|
50
59
|
return val;
|
@@ -53,6 +62,33 @@ const char* longest_prefix(radix_tree<std::string, std::vector<char>>* map_point
|
|
53
62
|
return NULL;
|
54
63
|
}
|
55
64
|
|
65
|
+
const char* longest_prefix_and_value(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, int* read_size, int* prefix_size) {
|
66
|
+
std::string string_key(key);
|
67
|
+
auto iter = map_pointer->longest_match(string_key);
|
68
|
+
|
69
|
+
if (iter != map_pointer->end()) {
|
70
|
+
long counter = 0;
|
71
|
+
int size_of_response = iter->second.size() + iter->first.size();
|
72
|
+
char *return_val = new char[size_of_response]{0};
|
73
|
+
|
74
|
+
strncpy(return_val, iter->first.c_str(), iter->first.size());
|
75
|
+
counter = iter->first.size();
|
76
|
+
|
77
|
+
for( auto& val3 : iter->second) {
|
78
|
+
return_val[counter] = val3;
|
79
|
+
counter++;
|
80
|
+
}
|
81
|
+
|
82
|
+
*prefix_size = iter->first.size();
|
83
|
+
*read_size = size_of_response;
|
84
|
+
return return_val;
|
85
|
+
}
|
86
|
+
|
87
|
+
*prefix_size = 0;
|
88
|
+
*read_size = 0;
|
89
|
+
return NULL;
|
90
|
+
}
|
91
|
+
|
56
92
|
const char* longest_prefix_value(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, int* read_size) {
|
57
93
|
std::string string_key(key);
|
58
94
|
auto iter = map_pointer->longest_match(string_key);
|
@@ -73,7 +109,7 @@ const char* longest_prefix_value(radix_tree<std::string, std::vector<char>>* map
|
|
73
109
|
return NULL;
|
74
110
|
}
|
75
111
|
|
76
|
-
|
112
|
+
char* fetch(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, int* read_size) {
|
77
113
|
auto iter = map_pointer->find(std::string(key));
|
78
114
|
long counter = 0;
|
79
115
|
|
@@ -92,10 +128,64 @@ const char* fetch(radix_tree<std::string, std::vector<char>>* map_pointer, const
|
|
92
128
|
return NULL;
|
93
129
|
}
|
94
130
|
|
131
|
+
int greedy_match(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, const char*** matches, int** match_sizes) {
|
132
|
+
std::string string_key(key);
|
133
|
+
typedef radix_tree<std::string, std::vector<char>>::iterator iterator;
|
134
|
+
std::vector<iterator> vec;
|
135
|
+
map_pointer->greedy_match(string_key, vec);
|
136
|
+
long counter = 0;
|
137
|
+
|
138
|
+
if (vec.size() > 0) {
|
139
|
+
*matches = new const char* [vec.size()]{nullptr};
|
140
|
+
*match_sizes = new int [vec.size()]{0};
|
141
|
+
for (auto& iter : vec) {
|
142
|
+
auto ret_str = new char[iter->second.size()];
|
143
|
+
long char_index = 0;
|
144
|
+
for (auto& val : iter->second) {
|
145
|
+
ret_str[char_index] = val;
|
146
|
+
++char_index;
|
147
|
+
}
|
148
|
+
(*matches)[counter] = ret_str;
|
149
|
+
(*match_sizes)[counter] = iter->second.size();
|
150
|
+
++counter;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
return counter;
|
154
|
+
}
|
155
|
+
|
156
|
+
int greedy_substring_match(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, const char*** matches, int** match_sizes) {
|
157
|
+
std::string string_key(key);
|
158
|
+
typedef radix_tree<std::string, std::vector<char>>::iterator iterator;
|
159
|
+
std::vector<iterator> vec;
|
160
|
+
map_pointer->greedy_substring_match(string_key, vec);
|
161
|
+
long counter = 0;
|
162
|
+
|
163
|
+
if (vec.size() > 0) {
|
164
|
+
*matches = new const char* [vec.size()]{nullptr};
|
165
|
+
*match_sizes = new int [vec.size()]{0};
|
166
|
+
for (auto& iter : vec) {
|
167
|
+
auto ret_str = new char[iter->second.size()];
|
168
|
+
long char_index = 0;
|
169
|
+
for (auto& val : iter->second) {
|
170
|
+
ret_str[char_index] = val;
|
171
|
+
++char_index;
|
172
|
+
}
|
173
|
+
(*matches)[counter] = ret_str;
|
174
|
+
(*match_sizes)[counter] = iter->second.size();
|
175
|
+
++counter;
|
176
|
+
}
|
177
|
+
}
|
178
|
+
return counter;
|
179
|
+
}
|
180
|
+
|
95
181
|
void insert(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, char* value, size_t size) {
|
96
182
|
map_pointer->insert({std::string(key), std::vector<char>(value, value + size)});
|
97
183
|
}
|
98
184
|
|
185
|
+
bool update(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, char* value, size_t size) {
|
186
|
+
return map_pointer->update({std::string(key), std::vector<char>(value, value + size)});
|
187
|
+
}
|
188
|
+
|
99
189
|
void destroy(radix_tree<std::string, std::vector<char>>* map_pointer) {
|
100
190
|
delete map_pointer;
|
101
191
|
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.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Dewitt
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -38,20 +38,34 @@ 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
|
44
58
|
requirements:
|
45
|
-
- - "
|
59
|
+
- - ">="
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '0'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- - "
|
66
|
+
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rake
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -138,7 +152,7 @@ licenses:
|
|
138
152
|
- MIT
|
139
153
|
metadata:
|
140
154
|
allowed_push_host: https://rubygems.org
|
141
|
-
post_install_message:
|
155
|
+
post_install_message:
|
142
156
|
rdoc_options: []
|
143
157
|
require_paths:
|
144
158
|
- lib
|
@@ -153,9 +167,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
167
|
- !ruby/object:Gem::Version
|
154
168
|
version: '0'
|
155
169
|
requirements: []
|
156
|
-
|
157
|
-
|
158
|
-
signing_key:
|
170
|
+
rubygems_version: 3.0.3
|
171
|
+
signing_key:
|
159
172
|
specification_version: 4
|
160
173
|
summary: radix tree implementation in c++ with FFI bindings
|
161
174
|
test_files: []
|