ffi-radix_tree 0.1.5 → 0.5.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 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