ffi-radix_tree 0.2.0 → 0.6.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 +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: []
|