aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoripknHama <ipknhama@gmail.com>2014-04-15 00:31:51 +0900
committeripknHama <ipknhama@gmail.com>2014-04-15 00:31:51 +0900
commit53debf7e2cb494d8ea89205812bf792e8e9354da (patch)
treec0195ecc0673b794087c99908a2314dfc8a0ff31
parent7ec586556e348725bff3919f4787a75d71c520fa (diff)
downloadcrow-53debf7e2cb494d8ea89205812bf792e8e9354da.tar.gz
crow-53debf7e2cb494d8ea89205812bf792e8e9354da.zip
parameter passing done for int
-rw-r--r--common.h55
-rw-r--r--example.cpp2
-rw-r--r--http_request.h2
-rw-r--r--routing.h197
-rw-r--r--test.py4
-rw-r--r--unittest.cpp8
-rw-r--r--utility.h2
7 files changed, 204 insertions, 66 deletions
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..1699b07
--- /dev/null
+++ b/common.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <string>
+
+namespace flask
+{
+ enum class ParamType
+ {
+ INVALID,
+
+ INT,
+ UINT,
+ DOUBLE,
+ STRING,
+ PATH,
+
+ MAX
+ };
+
+ struct routing_params
+ {
+ std::vector<int64_t> int_params;
+ std::vector<uint64_t> uint_params;
+ std::vector<double> double_params;
+ std::vector<std::string> string_params;
+
+ template <typename T>
+ T get(unsigned) const;
+
+ };
+
+ template<>
+ int64_t routing_params::get<int64_t>(unsigned index) const
+ {
+ return int_params.at(index);
+ }
+
+ template<>
+ uint64_t routing_params::get<uint64_t>(unsigned index) const
+ {
+ return uint_params.at(index);
+ }
+
+ template<>
+ double routing_params::get<double>(unsigned index) const
+ {
+ return double_params.at(index);
+ }
+
+ template<>
+ std::string routing_params::get<std::string>(unsigned index) const
+ {
+ return string_params.at(index);
+ }
+}
diff --git a/example.cpp b/example.cpp
index 9ecf0af..9b36da5 100644
--- a/example.cpp
+++ b/example.cpp
@@ -6,7 +6,7 @@ int main()
{
flask::Flask app;
- app.route("/")
+ FLASK_ROUTE(app, "/")
.name("hello")
([]{
return "Hello World!";
diff --git a/http_request.h b/http_request.h
index e5a8da0..c08d51f 100644
--- a/http_request.h
+++ b/http_request.h
@@ -1,5 +1,7 @@
#pragma once
+#include "common.h"
+
namespace flask
{
struct request
diff --git a/routing.h b/routing.h
index 8c3cc99..62f7d80 100644
--- a/routing.h
+++ b/routing.h
@@ -2,12 +2,12 @@
#include <cstdint>
#include <utility>
-#include <string>
#include <tuple>
#include <unordered_map>
#include <memory>
#include <boost/lexical_cast.hpp>
+#include "common.h"
#include "http_response.h"
//TEST
@@ -36,7 +36,8 @@ namespace flask
virtual void validate()
{
}
- virtual response handle(const request&)
+
+ virtual response handle(const request&, const routing_params&)
{
return response(400);
}
@@ -89,7 +90,7 @@ namespace flask
}
}
- response handle(const request&)
+ response handle(const request&, const routing_params&)
{
return handler_();
}
@@ -101,6 +102,33 @@ namespace flask
template <typename ... Args>
class TaggedRule : public BaseRule
{
+ private:
+ template <typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2> struct call
+ {
+ };
+
+ template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
+ struct call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, black_magic::S<Args2...>>
+ {
+ response operator()(F& handler, const routing_params& params)
+ {
+ using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<int64_t, NInt>>;
+ return call<F, NInt+1, NUint, NDouble, NString,
+ black_magic::S<Args1...>, pushed>()(handler, params);
+ }
+ };
+
+ template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1>
+ struct call<F, NInt, NUint, NDouble, NString, black_magic::S<>, black_magic::S<Args1...>>
+ {
+ response operator()(F& handler, const routing_params& params)
+ {
+ return handler(
+ params.get<typename Args1::type>(Args1::pos)...
+ );
+ //return response(500);
+ }
+ };
public:
TaggedRule(std::string rule)
: BaseRule(std::move(rule))
@@ -134,38 +162,41 @@ namespace flask
};
}
- response handle(const request&)
+ response handle(const request&, const routing_params& params)
{
//return handler_();
- return response(500);
+ return call<decltype(handler_), 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(handler_, params);
+ //return response(500);
}
private:
std::function<response(Args...)> handler_;
+
+ template <typename T, int Pos>
+ struct call_pair
+ {
+ using type = T;
+ static const int pos = Pos;
+ };
+
};
- constexpr const size_t INVALID_RULE_INDEX{static_cast<size_t>(-1)};
class Trie
{
public:
struct Node
{
- std::unordered_map<std::string, size_t> children;
- size_t rule_index{INVALID_RULE_INDEX};
- size_t child_for_int{INVALID_RULE_INDEX};
- size_t child_for_uint{INVALID_RULE_INDEX};
- size_t child_for_float{INVALID_RULE_INDEX};
- size_t child_for_str{INVALID_RULE_INDEX};
- size_t child_for_path{INVALID_RULE_INDEX};
+ std::unordered_map<std::string, unsigned> children;
+ unsigned rule_index{};
+ std::array<unsigned, (int)ParamType::MAX> param_childrens{};
bool IsSimpleNode() const
{
return
- rule_index == INVALID_RULE_INDEX &&
- child_for_int == INVALID_RULE_INDEX &&
- child_for_uint == INVALID_RULE_INDEX &&
- child_for_float == INVALID_RULE_INDEX &&
- child_for_str == INVALID_RULE_INDEX &&
- child_for_path == INVALID_RULE_INDEX;
+ !rule_index &&
+ std::all_of(
+ std::begin(param_childrens),
+ std::end(param_childrens),
+ [](unsigned x){ return !x; });
}
};
@@ -183,20 +214,59 @@ private:
}
}
-public:
void optimize()
{
optimizeNode(head());
}
- size_t find(const request& req, const Node* node = nullptr, size_t pos = 0) const
+public:
+ void validate()
+ {
+ if (!head()->IsSimpleNode())
+ throw std::runtime_error("Internal error: Trie header should be simple!");
+ optimize();
+ }
+
+ std::pair<unsigned, routing_params> find(const request& req, const Node* node = nullptr, unsigned pos = 0, routing_params* params = nullptr) const
{
- size_t found = INVALID_RULE_INDEX;
+ routing_params empty;
+ if (params == nullptr)
+ params = &empty;
+
+ unsigned found{};
+ routing_params match_params;
if (node == nullptr)
node = head();
if (pos == req.url.size())
- return node->rule_index;
+ return {node->rule_index, *params};
+
+ auto update_found = [&found, &match_params](std::pair<unsigned, routing_params>& ret)
+ {
+ if (ret.first && (!found || found > ret.first))
+ {
+ found = ret.first;
+ match_params = ret.second;
+ }
+ };
+
+ if (node->param_childrens[(int)ParamType::INT])
+ {
+ char c = req.url[pos];
+ if ((c >= '0' && c <= '9') || c == '+' || c == '-')
+ {
+ char* eptr;
+ errno = 0;
+ long long int value = strtoll(req.url.data()+pos, &eptr, 10);
+ if (errno != ERANGE && eptr != req.url.data()+pos)
+ {
+ params->int_params.push_back(value);
+ auto ret = find(req, &nodes_[node->param_childrens[(int)ParamType::INT]], eptr - req.url.data(), params);
+ update_found(ret);
+ params->int_params.pop_back();
+ }
+ }
+ }
for(auto& kv : node->children)
{
@@ -205,55 +275,53 @@ public:
if (req.url.compare(pos, fragment.size(), fragment) == 0)
{
- size_t ret = find(req, child, pos + fragment.size());
- if (ret != INVALID_RULE_INDEX && (found == INVALID_RULE_INDEX || found > ret))
- {
- found = ret;
- }
+ auto ret = find(req, child, pos + fragment.size(), params);
+ update_found(ret);
}
}
- return found;
+ return {found, std::move(match_params)};
}
- void add(const std::string& url, size_t rule_index)
+ void add(const std::string& url, unsigned rule_index)
{
- size_t idx{0};
+ unsigned idx{0};
- for(size_t i = 0; i < url.size(); i ++)
+ for(unsigned i = 0; i < url.size(); i ++)
{
char c = url[i];
if (c == '<')
{
- if (url.compare(i, 5, "<int>") == 0)
- {
- idx = nodes_[idx].child_for_int = new_node();
- i += 5;
- }
- else if (url.compare(i, 6, "<uint>") == 0)
- {
- idx = nodes_[idx].child_for_uint = new_node();
- i += 6;
- }
- else if (url.compare(i, 7, "<float>") == 0 ||
- url.compare(i, 8, "<double>") == 0)
+ bool found = false;
+
+ static struct ParamTraits
{
- idx = nodes_[idx].child_for_float = new_node();
- i += 7;
- }
- else if (url.compare(i, 5, "<str>") == 0)
+ ParamType type;
+ std::string name;
+ } paramTraits[] =
{
- idx = nodes_[idx].child_for_str = new_node();
- i += 5;
- }
- else if (url.compare(i, 6, "<path>") == 0)
+ { ParamType::INT, "<int>" },
+ { ParamType::UINT, "<uint>" },
+ { ParamType::DOUBLE, "<float>" },
+ { ParamType::DOUBLE, "<double>" },
+ { ParamType::STRING, "<str>" },
+ { ParamType::PATH, "<path>" },
+ };
+
+ for(auto it = begin(paramTraits); it != end(paramTraits); ++it)
{
- idx = nodes_[idx].child_for_path = new_node();
- i += 6;
+ if (url.compare(i, it->name.size(), it->name) == 0)
+ {
+ idx = nodes_[idx].param_childrens[(int)it->type] = new_node();
+ i += it->name.size();
+ found = true;
+ break;
+ }
}
- else
+
+ if (!found)
{
- throw std::runtime_error("Invalid url: " + url + " (" + boost::lexical_cast<std::string>(i) + ")");
+ throw std::runtime_error("Invalid parameter type: " + url + " (" + boost::lexical_cast<std::string>(i) + ")");
}
i --;
}
@@ -267,7 +335,7 @@ public:
idx = nodes_[idx].children[piece];
}
}
- if (nodes_[idx].rule_index != INVALID_RULE_INDEX)
+ if (nodes_[idx].rule_index)
throw std::runtime_error("handler already exists for " + url);
nodes_[idx].rule_index = rule_index;
}
@@ -282,7 +350,7 @@ public:
return &nodes_.front();
}
- size_t new_node()
+ unsigned new_node()
{
nodes_.resize(nodes_.size()+1);
return nodes_.size() - 1;
@@ -294,6 +362,7 @@ public:
class Router
{
public:
+ Router() : rules_(1) {}
template <uint64_t N>
typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
{
@@ -314,24 +383,26 @@ public:
void validate()
{
- trie_.optimize();
+ trie_.validate();
for(auto& rule:rules_)
{
- rule->validate();
+ if (rule)
+ rule->validate();
}
}
response handle(const request& req)
{
- size_t rule_index = trie_.find(req);
+ auto found = trie_.find(req);
+ unsigned rule_index = found.first;
- if (rule_index == INVALID_RULE_INDEX)
+ if (!rule_index)
return response(404);
if (rule_index >= rules_.size())
throw std::runtime_error("Trie internal structure corrupted!");
- return rules_[rule_index]->handle(req);
+ return rules_[rule_index]->handle(req, found.second);
}
private:
std::vector<std::unique_ptr<BaseRule>> rules_;
diff --git a/test.py b/test.py
index f6cc490..c7bc552 100644
--- a/test.py
+++ b/test.py
@@ -2,3 +2,7 @@ import urllib
assert "Hello World!" == urllib.urlopen('http://localhost:8080').read()
assert "About Flask example." == urllib.urlopen('http://localhost:8080/about').read()
assert 404 == urllib.urlopen('http://localhost:8080/list').getcode()
+assert "3 bottles of beer!" == urllib.urlopen('http://localhost:8080/hello/3').read()
+assert "100 bottles of beer!" == urllib.urlopen('http://localhost:8080/hello/100').read()
+assert "" == urllib.urlopen('http://localhost:8080/hello/500').read()
+assert 400 == urllib.urlopen('http://localhost:8080/hello/500').getcode()
diff --git a/unittest.cpp b/unittest.cpp
index 09718c2..eaf4466 100644
--- a/unittest.cpp
+++ b/unittest.cpp
@@ -1,6 +1,10 @@
-#include "routing.h"
-#include <functional>
+#include <iostream>
+using namespace std;
int main()
{
+ //cout << strtol("+123999999999999999999", NULL, 10) <<endl;
+ //cout <<errno <<endl;
+ cout << strtol("+9223372036854775807", NULL, 10) <<endl;
+ cout <<errno <<endl;
}
diff --git a/utility.h b/utility.h
index 2f3d84a..5da8b7e 100644
--- a/utility.h
+++ b/utility.h
@@ -128,6 +128,8 @@ namespace flask
{
template <typename U>
using push = S<U, T...>;
+ template <typename U>
+ using push_back = S<T..., U>;
template <template<typename ... Args> class U>
using rebind = U<T...>;
};