r2ree 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|