r2ree 0.0.1
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 +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +6 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +13 -0
- data/ext/r2ree/extconf.rb +17 -0
- data/ext/r2ree/r2ree.cc +104 -0
- data/r2ree.gemspec +20 -0
- data/vendor/r2ree/.travis.yml +21 -0
- data/vendor/r2ree/CMakeLists.txt +56 -0
- data/vendor/r2ree/README.md +5 -0
- data/vendor/r2ree/ext/gtest/CMakeLists.txt +31 -0
- data/vendor/r2ree/include/r2ree.hh +59 -0
- data/vendor/r2ree/src/r2ree.cc +193 -0
- data/vendor/r2ree/test/gtest_r2ree.cc +76 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 92e4940115abf3167b0f3789add6c1e2409bf16a
|
4
|
+
data.tar.gz: e75b0fd689d3f08caa23326a4d4c4976ba21a749
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6bbadaa7576826f70536a006168216ff981fad5993a1cfc558ee4797afb19b11bdab828a1ee2f9d2ff52645f03dd66ccce3e984b18feefccfc0df4afc70277a8
|
7
|
+
data.tar.gz: f087b59bc48b93d189eed595e2e83b7363bb995206dd8f200db1dd7ff52fd7e5434b86b60b202444bf50b5d310f8a0e80647b560b5a18039a67c45085ff8cabe
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Kunpei Sakai
|
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
|
+
# R2ree
|
2
|
+
|
3
|
+
[r2ree](https://github.com/namusyaka/r2ree) bindings for Ruby
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'r2ree'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install r2ree
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
tree = R2ree.new(['/a', '/b', '/c'])
|
25
|
+
|
26
|
+
tree.size #=> 3
|
27
|
+
tree.find('/b') #=> 1
|
28
|
+
tree.exist?('/c') #=> true
|
29
|
+
```
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/namusyaka/r2ree-ruby.
|
34
|
+
|
35
|
+
|
36
|
+
## License
|
37
|
+
|
38
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
39
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
CWD = File.expand_path(__dir__)
|
4
|
+
R2REE_DIR = File.join(CWD, '..', '..', 'vendor', 'r2ree')
|
5
|
+
|
6
|
+
Dir.chdir(R2REE_DIR) do
|
7
|
+
FileUtils.cp File.join('include', 'r2ree.hh'), File.join('..', '..', 'ext', 'r2ree', 'libr2ree.hh')
|
8
|
+
FileUtils.cp File.join('src', 'r2ree.cc'), File.join('..', '..', 'ext', 'r2ree', 'libr2ree.cc')
|
9
|
+
end
|
10
|
+
|
11
|
+
$CXXFLAGS +=
|
12
|
+
case cc_command
|
13
|
+
when /clang\+\+/ then " -std=c++11 -Wall -Wextra -I#{R2REE_DIR}/include"
|
14
|
+
when /g++/ then " -std=c++0x -Wall -Wextra -I#{R2REE_DIR}/include"
|
15
|
+
end
|
16
|
+
|
17
|
+
create_makefile('r2ree/r2ree')
|
data/ext/r2ree/r2ree.cc
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
#include "libr2ree.hh"
|
2
|
+
#include "string"
|
3
|
+
#include "iostream"
|
4
|
+
#include "ruby.h"
|
5
|
+
|
6
|
+
#define R2REE_VERSION "0.0.1"
|
7
|
+
|
8
|
+
using std::ignore;
|
9
|
+
using std::tie;
|
10
|
+
|
11
|
+
static VALUE rb_r2ree;
|
12
|
+
|
13
|
+
static void r2ree_free(r2ree::radix_tree *tree);
|
14
|
+
static r2ree::radix_tree *get_r2ree_radix_tree(VALUE klass);
|
15
|
+
static r2ree::parse_result match(VALUE klass, const char *path);
|
16
|
+
static VALUE r2ree_size(VALUE self);
|
17
|
+
static VALUE r2ree_s_new(int argc, VALUE *argv, VALUE self);
|
18
|
+
static VALUE r2ree_find(int argc, VALUE *argv, VALUE self);
|
19
|
+
static VALUE r2ree_exist(int argc, VALUE *argv, VALUE self);
|
20
|
+
static VALUE r2ree_size(VALUE self);
|
21
|
+
|
22
|
+
static void r2ree_free(r2ree::radix_tree *tree) {
|
23
|
+
tree->~radix_tree();
|
24
|
+
ruby_xfree(tree);
|
25
|
+
}
|
26
|
+
|
27
|
+
static r2ree::radix_tree *get_r2ree_radix_tree(VALUE klass) {
|
28
|
+
r2ree::radix_tree *tree;
|
29
|
+
return Data_Get_Struct(klass, r2ree::radix_tree, tree);
|
30
|
+
}
|
31
|
+
|
32
|
+
static r2ree::parse_result match(VALUE klass, const char *path) {
|
33
|
+
r2ree::radix_tree *tree = get_r2ree_radix_tree(klass);
|
34
|
+
return tree->get(path);
|
35
|
+
}
|
36
|
+
|
37
|
+
static VALUE r2ree_s_new(int argc, VALUE *argv, VALUE self) {
|
38
|
+
int i = 0;
|
39
|
+
VALUE paths, path;
|
40
|
+
r2ree::radix_tree *tree = new r2ree::radix_tree();
|
41
|
+
rb_scan_args(argc, argv, "1", &paths);
|
42
|
+
|
43
|
+
if (TYPE(paths) == T_ARRAY) {
|
44
|
+
while (!NIL_P(path = rb_ary_entry(paths, i))) {
|
45
|
+
if (TYPE(path) != T_STRING)
|
46
|
+
rb_raise(rb_eArgError, "wrong argument type, expected String");
|
47
|
+
tree->insert(StringValuePtr(path));
|
48
|
+
++i;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
return Data_Wrap_Struct(self, NULL, r2ree_free, tree);
|
53
|
+
};
|
54
|
+
|
55
|
+
static VALUE r2ree_size(VALUE self) {
|
56
|
+
r2ree::radix_tree *tree = get_r2ree_radix_tree(self);
|
57
|
+
int cid = tree->cid;
|
58
|
+
return INT2NUM(cid >= 0 ? cid + 1 : 0);
|
59
|
+
};
|
60
|
+
|
61
|
+
static VALUE r2ree_exist(int argc, VALUE *argv, VALUE self) {
|
62
|
+
VALUE path;
|
63
|
+
bool existence, leaf;
|
64
|
+
|
65
|
+
rb_scan_args(argc, argv, "1", &path);
|
66
|
+
|
67
|
+
if (TYPE(path) == T_STRING) {
|
68
|
+
tie(existence, ignore, ignore, leaf) = match(self, StringValuePtr(path));
|
69
|
+
return (existence && leaf) ? Qtrue : Qfalse;
|
70
|
+
} else {
|
71
|
+
return Qfalse;
|
72
|
+
}
|
73
|
+
};
|
74
|
+
|
75
|
+
static VALUE r2ree_find(int argc, VALUE *argv, VALUE self) {
|
76
|
+
int cid;
|
77
|
+
VALUE path;
|
78
|
+
bool existence, leaf;
|
79
|
+
|
80
|
+
rb_scan_args(argc, argv, "1", &path);
|
81
|
+
|
82
|
+
if (TYPE(path) == T_STRING) {
|
83
|
+
tie(existence, cid, ignore, leaf) = match(self, StringValuePtr(path));
|
84
|
+
return (existence && leaf) ? INT2NUM(cid) : INT2NUM(-1);
|
85
|
+
} else {
|
86
|
+
rb_raise(rb_eArgError, "wrong argument type, expected String");
|
87
|
+
}
|
88
|
+
};
|
89
|
+
|
90
|
+
extern "C" void Init_r2ree() {
|
91
|
+
VALUE tmp;
|
92
|
+
|
93
|
+
rb_r2ree = rb_define_class("R2ree", rb_cObject);
|
94
|
+
|
95
|
+
tmp = rb_str_new2(R2REE_VERSION);
|
96
|
+
rb_obj_freeze(tmp);
|
97
|
+
rb_const_set(rb_r2ree, rb_intern("VERSION"), tmp);
|
98
|
+
|
99
|
+
rb_define_singleton_method(rb_r2ree, "new", RUBY_METHOD_FUNC(r2ree_s_new), -1);
|
100
|
+
rb_define_method(rb_r2ree, "exist?", RUBY_METHOD_FUNC(r2ree_exist), -1);
|
101
|
+
rb_define_method(rb_r2ree, "find", RUBY_METHOD_FUNC(r2ree_find), -1);
|
102
|
+
rb_define_method(rb_r2ree, "size", RUBY_METHOD_FUNC(r2ree_size), 0);
|
103
|
+
rb_define_alias(rb_r2ree, "length", "size");
|
104
|
+
};
|
data/r2ree.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require File.expand_path('ext/r2ree/r2ree', __dir__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = "r2ree"
|
6
|
+
spec.version = R2ree::VERSION
|
7
|
+
spec.authors = ["namusyaka"]
|
8
|
+
spec.email = ["namusyaka@gmail.com"]
|
9
|
+
|
10
|
+
spec.summary = %q{r2ree radix tree implementation for ruby}
|
11
|
+
spec.description = spec.summary
|
12
|
+
spec.homepage = "https://github.com/namusyaka/r2ree-ruby"
|
13
|
+
spec.license = "MIT"
|
14
|
+
spec.extensions = %w[ext/r2ree/extconf.rb]
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
language: cpp
|
2
|
+
os:
|
3
|
+
- linux
|
4
|
+
- osx
|
5
|
+
cache:
|
6
|
+
- apt
|
7
|
+
compiler:
|
8
|
+
- clang
|
9
|
+
- gcc
|
10
|
+
before_install:
|
11
|
+
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update; fi
|
12
|
+
install:
|
13
|
+
- wget https://github.com/google/googletest/archive/release-1.7.0.zip -O googletest-release-1.7.0.zip
|
14
|
+
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then unzip -q googletest-release-1.7.0.zip && cd googletest-release-1.7.0 && sudo cp -r include/gtest /usr/local/include && g++ src/gtest-all.cc -I. -Iinclude -c && g++ src/gtest_main.cc -I. -Iinclude -c && ar -rv libgtest.a gtest-all.o && ar -rv libgtest_main.a gtest_main.o && sudo mv *.a /usr/local/lib && g++ -m32 src/gtest-all.cc -I. -Iinclude -c && g++ -m32 src/gtest_main.cc -I. -Iinclude -c && ar -rv libgtest.a gtest-all.o && ar -rv libgtest_main.a gtest_main.o && sudo mkdir /usr/local/lib32 && sudo mv *.a /usr/local/lib32 && cd ..; fi
|
15
|
+
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then unzip -q googletest-release-1.7.0.zip && cd googletest-release-1.7.0 && sudo cp -r include/gtest /usr/local/include && clang++ src/gtest-all.cc -I. -Iinclude -c && g++ src/gtest_main.cc -I. -Iinclude -c && ar -rv libgtest.a gtest-all.o && ar -rv libgtest_main.a gtest_main.o && sudo mv *.a /usr/local/lib && cd ..; fi
|
16
|
+
matrix:
|
17
|
+
exclude:
|
18
|
+
- os: osx
|
19
|
+
compiler: gcc
|
20
|
+
script:
|
21
|
+
- mkdir build && cd build && cmake .. && make && ./r2ree_test
|
@@ -0,0 +1,56 @@
|
|
1
|
+
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
2
|
+
set(PROJECT_NAME_STR r2ree)
|
3
|
+
project(${PROJECT_NAME_STR} C CXX)
|
4
|
+
|
5
|
+
find_package(Threads REQUIRED)
|
6
|
+
|
7
|
+
if(CMAKE_COMPILER_IS_GNUCXX)
|
8
|
+
add_definitions(-Wall -ansi -Wno-deprecated -pthread)
|
9
|
+
endif()
|
10
|
+
|
11
|
+
if(MSVC)
|
12
|
+
#vc 2012 fix for vararg templates
|
13
|
+
set(MSVC_COMPILER_DEFS "-D_VARIADIC_MAX=10")
|
14
|
+
endif()
|
15
|
+
#-------------------
|
16
|
+
# set common include folder for module
|
17
|
+
#-------------------
|
18
|
+
set(COMMON_INCLUDES ${PROJECT_SOURCE_DIR}/include)
|
19
|
+
set(EXT_PROJECTS_DIR ${PROJECT_SOURCE_DIR}/ext)
|
20
|
+
|
21
|
+
add_subdirectory(${EXT_PROJECTS_DIR}/gtest)
|
22
|
+
|
23
|
+
set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++ -Iinclude -include ${PROJECT_SOURCE_DIR}/src/r2ree.cc")
|
24
|
+
|
25
|
+
#-------------------
|
26
|
+
# Test
|
27
|
+
#-------------------
|
28
|
+
add_definitions(${MSVC_COMPILER_DEFS})
|
29
|
+
enable_testing()
|
30
|
+
set(PROJECT_TEST_NAME ${PROJECT_NAME_STR}_test)
|
31
|
+
include_directories(${GTEST_INCLUDE_DIRS} ${COMMON_INCLUDES})
|
32
|
+
|
33
|
+
file(GLOB TEST_SRC_FILES ${PROJECT_SOURCE_DIR}/test/*.cc)
|
34
|
+
add_executable(${PROJECT_TEST_NAME} ${TEST_SRC_FILES})
|
35
|
+
add_dependencies(${PROJECT_TEST_NAME} googletest)
|
36
|
+
|
37
|
+
|
38
|
+
if(NOT WIN32 OR MINGW)
|
39
|
+
target_link_libraries(${PROJECT_TEST_NAME}
|
40
|
+
${GTEST_LIBS_DIR}/libgtest.a
|
41
|
+
${GTEST_LIBS_DIR}/libgtest_main.a
|
42
|
+
)
|
43
|
+
else()
|
44
|
+
target_link_libraries(${PROJECT_TEST_NAME}
|
45
|
+
debug ${GTEST_LIBS_DIR}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${CMAKE_FIND_LIBRARY_SUFFIXES}
|
46
|
+
optimized ${GTEST_LIBS_DIR}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest${CMAKE_FIND_LIBRARY_SUFFIXES}
|
47
|
+
)
|
48
|
+
target_link_libraries(${PROJECT_TEST_NAME}
|
49
|
+
debug ${GTEST_LIBS_DIR}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest_main${CMAKE_FIND_LIBRARY_SUFFIXES}
|
50
|
+
optimized ${GTEST_LIBS_DIR}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}gtest_main${CMAKE_FIND_LIBRARY_SUFFIXES}
|
51
|
+
)
|
52
|
+
endif()
|
53
|
+
|
54
|
+
target_link_libraries(${PROJECT_TEST_NAME} ${CMAKE_THREAD_LIBS_INIT})
|
55
|
+
|
56
|
+
add_test(test1 ${PROJECT_TEST_NAME})
|
@@ -0,0 +1,31 @@
|
|
1
|
+
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
2
|
+
project(gtest_builder C CXX)
|
3
|
+
include(ExternalProject)
|
4
|
+
|
5
|
+
set(GTEST_FORCE_SHARED_CRT ON)
|
6
|
+
set(GTEST_DISABLE_PTHREADS OFF)
|
7
|
+
|
8
|
+
if(MINGW)
|
9
|
+
set(GTEST_DISABLE_PTHREADS ON)
|
10
|
+
endif()
|
11
|
+
|
12
|
+
ExternalProject_Add(googletest
|
13
|
+
GIT_REPOSITORY https://github.com/google/googletest.git
|
14
|
+
CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs
|
15
|
+
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs
|
16
|
+
-DCMAKE_CXX_FLAGS=${MSVC_COMPILER_DEFS}
|
17
|
+
-Dgtest_force_shared_crt=${GTEST_FORCE_SHARED_CRT}
|
18
|
+
-Dgtest_disable_pthreads=${GTEST_DISABLE_PTHREADS}
|
19
|
+
-DBUILD_GTEST=ON
|
20
|
+
PREFIX "${CMAKE_CURRENT_BINARY_DIR}"
|
21
|
+
# Disable install step
|
22
|
+
INSTALL_COMMAND ""
|
23
|
+
)
|
24
|
+
|
25
|
+
# Specify include dir
|
26
|
+
ExternalProject_Get_Property(googletest source_dir)
|
27
|
+
set(GTEST_INCLUDE_DIRS ${source_dir}/googletest/include PARENT_SCOPE)
|
28
|
+
|
29
|
+
# Specify MainTest's link libraries
|
30
|
+
ExternalProject_Get_Property(googletest binary_dir)
|
31
|
+
set(GTEST_LIBS_DIR ${binary_dir}/googlemock/gtest PARENT_SCOPE)
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#ifndef r2ree_h
|
2
|
+
#define r2ree_h
|
3
|
+
|
4
|
+
#include <string>
|
5
|
+
#include <vector>
|
6
|
+
#include <tuple>
|
7
|
+
|
8
|
+
namespace r2ree {
|
9
|
+
using std::string;
|
10
|
+
using std::vector;
|
11
|
+
using std::tuple;
|
12
|
+
using std::pair;
|
13
|
+
|
14
|
+
const char asterisk = '*';
|
15
|
+
const char colon = ':';
|
16
|
+
const char slash = '/';
|
17
|
+
|
18
|
+
typedef pair<string, string> parameter;
|
19
|
+
|
20
|
+
struct parameters {
|
21
|
+
parameter *params;
|
22
|
+
int size;
|
23
|
+
};
|
24
|
+
|
25
|
+
typedef tuple<bool, int, parameters, bool> parse_result;
|
26
|
+
|
27
|
+
struct radix_tree_node {
|
28
|
+
string path;
|
29
|
+
string indices;
|
30
|
+
vector<radix_tree_node*> children;
|
31
|
+
int id;
|
32
|
+
int type;
|
33
|
+
int max_parameters;
|
34
|
+
bool leaf;
|
35
|
+
|
36
|
+
radix_tree_node() = default;
|
37
|
+
explicit radix_tree_node(const string &path);
|
38
|
+
~radix_tree_node();
|
39
|
+
|
40
|
+
radix_tree_node* insert_child(char index, radix_tree_node *child);
|
41
|
+
radix_tree_node* get_child(char index);
|
42
|
+
int get_index_position(char target);
|
43
|
+
};
|
44
|
+
|
45
|
+
int find(const string &str, char target, int start);
|
46
|
+
|
47
|
+
class radix_tree {
|
48
|
+
public:
|
49
|
+
int cid;
|
50
|
+
radix_tree();
|
51
|
+
~radix_tree();
|
52
|
+
int insert(const string &path);
|
53
|
+
parse_result get(const string &path);
|
54
|
+
private:
|
55
|
+
radix_tree_node *root;
|
56
|
+
};
|
57
|
+
}; // namespace r2ree
|
58
|
+
|
59
|
+
#endif
|
@@ -0,0 +1,193 @@
|
|
1
|
+
/*
|
2
|
+
* The MIT License (MIT)
|
3
|
+
*
|
4
|
+
* Copyright (c) 2016 namusyaka
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
11
|
+
* furnished to do so, subject to the following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
14
|
+
* all copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
* THE SOFTWARE.
|
23
|
+
*/
|
24
|
+
|
25
|
+
#include "r2ree.hh"
|
26
|
+
|
27
|
+
namespace r2ree {
|
28
|
+
int find(const string &str, char target, int start) {
|
29
|
+
auto i = str.find(target, start);
|
30
|
+
return i == -1 ? str.size() : i;
|
31
|
+
}
|
32
|
+
|
33
|
+
radix_tree_node::radix_tree_node(const string &path) {
|
34
|
+
this->id = -1;
|
35
|
+
this->leaf = false;
|
36
|
+
this->path = path;
|
37
|
+
}
|
38
|
+
|
39
|
+
radix_tree_node::~radix_tree_node() {
|
40
|
+
for (auto &c : this->children)
|
41
|
+
delete c;
|
42
|
+
}
|
43
|
+
|
44
|
+
radix_tree_node* radix_tree_node::insert_child(char index, radix_tree_node *child) {
|
45
|
+
auto i = this->get_index_position(index);
|
46
|
+
this->indices.insert(this->indices.begin() + i, index);
|
47
|
+
this->children.insert(this->children.begin() + i, child);
|
48
|
+
return child;
|
49
|
+
}
|
50
|
+
|
51
|
+
radix_tree_node* radix_tree_node::get_child(char index) {
|
52
|
+
auto i = this->get_index_position(index);
|
53
|
+
return this->indices[i] != index ? nullptr : this->children[i];
|
54
|
+
}
|
55
|
+
|
56
|
+
int radix_tree_node::get_index_position(char target) {
|
57
|
+
int low = 0, high = this->indices.size(), mid;
|
58
|
+
while (low < high) {
|
59
|
+
mid = low + ((high - low) >> 1);
|
60
|
+
if (this->indices[mid] < target)
|
61
|
+
low = mid + 1;
|
62
|
+
else
|
63
|
+
high = mid;
|
64
|
+
}
|
65
|
+
return low;
|
66
|
+
}
|
67
|
+
|
68
|
+
radix_tree::radix_tree() {
|
69
|
+
this->cid = -1;
|
70
|
+
this->root = new radix_tree_node();
|
71
|
+
}
|
72
|
+
|
73
|
+
radix_tree::~radix_tree() {
|
74
|
+
delete this->root;
|
75
|
+
}
|
76
|
+
|
77
|
+
int radix_tree::insert(const string &path) {
|
78
|
+
auto root = this->root;
|
79
|
+
int i = 0, n = path.size(), param_count = 0, code = 0, cid = ++this->cid;
|
80
|
+
|
81
|
+
while (i < n) {
|
82
|
+
if (!root->indices.empty() && (root->indices[0] == asterisk ||
|
83
|
+
path[i] == asterisk ||
|
84
|
+
(path[i] != colon && root->indices[0] == colon) ||
|
85
|
+
(path[i] == colon && root->indices[0] != colon) ||
|
86
|
+
(path[i] == colon && root->indices[0] == colon && path.substr(
|
87
|
+
i + 1, find(path, slash, i) - i - 1) != root->children[0]->path))) {
|
88
|
+
code = -1;
|
89
|
+
break;
|
90
|
+
}
|
91
|
+
|
92
|
+
auto child = root->get_child(path[i]);
|
93
|
+
if (!child) {
|
94
|
+
auto p = find(path, colon, i);
|
95
|
+
if (p == n) {
|
96
|
+
p = find(path, asterisk, i);
|
97
|
+
|
98
|
+
if (path[i] == slash) {
|
99
|
+
root = root->insert_child(slash, new radix_tree_node(path.substr(i, 1)));
|
100
|
+
|
101
|
+
if (i == 0 && path[i + 1] == 0)
|
102
|
+
break;
|
103
|
+
|
104
|
+
++i;
|
105
|
+
}
|
106
|
+
|
107
|
+
root = root->insert_child(path[i], new radix_tree_node(path.substr(i, p - i)));
|
108
|
+
|
109
|
+
if (p < n) {
|
110
|
+
root = root->insert_child(asterisk, new radix_tree_node(path.substr(p + 1)));
|
111
|
+
++param_count;
|
112
|
+
}
|
113
|
+
|
114
|
+
break;
|
115
|
+
}
|
116
|
+
|
117
|
+
root = root->insert_child(path[i], new radix_tree_node(path.substr(i, p - i)));
|
118
|
+
i = find(path, slash, p);
|
119
|
+
|
120
|
+
root = root->insert_child(colon, new radix_tree_node(path.substr(p + 1, i - p - 1)));
|
121
|
+
++param_count;
|
122
|
+
|
123
|
+
if (i == n)
|
124
|
+
break;
|
125
|
+
} else {
|
126
|
+
root = child;
|
127
|
+
|
128
|
+
if (path[i] == colon) {
|
129
|
+
++param_count;
|
130
|
+
i += root->path.size() + 1;
|
131
|
+
|
132
|
+
if (i == n)
|
133
|
+
break;
|
134
|
+
} else {
|
135
|
+
auto j = 0UL, m = root->path.size();
|
136
|
+
|
137
|
+
for (; i < n && j < m && path[i] == root->path[j]; ++i, ++j)
|
138
|
+
continue;
|
139
|
+
|
140
|
+
if (j < m) {
|
141
|
+
auto child = new radix_tree_node(root->path.substr(j));
|
142
|
+
|
143
|
+
child->indices = root->indices;
|
144
|
+
child->children = root->children;
|
145
|
+
|
146
|
+
root->path = root->path.substr(0, j);
|
147
|
+
root->indices = child->path[0];
|
148
|
+
root->children = {child};
|
149
|
+
}
|
150
|
+
|
151
|
+
if (i == n)
|
152
|
+
break;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
if (param_count > this->root->max_parameters)
|
158
|
+
this->root->max_parameters = param_count;
|
159
|
+
|
160
|
+
root->id = cid;
|
161
|
+
root->leaf = true;
|
162
|
+
return code;
|
163
|
+
}
|
164
|
+
|
165
|
+
parse_result radix_tree::get(const string &path) {
|
166
|
+
parameters params = parameters{ new parameter[root->max_parameters] };
|
167
|
+
|
168
|
+
auto root = this->root;
|
169
|
+
int i = 0, n = path.size(), p;
|
170
|
+
|
171
|
+
while (i < n) {
|
172
|
+
if (root->indices.empty())
|
173
|
+
return parse_result();
|
174
|
+
|
175
|
+
if (root->indices[0] == colon) {
|
176
|
+
root = root->children[0];
|
177
|
+
p = find(path, slash, i);
|
178
|
+
params.params[params.size++] = parameter{ root->path, path.substr(i, p - i) };
|
179
|
+
i = p;
|
180
|
+
} else if (root->indices[0] == asterisk) {
|
181
|
+
root = root->children[0];
|
182
|
+
params.params[params.size++] = parameter{ root->path, path.substr(i) };
|
183
|
+
break;
|
184
|
+
} else {
|
185
|
+
root = root->get_child(path[i]);
|
186
|
+
if (!root || path.substr(i, root->path.size()) != root->path)
|
187
|
+
return parse_result();
|
188
|
+
i += root->path.size();
|
189
|
+
}
|
190
|
+
}
|
191
|
+
return parse_result{true, root->id, params, root->leaf};
|
192
|
+
}
|
193
|
+
} // namespace r2ree
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#include "gtest/gtest.h"
|
2
|
+
#include "r2ree.hh"
|
3
|
+
|
4
|
+
namespace {
|
5
|
+
class r2ree_test : public ::testing::Test{
|
6
|
+
protected:
|
7
|
+
r2ree::radix_tree tree;
|
8
|
+
};
|
9
|
+
|
10
|
+
TEST_F(r2ree_test, insert_basic) {
|
11
|
+
tree.insert("/user");
|
12
|
+
ASSERT_EQ(0, tree.cid);
|
13
|
+
}
|
14
|
+
|
15
|
+
TEST_F(r2ree_test, insert_with_multiple_routes) {
|
16
|
+
tree.insert("/user");
|
17
|
+
tree.insert("/123");
|
18
|
+
ASSERT_EQ(1, tree.cid);
|
19
|
+
}
|
20
|
+
|
21
|
+
TEST_F(r2ree_test, insert_with_no_route) {
|
22
|
+
ASSERT_EQ(-1, tree.cid);
|
23
|
+
}
|
24
|
+
|
25
|
+
TEST_F(r2ree_test, get_basic) {
|
26
|
+
r2ree::parse_result actual;
|
27
|
+
|
28
|
+
tree.insert("/user");
|
29
|
+
actual = tree.get("/user");
|
30
|
+
ASSERT_EQ(true, std::get<0>(actual));
|
31
|
+
ASSERT_EQ(0, std::get<1>(actual));
|
32
|
+
ASSERT_EQ(0, std::get<2>(actual).size);
|
33
|
+
ASSERT_EQ(true, std::get<3>(actual));
|
34
|
+
}
|
35
|
+
|
36
|
+
TEST_F(r2ree_test, get_with_nonexistence_pattern) {
|
37
|
+
r2ree::parse_result actual;
|
38
|
+
|
39
|
+
tree.insert("/user");
|
40
|
+
actual = tree.get("/users");
|
41
|
+
ASSERT_EQ(false, std::get<0>(actual));
|
42
|
+
ASSERT_EQ(0, std::get<1>(actual));
|
43
|
+
ASSERT_EQ(0, std::get<2>(actual).size);
|
44
|
+
ASSERT_EQ(false, std::get<3>(actual));
|
45
|
+
}
|
46
|
+
|
47
|
+
TEST_F(r2ree_test, get_with_only_root) {
|
48
|
+
r2ree::parse_result actual;
|
49
|
+
|
50
|
+
tree.insert("/");
|
51
|
+
actual = tree.get("/");
|
52
|
+
ASSERT_EQ(true, std::get<0>(actual));
|
53
|
+
ASSERT_EQ(0, std::get<1>(actual));
|
54
|
+
ASSERT_EQ(0, std::get<2>(actual).size);
|
55
|
+
ASSERT_EQ(true, std::get<3>(actual));
|
56
|
+
}
|
57
|
+
|
58
|
+
TEST_F(r2ree_test, get_with_captured_params) {
|
59
|
+
r2ree::parse_result actual;
|
60
|
+
r2ree::parameters params;
|
61
|
+
|
62
|
+
tree.insert("/users/:name/tags/:tag_id");
|
63
|
+
|
64
|
+
actual = tree.get("/users/namusyaka/tags/foo");
|
65
|
+
params = std::get<2>(actual);
|
66
|
+
|
67
|
+
ASSERT_EQ(true, std::get<0>(actual));
|
68
|
+
ASSERT_EQ(0, std::get<1>(actual));
|
69
|
+
ASSERT_EQ(2, params.size);
|
70
|
+
ASSERT_EQ("name", params.params[0].first);
|
71
|
+
ASSERT_EQ("namusyaka", params.params[0].second);
|
72
|
+
ASSERT_EQ("tag_id", params.params[1].first);
|
73
|
+
ASSERT_EQ("foo", params.params[1].second);
|
74
|
+
ASSERT_EQ(true, std::get<3>(actual));
|
75
|
+
}
|
76
|
+
}
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: r2ree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- namusyaka
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: r2ree radix tree implementation for ruby
|
14
|
+
email:
|
15
|
+
- namusyaka@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions:
|
18
|
+
- ext/r2ree/extconf.rb
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".gitignore"
|
22
|
+
- ".travis.yml"
|
23
|
+
- Gemfile
|
24
|
+
- LICENSE.txt
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- ext/r2ree/extconf.rb
|
28
|
+
- ext/r2ree/r2ree.cc
|
29
|
+
- r2ree.gemspec
|
30
|
+
- vendor/r2ree/.travis.yml
|
31
|
+
- vendor/r2ree/CMakeLists.txt
|
32
|
+
- vendor/r2ree/README.md
|
33
|
+
- vendor/r2ree/ext/gtest/CMakeLists.txt
|
34
|
+
- vendor/r2ree/include/r2ree.hh
|
35
|
+
- vendor/r2ree/src/r2ree.cc
|
36
|
+
- vendor/r2ree/test/gtest_r2ree.cc
|
37
|
+
homepage: https://github.com/namusyaka/r2ree-ruby
|
38
|
+
licenses:
|
39
|
+
- MIT
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 2.5.1
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: r2ree radix tree implementation for ruby
|
61
|
+
test_files: []
|