ffi-radix_tree 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b552a559de9350d73c2c847b2aa4f0157fed12a8
4
+ data.tar.gz: f25f0dc4ad08766c3f805b8f75cba8c6fbda5a3d
5
+ SHA512:
6
+ metadata.gz: 63fc44026e11a67bdef49b7c7fd2c1fe3c5c1a34ef7ae3ae26823c52d77a0b278b43e506114dda67baf10f7cfb160e855c5bcef05005102e320ff3ae2395e9eb
7
+ data.tar.gz: 8facbab3ba214d8c382f2c176d6284c9743de7d34e1368e558ed550fa8e4d2b06c988bd29cef5cf01e3286287b9398628a913707284d8f6652960f647ebb8ebf
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.ruby-*
2
+ *.so
3
+ CMakeCache.txt
4
+ /vendor/radixtree/CMakeFiles
5
+ /vendor/radixtree/CMakeFiles/**/*
6
+ /vendor/radixtree/Makefile
7
+ cmake_install.cmake
8
+ /.bundle/
9
+ /.yardoc
10
+ /Gemfile.lock
11
+ /_yardoc/
12
+ /coverage/
13
+ /doc/
14
+ /pkg/
15
+ /spec/reports/
16
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.15.4
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in ffi-radix_tree.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Brandon Dewitt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Ffi::RadixTree
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ffi/radix_tree`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'ffi-radix_tree'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install ffi-radix_tree
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/ffi-radix_tree.
36
+
37
+ ## License
38
+
39
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ import "ext/ffi/radixtree/Rakefile"
4
+
5
+ namespace :radixtree do
6
+ desc "build radixtree"
7
+ task :compile do
8
+ Rake::Task[:compile_radixtree].invoke
9
+ end
10
+ end
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << "test"
14
+ t.libs << "lib"
15
+ t.test_files = FileList["test/**/*_test.rb"]
16
+ end
17
+ Rake::Task[:test].prerequisites << "radixtree:compile"
18
+
19
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ffi/radix_tree"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,62 @@
1
+ require "rubygems"
2
+ require "fileutils"
3
+ require "ffi"
4
+
5
+ # Copied fom mkmf
6
+ def find_executable(bin, path = nil)
7
+ executable_file = proc do |name|
8
+ begin
9
+ stat = File.stat(name)
10
+ rescue SystemCallError
11
+ else
12
+ next name if stat.file? and stat.executable?
13
+ end
14
+ end
15
+
16
+ if File.expand_path(bin) == bin
17
+ return bin if executable_file.call(bin)
18
+ return nil
19
+ end
20
+ if path ||= ENV['PATH']
21
+ path = path.split(File::PATH_SEPARATOR)
22
+ else
23
+ path = %w[/usr/local/bin /usr/ucb /usr/bin /bin]
24
+ end
25
+ file = nil
26
+ path.each do |dir|
27
+ return file if executable_file.call(file = File.join(dir, bin))
28
+ end
29
+ nil
30
+ end
31
+
32
+ def sys(cmd)
33
+ puts " -- #{cmd}"
34
+ unless ret = system(cmd)
35
+ raise "ERROR: '#{cmd}' failed"
36
+ end
37
+ ret
38
+ end
39
+
40
+ desc "Build the radixtree shared lib"
41
+ task :compile_radixtree do
42
+ # Do not attempt to install if we want to use the system radixtree lib
43
+ next if ENV.key?("RADIX_TREE_USE_SYSTEM_LIB")
44
+
45
+ if !find_executable("cmake")
46
+ abort "ERROR: CMake is required to build ffi-radix_tree"
47
+ end
48
+
49
+ CWD = ::File.expand_path(::File.dirname(__FILE__))
50
+ RADIXTREE_DIR = ::File.join(CWD, "..", "..", "..", "vendor", "radixtree")
51
+
52
+ ::Dir.chdir(RADIXTREE_DIR) do
53
+ sys("cmake CMakeLists.txt")
54
+ sys("make")
55
+ end
56
+
57
+ unless ::File.exist?(::File.join(RADIXTREE_DIR, "libradixtree.#{::FFI::Platform::LIBSUFFIX}"))
58
+ abort "ERROR: Failed to build radixtree"
59
+ end
60
+ end
61
+
62
+ task :default => :compile_radixtree
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "ffi/radix_tree/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ffi-radix_tree"
8
+ spec.version = FFI::RadixTree::VERSION
9
+ spec.authors = ["Brandon Dewitt"]
10
+ spec.email = ["brandonsdewitt@gmail.com"]
11
+
12
+ spec.summary = %q{ radix tree implementation in c++ with FFI bindings }
13
+ spec.description = %q{ radix tree implementation in c++ with FFI bindings }
14
+ spec.homepage = "https://www.github.com/abrandoned/ffi-radix_tree"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.extensions = "ext/ffi/radixtree/Rakefile"
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency "msgpack"
35
+ spec.add_dependency "ffi"
36
+
37
+ spec.add_development_dependency "bundler", "~> 1.15"
38
+ spec.add_development_dependency "rake", "~> 10.0"
39
+ spec.add_development_dependency "minitest", "~> 5.0"
40
+ end
@@ -0,0 +1,129 @@
1
+ require "ffi"
2
+ require "msgpack"
3
+ require "ffi/radix_tree/version"
4
+
5
+ module FFI
6
+ module RadixTree
7
+ extend FFI::Library
8
+ ffi_lib_flags :now, :global
9
+
10
+ ##
11
+ # ffi-rzmq-core for reference
12
+ #
13
+ # https://github.com/chuckremes/ffi-rzmq-core/blob/master/lib/ffi-rzmq-core/libzmq.rb
14
+ #
15
+ begin
16
+ # bias the library discovery to a path inside the gem first, then
17
+ # to the usual system paths
18
+ gem_base = ::File.join(::File.dirname(__FILE__), '..', '..', '..')
19
+ inside_gem = ::File.join(gem_base, 'ext')
20
+ local_path = ::FFI::Platform::IS_WINDOWS ? ENV['PATH'].split(';') : ENV['PATH'].split(':')
21
+ env_path = [ ENV['RADIX_TREE_LIB_PATH'] ].compact
22
+ rbconfig_path = ::RbConfig::CONFIG["libdir"]
23
+ homebrew_path = nil
24
+
25
+ # RUBYOPT set by RVM breaks 'brew' so we need to unset it.
26
+ rubyopt = ENV.delete('RUBYOPT')
27
+
28
+ begin
29
+ stdout, stderr, status = ::Open3.capture3("brew", "--prefix")
30
+ homebrew_path = if status.success?
31
+ "#{stdout.chomp}/lib"
32
+ else
33
+ '/usr/local/homebrew/lib'
34
+ end
35
+ rescue
36
+ # Homebrew doesn't exist
37
+ end
38
+
39
+ # Restore RUBYOPT after executing 'brew' above.
40
+ ENV['RUBYOPT'] = rubyopt
41
+
42
+ # Search for libradixtree in the following order...
43
+ radixtree_lib_paths =
44
+ if ENV.key?("RADIX_TREE_USE_SYSTEM_LIB")
45
+ [inside_gem] + env_path + local_path + [rbconfig_path] + [
46
+ '/usr/local/lib', '/opt/local/lib', homebrew_path, '/usr/lib64'
47
+ ]
48
+ else
49
+ [::File.join(gem_base, "vendor/radixtree/build/src")]
50
+ end
51
+
52
+ RADIX_TREE_LIB_PATHS = radixtree_lib_paths.
53
+ compact.map{|path| "#{path}/libradixtree.#{::FFI::Platform::LIBSUFFIX}"}
54
+
55
+ ffi_lib(RADIX_TREE_LIB_PATHS + %w{libradixtree})
56
+ rescue LoadError => error
57
+ if RADIX_TREE_LIB_PATHS.any? {|path| ::File.file?(::File.join(path)) }
58
+ warn "Unable to load this gem. The libradixtree library exists, but cannot be loaded."
59
+ warn "Set RADIX_TREE_LIB_PATH if custom load path is desired"
60
+ warn "If this is Windows:"
61
+ warn "- Check that you have MSVC runtime installed or statically linked"
62
+ warn "- Check that your DLL is compiled for #{FFI::Platform::ADDRESS_SIZE} bit"
63
+ else
64
+ warn "Unable to load this gem. The libradixtree library (or DLL) could not be found."
65
+ warn "Set RADIX_TREE_LIB_PATH if custom load path is desired"
66
+ warn "If this is a Windows platform, make sure libradixtree.dll is on the PATH."
67
+ warn "If the DLL was built with mingw, make sure the other two dependent DLLs,"
68
+ warn "libgcc_s_sjlj-1.dll and libstdc++6.dll, are also on the PATH."
69
+ warn "For non-Windows platforms, make sure libradixtree is located in this search path:"
70
+ warn RADIX_TREE_LIB_PATHS.inspect
71
+ end
72
+ raise error
73
+ end
74
+
75
+ attach_function :create, [], :pointer
76
+ attach_function :destroy, [:pointer], :void
77
+ attach_function :erase, [:string], :void
78
+ attach_function :fetch, [:pointer, :string, :pointer], :pointer
79
+ attach_function :insert, [:pointer, :string, :pointer, :size_t], :void
80
+ attach_function :longest_prefix, [:pointer, :string], :string
81
+ attach_function :longest_prefix_value, [:pointer, :string, :pointer], :pointer
82
+ attach_function :match_free, [:pointer], :void
83
+ attach_function :has_key, [:pointer, :string], :bool
84
+ end
85
+
86
+ class Tree
87
+ DESTROY_METHOD = ::RadixTree.method(:destroy)
88
+ FREE_METHOD = ::RadixTree.method(:match_free)
89
+
90
+ def initialize
91
+ @ptr = ::FFI::AutoPointer.new(::FFI::RadixTree.create, DESTROY_METHOD)
92
+ end
93
+
94
+ def has_key?(key)
95
+ ::FFI::RadixTree.has_key(@ptr, key)
96
+ end
97
+
98
+ def push(key, value)
99
+ storage_data = ::MessagePack.pack(value)
100
+ bytesize = storage_data.bytesize
101
+ memory_buffer = ::FFI::MemoryPointer.new(:char, bytesize, true)
102
+ memory_buffer.put_bytes(0, storage_data)
103
+ ::FFI::RadixTree.insert(@ptr, key, memory_buffer, bytesize)
104
+ end
105
+
106
+ def get(key)
107
+ byte_length = ::FFI::MemoryPointer.new(:int)
108
+ byte_pointer = ::FFI::AutoPointer.new(::FFI::RadixTree.fetch(@ptr, key, byte_length), FREE_METHOD)
109
+ bytesize = byte_length.read_int
110
+ return nil if bytesize <= 0
111
+ ::MessagePack.unpack(byte_pointer.get_bytes(0, bytesize))
112
+ end
113
+
114
+ def longest_prefix(string)
115
+ value, p_out = ::FFI::RadixTree.longest_prefix(@ptr, string)
116
+ p_out = ::FFI::AutoPointer.new(p_out, FREE_METHOD) unless p_out.nil?
117
+ value.force_encoding("UTF-8") unless value.nil?
118
+ value
119
+ end
120
+
121
+ def longest_prefix_value(string)
122
+ byte_length = ::FFI::MemoryPointer.new(:int)
123
+ byte_pointer = ::FFI::AutoPointer.new(::FFI::RadixTree.longest_prefix_value(@ptr, string, byte_length), FREE_METHOD)
124
+ bytesize = byte_length.read_int
125
+ return nil if bytesize <= 0
126
+ ::MessagePack.unpack(byte_pointer.get_bytes(0, bytesize))
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,5 @@
1
+ module FFI
2
+ module RadixTree
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ cmake_minimum_required(VERSION 2.8)
2
+
3
+ project (radixtree)
4
+ set(CMAKE_CXX_FLAGS "-Wall -Wextra")
5
+ set(CMAKE_CXX_FLAGS_DEBUG "-g")
6
+ set(CMAKE_CXX_FLAGS_RELEASE "-O3")
7
+ set(CMAKE_BUILD_TYPE Release)
8
+
9
+ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
10
+ add_library(radixtree SHARED ffi_radix_tree.cpp)
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2010, Yuuki Takano <ytakanoster@gmail.com>, All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification,
4
+ are permitted provided that the following conditions are met:
5
+
6
+ * Redistributions of source code must retain the above copyright notice,
7
+ this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright notice,
9
+ this list of conditions and the following disclaimer in the documentation
10
+ and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
16
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,101 @@
1
+ #include <algorithm>
2
+ #include <iostream>
3
+ #include <cstring>
4
+ #include <string>
5
+ #include <vector>
6
+
7
+ #include "radix_tree.hpp"
8
+
9
+ #ifdef __cplusplus
10
+ #define EXTERN_C extern "C"
11
+ #define EXTERN_C_BEGIN extern "C" {
12
+ #define EXTERN_C_END }
13
+ #else
14
+ #define EXTERN_C /* Nothing */
15
+ #define EXTERN_C_BEGIN /* Nothing */
16
+ #define EXTERN_C_END /* Nothing */
17
+ #endif
18
+
19
+ EXTERN_C_BEGIN
20
+ radix_tree<std::string, std::vector<char>>* create() {
21
+ radix_tree<std::string, std::vector<char>>* map_pointer = new radix_tree<std::string, std::vector<char>>();
22
+
23
+ return map_pointer;
24
+ }
25
+
26
+ void erase(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key) {
27
+ map_pointer->erase(std::string(key));
28
+ }
29
+
30
+ bool has_key(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key) {
31
+ return map_pointer->find(std::string(key)) != map_pointer->end();
32
+ }
33
+
34
+ void match_free(const char* match) {
35
+ if (match != NULL) {
36
+ delete[] match;
37
+ }
38
+ }
39
+
40
+ const char* longest_prefix(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key) {
41
+ std::string string_key(key);
42
+ auto iter = map_pointer->longest_match(string_key);
43
+
44
+ if (iter != map_pointer->end()) {
45
+ char *val = new char[iter->first.size() + 1]{0};
46
+ val[iter->first.size()] = '\0';
47
+ memcpy(val, iter->first.c_str(), iter->first.size());
48
+
49
+ return val;
50
+ }
51
+
52
+ return NULL;
53
+ }
54
+
55
+ const char* longest_prefix_value(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, int* read_size) {
56
+ std::string string_key(key);
57
+ auto iter = map_pointer->longest_match(string_key);
58
+ long counter = 0;
59
+
60
+ if (iter != map_pointer->end()) {
61
+ char *return_val = new char[iter->second.size()]{0};
62
+ for( auto& val : iter->second ) {
63
+ return_val[counter] = val;
64
+ counter++;
65
+ }
66
+
67
+ *read_size = iter->second.size();
68
+ return return_val;
69
+ }
70
+
71
+ *read_size = 0;
72
+ return NULL;
73
+ }
74
+
75
+ const char* fetch(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, int* read_size) {
76
+ auto iter = map_pointer->find(std::string(key));
77
+ long counter = 0;
78
+
79
+ if (iter != map_pointer->end()) {
80
+ char *return_val = new char[iter->second.size()]{0};
81
+ for( auto& val : iter->second ) {
82
+ return_val[counter] = val;
83
+ counter++;
84
+ }
85
+
86
+ *read_size = iter->second.size();
87
+ return return_val;
88
+ }
89
+
90
+ *read_size = 0;
91
+ return NULL;
92
+ }
93
+
94
+ void insert(radix_tree<std::string, std::vector<char>>* map_pointer, const char* key, char* value, size_t size) {
95
+ map_pointer->insert({std::string(key), std::vector<char>(value, value + size)});
96
+ }
97
+
98
+ void destroy(radix_tree<std::string, std::vector<char>>* map_pointer) {
99
+ delete map_pointer;
100
+ }
101
+ EXTERN_C_END