r2ree 0.0.1

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: 92e4940115abf3167b0f3789add6c1e2409bf16a
4
+ data.tar.gz: e75b0fd689d3f08caa23326a4d4c4976ba21a749
5
+ SHA512:
6
+ metadata.gz: 6bbadaa7576826f70536a006168216ff981fad5993a1cfc558ee4797afb19b11bdab828a1ee2f9d2ff52645f03dd66ccce3e984b18feefccfc0df4afc70277a8
7
+ data.tar.gz: f087b59bc48b93d189eed595e2e83b7363bb995206dd8f200db1dd7ff52fd7e5434b86b60b202444bf50b5d310f8a0e80647b560b5a18039a67c45085ff8cabe
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.5
6
+ before_script: bundle exec rake build
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in r2ree.gemspec
4
+
5
+ gem 'bundler', '~> 1.12'
6
+ gem 'rake', '~> 10.0'
7
+ gem 'test-unit'
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,13 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.test_files = Dir['test/**/test_*.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ task :build do |t|
10
+ sh 'cd ext/r2ree && ruby extconf.rb && make'
11
+ end
12
+
13
+ task default: :test
@@ -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')
@@ -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,5 @@
1
+ # r2ree
2
+
3
+ Radix tree implementation for use in HTTP routing.
4
+
5
+ r2ree is written in C++ and licensed under the MIT License, it can be used as a library.
@@ -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: []