ffi-radix_tree 0.1.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0b481cbe2fe92e746154ef5d7f640eaa360b2ad1
4
- data.tar.gz: 2c31d231c98984ea3ac623c673fea32f3d9f49e3
2
+ SHA256:
3
+ metadata.gz: 273613cad863a6795457a41a30cac700b0f1a1a0be3e53359ab6dff4c405cc00
4
+ data.tar.gz: 3533ae343c7fbf6d4a5cee15ad33f69bd7c63f0df3a1e239d684a9605215268d
5
5
  SHA512:
6
- metadata.gz: 1569a95cd03b4dee2136b26175e83a3732572b7aaf30af692169d0831f4748fb103603de25414160b54f0a56b59c5b194e75d8ae819cdf58d3c032a5869a4c16
7
- data.tar.gz: bdc72d457259f6fb473f0690974fefbc10fb36ff121cfc6dd75c2ae8de517caf86e27ff7107af7d028244053b2ac514c856e22d296ed06794d634815e4096c6d
6
+ metadata.gz: 3b1cfcf6c1121b84d4b0f51f76dff086e76e854ca2de8eb9d4c3d70446566c4524572194f5490d7c8625e60e61701ce02383d168d47462732f4aee423685789d
7
+ data.tar.gz: f1c75e06b8af34d66d463c3b2702854ce711a08ae33eb4e6d69d58d72954e01d2e7777b80bcdd1f95d6bdf40fedafb4f11f7fbea1d39df1af0ee25354f7fc2a2
data/README.md CHANGED
@@ -22,7 +22,41 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
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,13 +7,75 @@ 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
- Rake::TestTask.new(:test) do |t|
13
- t.libs << "test"
73
+ Rake::TestTask.new(:spec) do |t|
74
+ t.libs << "spec"
14
75
  t.libs << "lib"
15
- t.test_files = FileList["test/**/*_test.rb"]
76
+ t.pattern = "spec/**/*_spec.rb"
77
+ t.verbose = true
16
78
  end
17
- Rake::Task[:test].prerequisites << "radixtree:compile"
79
+ Rake::Task[:spec].prerequisites << "radixtree:compile"
18
80
 
19
- task :default => :test
81
+ task :default => :spec
@@ -34,7 +34,10 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency "msgpack"
35
35
  spec.add_dependency "ffi"
36
36
 
37
- spec.add_development_dependency "bundler", "~> 1.15"
37
+ spec.add_development_dependency "benchmark-ips"
38
+ spec.add_development_dependency "bundler"
38
39
  spec.add_development_dependency "rake", "~> 10.0"
40
+ spec.add_development_dependency "mocha"
41
+ spec.add_development_dependency "pry"
39
42
  spec.add_development_dependency "minitest", "~> 5.0"
40
43
  end
@@ -74,75 +74,181 @@ module FFI
74
74
 
75
75
  attach_function :create, [], :pointer
76
76
  attach_function :destroy, [:pointer], :void
77
- attach_function :erase, [:string], :void
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
86
- DESTROY_METHOD = ::FFI::RadixTree.method(:destroy)
87
- FREE_METHOD = ::FFI::RadixTree.method(:match_free)
88
-
89
- def self.destroy!(tree)
90
- tree.destroy! unless tree.nil?
91
- end
92
-
93
91
  def initialize
94
- @ptr = ::FFI::AutoPointer.new(::FFI::RadixTree.create, DESTROY_METHOD)
92
+ @ptr = ::FFI::AutoPointer.new(::FFI::RadixTree.create, ::FFI::RadixTree.method(:destroy))
95
93
  @first_character_present = {}
96
94
  end
97
95
 
98
- def destroy!
99
- ::FFI::RadixTree.destroy(@ptr) unless @ptr.nil?
100
- @ptr = nil
101
- end
102
-
103
96
  def has_key?(key)
104
97
  ::FFI::RadixTree.has_key(@ptr, key)
105
98
  end
106
99
 
107
100
  def push(key, value)
101
+ push_response = nil
102
+ @first_character_present[key[0]] = true
103
+ storage_data = ::MessagePack.pack(value)
104
+ bytesize = storage_data.bytesize
105
+
106
+ ::FFI::MemoryPointer.new(:char, bytesize, true) do |memory_buffer|
107
+ memory_buffer.put_bytes(0, storage_data)
108
+ push_response = ::FFI::RadixTree.insert(@ptr, key, memory_buffer, bytesize)
109
+ end
110
+
111
+ push_response
112
+ end
113
+
114
+ def push_or_update(key, value)
115
+ response = nil
108
116
  @first_character_present[key[0]] = true
109
117
  storage_data = ::MessagePack.pack(value)
110
118
  bytesize = storage_data.bytesize
111
- memory_buffer = ::FFI::MemoryPointer.new(:char, bytesize, true)
112
- memory_buffer.put_bytes(0, storage_data)
113
- ::FFI::RadixTree.insert(@ptr, key, memory_buffer, bytesize)
119
+
120
+ ::FFI::MemoryPointer.new(:char, bytesize, true) do |memory_buffer|
121
+ memory_buffer.put_bytes(0, storage_data)
122
+ response = ::FFI::RadixTree.update(@ptr, key, memory_buffer, bytesize)
123
+ response ||= ::FFI::RadixTree.insert(@ptr, key, memory_buffer, bytesize)
124
+ end
125
+
126
+ response
127
+ end
128
+
129
+ def erase(key)
130
+ ::FFI::RadixTree.erase(@ptr, key)
114
131
  end
115
132
 
116
133
  def get(key)
117
134
  return nil unless @first_character_present[key[0]]
118
- byte_length = ::FFI::MemoryPointer.new(:int)
119
- byte_pointer = ::FFI::AutoPointer.new(::FFI::RadixTree.fetch(@ptr, key, byte_length), FREE_METHOD)
120
- bytesize = byte_length.read_int
121
- return nil if bytesize <= 0
122
- ::MessagePack.unpack(byte_pointer.get_bytes(0, bytesize))
135
+ byte_pointer = get_response = nil
136
+
137
+ ::FFI::MemoryPointer.new(:int) do |byte_length|
138
+ byte_pointer = ::FFI::RadixTree.fetch(@ptr, key, byte_length)
139
+ bytesize = byte_length.read_int
140
+
141
+ if bytesize && bytesize > 0
142
+ bytes = byte_pointer.get_bytes(0, bytesize)
143
+ get_response = ::MessagePack.unpack(bytes)
144
+ end
145
+ end
146
+
147
+ get_response
123
148
  ensure
124
- ::FFI::RadixTree.match_free(byte_pointer) unless byte_pointer.nil?
149
+ ::FFI::RadixTree.match_free(byte_pointer) if byte_pointer
125
150
  end
126
151
 
127
152
  def longest_prefix(string)
128
153
  return nil unless @first_character_present[string[0]]
129
154
  value, p_out = ::FFI::RadixTree.longest_prefix(@ptr, string)
130
- p_out = ::FFI::AutoPointer.new(p_out, FREE_METHOD) unless p_out.nil?
131
- value.force_encoding("UTF-8") unless value.nil?
155
+ value.force_encoding("UTF-8") if value
132
156
  value
133
157
  ensure
134
- ::FFI::RadixTree.match_free(p_out) unless p_out.nil?
158
+ ::FFI::RadixTree.match_free(p_out) if p_out
159
+ end
160
+
161
+ def longest_prefix_and_value(string)
162
+ return [nil, nil] unless @first_character_present[string[0]]
163
+ byte_pointer = prefix_response = get_response = nil
164
+
165
+ ::FFI::MemoryPointer.new(:int) do |byte_length|
166
+ ::FFI::MemoryPointer.new(:int) do |prefix_length|
167
+ byte_pointer = ::FFI::RadixTree.longest_prefix_and_value(@ptr, string, byte_length, prefix_length)
168
+ bytesize = byte_length.read_int
169
+
170
+ if bytesize && bytesize > 0
171
+ prefix_size = prefix_length.read_int
172
+ get_response = byte_pointer.get_bytes(0, bytesize)
173
+ prefix_response = get_response[0..(prefix_size - 1)]
174
+ get_response = ::MessagePack.unpack(get_response[prefix_size..-1])
175
+ end
176
+ end
177
+ end
178
+
179
+ [prefix_response, get_response]
180
+ ensure
181
+ ::FFI::RadixTree.match_free(byte_pointer) if byte_pointer
135
182
  end
136
183
 
137
184
  def longest_prefix_value(string)
138
185
  return nil unless @first_character_present[string[0]]
139
- byte_length = ::FFI::MemoryPointer.new(:int)
140
- byte_pointer = ::FFI::AutoPointer.new(::FFI::RadixTree.longest_prefix_value(@ptr, string, byte_length), FREE_METHOD)
141
- bytesize = byte_length.read_int
142
- return nil if bytesize <= 0
143
- ::MessagePack.unpack(byte_pointer.get_bytes(0, bytesize))
186
+ byte_pointer = get_response = nil
187
+
188
+ ::FFI::MemoryPointer.new(:int) do |byte_length|
189
+ byte_pointer = ::FFI::RadixTree.longest_prefix_value(@ptr, string, byte_length)
190
+ bytesize = byte_length.read_int
191
+ get_response = ::MessagePack.unpack(byte_pointer.get_bytes(0, bytesize)) if bytesize && bytesize > 0
192
+ end
193
+
194
+ get_response
195
+ ensure
196
+ ::FFI::RadixTree.match_free(byte_pointer) if byte_pointer
197
+ end
198
+
199
+ def greedy_match(string)
200
+ return [] unless @first_character_present[string[0]]
201
+ array_pointer = nil
202
+ match_sizes_pointer = nil
203
+ array_size = 0
204
+ get_response = []
205
+
206
+ ::FFI::MemoryPointer.new(:pointer) do |match_array|
207
+ ::FFI::MemoryPointer.new(:pointer) do |match_sizes_array|
208
+ array_size = ::FFI::RadixTree.greedy_match(@ptr, string, match_array, match_sizes_array)
209
+ if array_size > 0
210
+ array_sizes_pointer = match_sizes_array.read_pointer
211
+ match_sizes = array_sizes_pointer.get_array_of_int(0, array_size)
212
+ array_pointer = match_array.read_pointer
213
+ char_arrays = array_pointer.get_array_of_pointer(0, array_size)
214
+ char_arrays.each_with_index do |ptr, index|
215
+ get_response << ::MessagePack.unpack(ptr.get_bytes(0, match_sizes[index]))
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ get_response
222
+ ensure
223
+ ::FFI::RadixTree.multi_match_free(array_pointer, array_size) if array_pointer
224
+ ::FFI::RadixTree.match_free(match_sizes_pointer) if match_sizes_pointer
225
+ end
226
+
227
+ def greedy_substring_match(string)
228
+ array_pointer = nil
229
+ match_sizes_pointer = nil
230
+ array_size = 0
231
+ get_response = []
232
+
233
+ ::FFI::MemoryPointer.new(:pointer) do |match_array|
234
+ ::FFI::MemoryPointer.new(:pointer) do |match_sizes_array|
235
+ array_size = ::FFI::RadixTree.greedy_substring_match(@ptr, string, match_array, match_sizes_array)
236
+ if array_size > 0
237
+ array_sizes_pointer = match_sizes_array.read_pointer
238
+ match_sizes = array_sizes_pointer.get_array_of_int(0, array_size)
239
+ array_pointer = match_array.read_pointer
240
+ char_arrays = array_pointer.get_array_of_pointer(0, array_size)
241
+ char_arrays.each_with_index do |ptr, index|
242
+ get_response << ::MessagePack.unpack(ptr.get_bytes(0, match_sizes[index]))
243
+ end
244
+ end
245
+ end
246
+ end
247
+
248
+ get_response
144
249
  ensure
145
- ::FFI::RadixTree.match_free(byte_pointer) unless byte_pointer.nil?
250
+ ::FFI::RadixTree.multi_match_free(array_pointer, array_size) if array_pointer
251
+ ::FFI::RadixTree.match_free(match_sizes_pointer) if match_sizes_pointer
146
252
  end
147
253
  end
148
254
  end
@@ -1,5 +1,5 @@
1
1
  module FFI
2
2
  module RadixTree
3
- VERSION = "0.1.5"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  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
- const char* fetch(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, int* read_size) {
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.1.5
4
+ version: 0.5.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: 2017-10-18 00:00:00.000000000 Z
11
+ date: 2021-06-03 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: '1.15'
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: '1.15'
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,34 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: mocha
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
69
111
  - !ruby/object:Gem::Dependency
70
112
  name: minitest
71
113
  requirement: !ruby/object:Gem::Requirement
@@ -125,8 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
167
  - !ruby/object:Gem::Version
126
168
  version: '0'
127
169
  requirements: []
128
- rubyforge_project:
129
- rubygems_version: 2.5.1
170
+ rubygems_version: 3.0.3
130
171
  signing_key:
131
172
  specification_version: 4
132
173
  summary: radix tree implementation in c++ with FFI bindings