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 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: []