refactored speedy and http server
This commit is contained in:
parent
9f943917ce
commit
9f8761032f
Makefiletest_skip.cpp
api
build.shdata_structures/stack
main.cppmemgraphmemgraph.cppmpsc.cpppromise.cppspeedy
http
httpconnection.hpphttpconnection.inlhttpparser.hpphttpparser.inlhttpparsersettings.hpphttpparsersettings.inlhttpserver.hpphttpserver.inlrequest.hppresponse.hppresponse.inl
r3.hpprequest.hppresponse.hppspeedy.hpputils
17
Makefile
Normal file
17
Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
CXX=clang++
|
||||
CFLAGS=-std=c++1y -Wall -O2
|
||||
LDFLAGS=-luv -lhttp_parser speedy/r3/.libs/libr3.a -L/usr/local/lib -lpcre
|
||||
|
||||
INC=-I./
|
||||
SOURCES=$(wildcard *.cpp)
|
||||
EXECUTABLE=memgraph
|
||||
|
||||
all: $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(SOURCES)
|
||||
$(CXX) $(CFLAGS) $(SOURCES) -o $(EXECUTABLE) $(INC) $(LDFLAGS)
|
||||
|
||||
.PHONY:
|
||||
clean:
|
||||
rm -f memgraph
|
||||
rm -f *.o
|
@ -2,6 +2,8 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
print "generating include.hpp file"
|
||||
|
||||
resource_path = 'resources'
|
||||
template_path = 'resources/include.hpp.template'
|
||||
include_path = 'resources/include.hpp'
|
||||
@ -16,6 +18,7 @@ class Resource(object):
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.class_name = None
|
||||
|
||||
with open(os.path.join(resource_path, filename)) as f:
|
||||
class_name = re.compile('\s*class\s*(\w+)\s*\:')
|
||||
@ -32,8 +35,10 @@ class Resource(object):
|
||||
|
||||
|
||||
def load_resources():
|
||||
return [Resource(f) for f in os.listdir(resource_path)
|
||||
if f.endswith('.hpp')]
|
||||
resources = [Resource(f) for f in os.listdir(resource_path)
|
||||
if f.endswith('.hpp')]
|
||||
|
||||
return [r for r in resources if r.class_name is not None]
|
||||
|
||||
|
||||
def write_includes(file, resources):
|
||||
|
@ -4,20 +4,21 @@
|
||||
#include "speedy/speedy.hpp"
|
||||
#include "api/restful/resource.hpp"
|
||||
|
||||
class Animal : public api::Resource<Animal, api::GET, api::POST>
|
||||
{
|
||||
public:
|
||||
Animal(speedy::Speedy& app) : Resource(app, "/animal") {}
|
||||
/* class Ani mal : public Resource<Animal, GET, POST> */
|
||||
/* { */
|
||||
/* public: */
|
||||
/* Animal(sp::Speedy& app) */
|
||||
/* : Resource(app, "/animal/{id:\\d+}/{name:\\w+}/commit") {} */
|
||||
|
||||
void get(http::Request& req, http::Response& res)
|
||||
{
|
||||
return res.send("Ok, here is a Dog");
|
||||
}
|
||||
/* void get(sp::Request& req, sp::Response& res) */
|
||||
/* { */
|
||||
/* return res.send("Ok, here is a Dog"); */
|
||||
/* } */
|
||||
|
||||
void post(http::Request& req, http::Response& res)
|
||||
{
|
||||
return res.send("Oh, you gave me an animal?");
|
||||
}
|
||||
};
|
||||
/* void post(sp::Request& req, sp::Response& res) */
|
||||
/* { */
|
||||
/* return res.send("Oh, you gave me an animal?"); */
|
||||
/* } */
|
||||
/* }; */
|
||||
|
||||
#endif
|
||||
|
@ -15,19 +15,19 @@
|
||||
#include "api/restful/resource.hpp"
|
||||
#include "speedy/speedy.hpp"
|
||||
|
||||
#include "animal.hpp"
|
||||
#include "node.hpp"
|
||||
|
||||
static std::list<std::unique_ptr<api::RestfulResource>> resources;
|
||||
static std::list<std::unique_ptr<RestfulResource>> resources;
|
||||
|
||||
template <class T>
|
||||
void insert(speedy::Speedy& app)
|
||||
void insert(sp::Speedy& app)
|
||||
{
|
||||
resources.push_back(std::unique_ptr<api::RestfulResource>(new T(app)));
|
||||
resources.push_back(std::unique_ptr<RestfulResource>(new T(app)));
|
||||
}
|
||||
|
||||
void init(speedy::Speedy& app)
|
||||
void init(sp::Speedy& app)
|
||||
{
|
||||
insert<Animal>(app);
|
||||
insert<Node>(app);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -17,15 +17,15 @@
|
||||
|
||||
<INCLUDE>
|
||||
|
||||
static std::list<std::unique_ptr<api::RestfulResource>> resources;
|
||||
static std::list<std::unique_ptr<RestfulResource>> resources;
|
||||
|
||||
template <class T>
|
||||
void insert(speedy::Speedy& app)
|
||||
void insert(sp::Speedy& app)
|
||||
{
|
||||
resources.push_back(std::unique_ptr<api::RestfulResource>(new T(app)));
|
||||
resources.push_back(std::unique_ptr<RestfulResource>(new T(app)));
|
||||
}
|
||||
|
||||
void init(speedy::Speedy& app)
|
||||
void init(sp::Speedy& app)
|
||||
{
|
||||
<INIT>
|
||||
}
|
||||
|
32
api/resources/node.hpp
Normal file
32
api/resources/node.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef MEMGRAPH_API_RESOURCES_NODE_HPP
|
||||
#define MEMGRAPH_API_RESOURCES_NODE_HPP
|
||||
|
||||
#include "api/restful/resource.hpp"
|
||||
|
||||
class Node : public Resource<Node, GET, POST>
|
||||
{
|
||||
public:
|
||||
Node(sp::Speedy& app) : Resource(app, "/node") {}
|
||||
|
||||
void get(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
return res.send("Ok, here is a Dog");
|
||||
}
|
||||
|
||||
void post(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
return res.send("Ok, here is a Dog");
|
||||
}
|
||||
|
||||
void put(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
return res.send("Ok, here is a Dog");
|
||||
}
|
||||
|
||||
void del(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
return res.send("Ok, here is a Dog");
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -5,9 +5,6 @@
|
||||
#include "speedy/speedy.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
|
||||
namespace api
|
||||
{
|
||||
|
||||
/** @brief GET handler method for the resource
|
||||
* Contains the code for registering GET handler for a URL to Speedy
|
||||
*/
|
||||
@ -21,7 +18,7 @@ struct GET
|
||||
* @param resource Object containing ::get http::request_cb_t handler
|
||||
*/
|
||||
template <class T>
|
||||
void link(speedy::Speedy& app, const std::string& path, T& resource)
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.get(path, std::bind(&T::get, resource, _1, _2));
|
||||
@ -41,13 +38,33 @@ struct POST
|
||||
* @param resource Object containing ::post http::request_cb_t handler
|
||||
*/
|
||||
template <class T>
|
||||
void link(speedy::Speedy& app, const std::string& path, T& resource)
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.post(path, std::bind(&T::post, resource, _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
struct PUT
|
||||
{
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.put(path, std::bind(&T::put, resource, _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
struct DELETE
|
||||
{
|
||||
template <class T>
|
||||
void link(sp::Speedy& app, const std::string& path, T& resource)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
app.del(path, std::bind(&T::del, resource, _1, _2));
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
@ -66,7 +83,7 @@ struct Method : public M
|
||||
* @param app instance of speedy to register the method to
|
||||
* @param path URL of the resource being registered
|
||||
*/
|
||||
Method(speedy::Speedy& app, const std::string& path)
|
||||
Method(sp::Speedy& app, const std::string& path)
|
||||
{
|
||||
M::link(app, path, static_cast<T&>(*this));
|
||||
}
|
||||
@ -95,7 +112,7 @@ struct Methods;
|
||||
template <class T, class M, class... Ms>
|
||||
struct Methods<T, M, Ms...> : public Method<T, M>, public Methods<T, Ms...>
|
||||
{
|
||||
Methods(speedy::Speedy& app, const std::string& path)
|
||||
Methods(sp::Speedy& app, const std::string& path)
|
||||
: Method<T, M>(app, path), Methods<T, Ms...>(app, path) {}
|
||||
};
|
||||
|
||||
@ -122,7 +139,7 @@ struct RestfulResource {};
|
||||
* class T. Methods are given as a template parameter to the class. Valid
|
||||
* template parameters are classes which implement a function
|
||||
*
|
||||
* void link(speedy::Speedy&, const std::string&, T& resource)
|
||||
* void link(sp::Speedy&, const std::string&, T& resource)
|
||||
*
|
||||
* which registers a method you want to use with speedy
|
||||
*
|
||||
@ -133,10 +150,8 @@ template <class T, class... Ms>
|
||||
class Resource : public detail::Methods<T, Ms...>, public RestfulResource
|
||||
{
|
||||
public:
|
||||
Resource(speedy::Speedy& app, const std::string& path)
|
||||
Resource(sp::Speedy& app, const std::string& path)
|
||||
: detail::Methods<T, Ms...>(app, path) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
4
build.sh
Executable file
4
build.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd api && python link_resources.py && cd ..
|
||||
make clean && make
|
11
data_structures/stack/array_stack.hpp
Normal file
11
data_structures/stack/array_stack.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef MEMGRAPH_DATA_STRUCTURES_STACK_ARRAY_STACK_HPP
|
||||
#define MEMGRAPH_DATA_STRUCTURES_STACK_ARRAY_STACK_HPP
|
||||
|
||||
template <class T>
|
||||
class ArrayStack
|
||||
{
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
55
main.cpp
55
main.cpp
@ -1,55 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
#include "transaction/transaction_engine.hpp"
|
||||
#include "memory/memory_engine.hpp"
|
||||
#include "storage/storage_engine.hpp"
|
||||
|
||||
constexpr int K = 10000000;
|
||||
constexpr int N = 64;
|
||||
|
||||
void insert(TransactionEngine* tx, StorageEngine* storage)
|
||||
{
|
||||
for(int i = 0; i < (K / N); ++i)
|
||||
{
|
||||
auto t = tx->begin();
|
||||
|
||||
Vertex* v;
|
||||
storage->insert(&v, t);
|
||||
|
||||
tx->commit(t);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
auto tx = new TransactionEngine(0);
|
||||
auto mem = new MemoryEngine();
|
||||
auto storage = new StorageEngine(*mem);
|
||||
|
||||
// auto t1 = tx->begin();
|
||||
//
|
||||
// Vertex* v1 = nullptr;
|
||||
// storage->insert(&v1, t1);
|
||||
//
|
||||
// tx->commit(t1);
|
||||
//
|
||||
// auto t2 = tx->begin();
|
||||
//
|
||||
// Vertex* v2 = nullptr;
|
||||
// storage->insert(&v2, t2);
|
||||
//
|
||||
// tx->commit(t2);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
threads.push_back(std::thread(insert, tx, storage));
|
||||
|
||||
for(auto& thread : threads)
|
||||
thread.join();
|
||||
|
||||
return 0;
|
||||
}
|
22
memgraph.cpp
Normal file
22
memgraph.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "speedy/speedy.hpp"
|
||||
#include "api/resources/include.hpp"
|
||||
|
||||
#include "utils/auto_scope.hpp"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
uv::UvLoop loop;
|
||||
sp::Speedy app(loop);
|
||||
|
||||
init(app);
|
||||
|
||||
http::Ipv4 ip("0.0.0.0", 3400);
|
||||
app.listen(ip);
|
||||
|
||||
loop.run(uv::UvLoop::Mode::Default);
|
||||
|
||||
return 0;
|
||||
}
|
38
mpsc.cpp
38
mpsc.cpp
@ -1,38 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include "debug/log.hpp"
|
||||
|
||||
static constexpr int N = 1000;
|
||||
|
||||
using q_t = lockfree::MpscQueue<std::string>;
|
||||
|
||||
void produce()
|
||||
{
|
||||
std::hash<std::thread::id> hasher;
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
{
|
||||
auto str = "Hello number " + std::to_string(i) + " from thread " +
|
||||
std::to_string(hasher(std::this_thread::get_id()));
|
||||
|
||||
LOG_DEBUG(str);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
constexpr int THREADS = 256;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for(int i = 0; i < THREADS; ++i)
|
||||
threads.push_back(std::thread([]() { produce(); }));
|
||||
|
||||
for(auto& thread : threads)
|
||||
thread.join();
|
||||
|
||||
return 0;
|
||||
}
|
115
promise.cpp
115
promise.cpp
@ -1,115 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
|
||||
#include "server/uv/uv.hpp"
|
||||
#include "server/http/http.hpp"
|
||||
|
||||
template <class T, class... Args>
|
||||
class Promise
|
||||
{
|
||||
public:
|
||||
Promise(std::function<T(Args...)> f, Args&&... args)
|
||||
{
|
||||
result = f(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class R>
|
||||
Promise<R, T> then(std::function<R(T)> f)
|
||||
{
|
||||
return Promise(f, std::forward<T>(result));
|
||||
}
|
||||
|
||||
T get()
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
T result;
|
||||
std::atomic<bool> completed;
|
||||
};
|
||||
|
||||
class TaskPool
|
||||
{
|
||||
template <class R, class... Args>
|
||||
class Task
|
||||
{
|
||||
public:
|
||||
using task_t = Task<R, Args...>;
|
||||
using work_f = std::function<R(Args...)>;
|
||||
using after_work_f = std::function<void(R)>;
|
||||
|
||||
Task(work_f work, after_work_f callback)
|
||||
: work(work), callback(callback)
|
||||
{
|
||||
req.data = this;
|
||||
}
|
||||
|
||||
void launch(uv::UvLoop& loop)
|
||||
{
|
||||
uv_queue_work(loop, &req, work_cb, after_work_cb);
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<R(Args...)> work;
|
||||
std::function<void(R&)> callback;
|
||||
|
||||
R result;
|
||||
|
||||
uv_work_t req;
|
||||
|
||||
static void work_cb(uv_work_t* req)
|
||||
{
|
||||
auto& task = *reinterpret_cast<task_t*>(req->data);
|
||||
}
|
||||
|
||||
static void after_work_cb(uv_work_t* req, int)
|
||||
{
|
||||
auto task = reinterpret_cast<task_t>(req->data);
|
||||
delete task;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
TaskPool(uv::UvLoop& loop) : loop(loop) {}
|
||||
|
||||
template <class R, class...Args>
|
||||
void launch(std::function<R(Args...)> func,
|
||||
std::function<void(R&)> callback)
|
||||
{
|
||||
auto task = new Task<R, Args...>(func, callback);
|
||||
task->launch(loop);
|
||||
}
|
||||
|
||||
private:
|
||||
uv::UvLoop& loop;
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uv::UvLoop loop;
|
||||
TaskPool tp(loop);
|
||||
|
||||
tp.launch([](void) -> int { return 3 },
|
||||
[](int x) -> void { std::cout << x << std::endl; });
|
||||
|
||||
// http::HttpServer server(loop);
|
||||
//
|
||||
// http::Ipv4 ip("0.0.0.0", 3400);
|
||||
//
|
||||
// server.listen(ip, [](http::Request& req, http::Response& res) {
|
||||
//
|
||||
//
|
||||
//
|
||||
// res.send(req.url);
|
||||
// });
|
||||
//
|
||||
// loop.run(uv::UvLoop::Mode::Default);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
@ -7,25 +7,35 @@
|
||||
#include "request.hpp"
|
||||
#include "response.hpp"
|
||||
|
||||
#include "utils/memory/block_allocator.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
template <class Req, class Res>
|
||||
class HttpServer;
|
||||
|
||||
template <class Req, class Res>
|
||||
class HttpConnection
|
||||
{
|
||||
friend class HttpServer<Req, Res>;
|
||||
|
||||
using server_t = HttpServer<Req, Res>;
|
||||
using connection_t = HttpConnection<Req, Res>;
|
||||
using parser_t = HttpParser<Req, Res>;
|
||||
|
||||
public:
|
||||
HttpConnection(uv::UvLoop& loop, HttpServer& server);
|
||||
HttpConnection(uv::UvLoop& loop, HttpServer<Req, Res>& server);
|
||||
|
||||
void close();
|
||||
|
||||
HttpServer& server;
|
||||
server_t& server;
|
||||
uv::TcpStream client;
|
||||
|
||||
HttpParser parser;
|
||||
parser_t parser;
|
||||
|
||||
Request request;
|
||||
Response response;
|
||||
Req request;
|
||||
Res response;
|
||||
|
||||
bool keep_alive;
|
||||
};
|
||||
|
@ -8,17 +8,19 @@
|
||||
namespace http
|
||||
{
|
||||
|
||||
HttpConnection::HttpConnection(uv::UvLoop& loop, HttpServer& server)
|
||||
template <class Req, class Res>
|
||||
HttpConnection<Req, Res>::HttpConnection(uv::UvLoop& loop, server_t& server)
|
||||
: server(server), client(loop), response(*this)
|
||||
{
|
||||
client.data(this);
|
||||
parser.data(this);
|
||||
}
|
||||
|
||||
void HttpConnection::close()
|
||||
template <class Req, class Res>
|
||||
void HttpConnection<Req, Res>::close()
|
||||
{
|
||||
client.close([](uv_handle_t* client) -> void {
|
||||
delete reinterpret_cast<HttpConnection*>(client->data);
|
||||
delete reinterpret_cast<connection_t*>(client->data);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -9,19 +9,25 @@
|
||||
namespace http
|
||||
{
|
||||
|
||||
template <class Req, class Res>
|
||||
class HttpConnection;
|
||||
|
||||
template <class Req, class Res>
|
||||
class HttpParserSettings;
|
||||
|
||||
template <class Req, class Res>
|
||||
class HttpParser
|
||||
{
|
||||
friend class HttpParserSettings;
|
||||
using connection_t = HttpConnection<Req, Res>;
|
||||
using settings_t = HttpParserSettings<Req, Res>;
|
||||
|
||||
friend class HttpParserSettings<Req, Res>;
|
||||
public:
|
||||
HttpParser();
|
||||
|
||||
static void init();
|
||||
|
||||
size_t execute(HttpParserSettings& settings,
|
||||
const char* data,
|
||||
size_t size);
|
||||
size_t execute(settings_t& settings, const char* data, size_t size);
|
||||
|
||||
template <typename T>
|
||||
T* data();
|
||||
|
@ -9,73 +9,80 @@
|
||||
namespace http
|
||||
{
|
||||
|
||||
HttpParser::HttpParser()
|
||||
template <class Req, class Res>
|
||||
HttpParser<Req, Res>::HttpParser()
|
||||
{
|
||||
http_parser_init(&parser, HTTP_REQUEST);
|
||||
}
|
||||
|
||||
size_t HttpParser::execute(HttpParserSettings& settings,
|
||||
const char* data,
|
||||
size_t size)
|
||||
template <class Req, class Res>
|
||||
size_t HttpParser<Req, Res>::
|
||||
execute(settings_t& settings, const char* data, size_t size)
|
||||
{
|
||||
return http_parser_execute(&parser, settings, data, size);
|
||||
}
|
||||
|
||||
template <class Req, class Res>
|
||||
template <typename T>
|
||||
T* HttpParser::data()
|
||||
T* HttpParser<Req, Res>::data()
|
||||
{
|
||||
return reinterpret_cast<T*>(parser.data);
|
||||
}
|
||||
|
||||
template <class Req, class Res>
|
||||
template <typename T>
|
||||
void HttpParser::data(T* value)
|
||||
void HttpParser<Req, Res>::data(T* value)
|
||||
{
|
||||
parser.data = reinterpret_cast<void*>(value);
|
||||
}
|
||||
|
||||
int HttpParser::on_message_begin(http_parser*)
|
||||
template <class Req, class Res>
|
||||
int HttpParser<Req, Res>::on_message_begin(http_parser*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_url(http_parser* parser,
|
||||
const char* at,
|
||||
size_t length)
|
||||
template <class Req, class Res>
|
||||
int HttpParser<Req, Res>::
|
||||
on_url(http_parser* parser, const char* at, size_t length)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
||||
conn.request.url = std::string(at, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_status_complete(http_parser*, const char*, size_t)
|
||||
template <class Req, class Res>
|
||||
int HttpParser<Req, Res>::
|
||||
on_status_complete(http_parser*, const char*, size_t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_header_field(http_parser* parser,
|
||||
const char* at,
|
||||
size_t length)
|
||||
template <class Req, class Res>
|
||||
int HttpParser<Req, Res>::
|
||||
on_header_field(http_parser* parser, const char* at, size_t length)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
||||
conn.parser.last_field = std::string(at, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_header_value(http_parser* parser,
|
||||
const char* at,
|
||||
size_t length)
|
||||
template <class Req, class Res>
|
||||
int HttpParser<Req, Res>::
|
||||
on_header_value(http_parser* parser, const char* at, size_t length)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
||||
conn.request.headers[conn.parser.last_field] = std::string(at, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_headers_complete(http_parser* parser)
|
||||
template <class Req, class Res>
|
||||
int HttpParser<Req, Res>::on_headers_complete(http_parser* parser)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
||||
|
||||
conn.request.version.major = parser->http_major;
|
||||
conn.request.version.minor = parser->http_minor;
|
||||
@ -86,20 +93,23 @@ int HttpParser::on_headers_complete(http_parser* parser)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_body(http_parser* parser, const char* at, size_t length)
|
||||
template <class Req, class Res>
|
||||
int HttpParser<Req, Res>::
|
||||
on_body(http_parser* parser, const char* at, size_t length)
|
||||
{
|
||||
if(length == 0)
|
||||
return 0;
|
||||
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
||||
conn.request.body.append(at, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_message_complete(http_parser* parser)
|
||||
template <class Req, class Res>
|
||||
int HttpParser<Req, Res>::on_message_complete(http_parser* parser)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
connection_t& conn = *reinterpret_cast<connection_t*>(parser->data);
|
||||
conn.server.respond(conn);
|
||||
|
||||
return 0;
|
||||
|
@ -6,6 +6,7 @@
|
||||
namespace http
|
||||
{
|
||||
|
||||
template <class Req, class Res>
|
||||
class HttpParserSettings
|
||||
{
|
||||
public:
|
||||
|
@ -7,19 +7,21 @@
|
||||
namespace http
|
||||
{
|
||||
|
||||
HttpParserSettings::HttpParserSettings()
|
||||
template <class Req, class Res>
|
||||
HttpParserSettings<Req, Res>::HttpParserSettings()
|
||||
{
|
||||
settings.on_header_field = HttpParser::on_header_field;
|
||||
settings.on_header_value = HttpParser::on_header_value;
|
||||
settings.on_headers_complete = HttpParser::on_headers_complete;
|
||||
settings.on_body = HttpParser::on_body;
|
||||
settings.on_status = HttpParser::on_status_complete;
|
||||
settings.on_message_begin = HttpParser::on_message_begin;
|
||||
settings.on_message_complete = HttpParser::on_message_complete;
|
||||
settings.on_url = HttpParser::on_url;
|
||||
settings.on_header_field = HttpParser<Req, Res>::on_header_field;
|
||||
settings.on_header_value = HttpParser<Req, Res>::on_header_value;
|
||||
settings.on_headers_complete = HttpParser<Req, Res>::on_headers_complete;
|
||||
settings.on_body = HttpParser<Req, Res>::on_body;
|
||||
settings.on_status = HttpParser<Req, Res>::on_status_complete;
|
||||
settings.on_message_begin = HttpParser<Req, Res>::on_message_begin;
|
||||
settings.on_message_complete = HttpParser<Req, Res>::on_message_complete;
|
||||
settings.on_url = HttpParser<Req, Res>::on_url;
|
||||
}
|
||||
|
||||
HttpParserSettings::operator http_parser_settings*()
|
||||
template <class Req, class Res>
|
||||
HttpParserSettings<Req, Res>::operator http_parser_settings*()
|
||||
{
|
||||
return &settings;
|
||||
}
|
||||
|
@ -11,14 +11,17 @@
|
||||
#include "response.hpp"
|
||||
#include "ipv4.hpp"
|
||||
|
||||
#include "utils/memory/block_allocator.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
typedef std::function<void(Request&, Response&)> request_cb_t;
|
||||
|
||||
template <class Req, class Res>
|
||||
class HttpServer
|
||||
{
|
||||
friend class HttpParser;
|
||||
public:
|
||||
using request_cb_t = std::function<void(Req&, Res&)>;
|
||||
|
||||
HttpServer(uv::UvLoop& loop);
|
||||
|
||||
void listen(const Ipv4& ip, request_cb_t callback);
|
||||
@ -27,9 +30,14 @@ public:
|
||||
operator uv_stream_t*();
|
||||
|
||||
private:
|
||||
using server_t = HttpServer<Req, Res>;
|
||||
using connection_t = HttpConnection<Req, Res>;
|
||||
|
||||
friend class HttpParser<Req, Res>;
|
||||
|
||||
uv::UvLoop& loop;
|
||||
uv::TcpStream stream;
|
||||
HttpParserSettings settings;
|
||||
HttpParserSettings<Req, Res> settings;
|
||||
|
||||
request_cb_t request_cb;
|
||||
|
||||
@ -43,7 +51,7 @@ private:
|
||||
size_t suggested_size,
|
||||
uv_buf_t* buf);
|
||||
|
||||
static void respond(HttpConnection& conn);
|
||||
static void respond(HttpConnection<Req, Res>& conn);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -6,63 +6,71 @@
|
||||
namespace http
|
||||
{
|
||||
|
||||
HttpServer::HttpServer(uv::UvLoop& loop)
|
||||
static BlockAllocator<65536> buffer_allocator;
|
||||
|
||||
template <class Req, class Res>
|
||||
HttpServer<Req, Res>::HttpServer(uv::UvLoop& loop)
|
||||
: loop(loop), stream(loop) {}
|
||||
|
||||
void HttpServer::listen(const Ipv4& ip, request_cb_t callback)
|
||||
template <class Req, class Res>
|
||||
void HttpServer<Req, Res>::listen(const Ipv4& ip, request_cb_t callback)
|
||||
{
|
||||
request_cb = callback;
|
||||
stream.data(this);
|
||||
|
||||
uv_tcp_bind(stream, ip, 0);
|
||||
uv_listen(stream, 128, HttpServer::on_connect);
|
||||
uv_listen(stream, 128, server_t::on_connect);
|
||||
}
|
||||
|
||||
HttpServer::operator uv_tcp_t*()
|
||||
template <class Req, class Res>
|
||||
HttpServer<Req, Res>::operator uv_tcp_t*()
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
|
||||
HttpServer::operator uv_stream_t*()
|
||||
template <class Req, class Res>
|
||||
HttpServer<Req, Res>::operator uv_stream_t*()
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
|
||||
void HttpServer::on_connect(uv_stream_t* tcp_server, int)
|
||||
template <class Req, class Res>
|
||||
void HttpServer<Req, Res>::on_connect(uv_stream_t* tcp_server, int)
|
||||
{
|
||||
HttpServer& server = *reinterpret_cast<HttpServer*>(tcp_server->data);
|
||||
server_t& server = *reinterpret_cast<server_t*>(tcp_server->data);
|
||||
|
||||
auto connection = new HttpConnection(server.loop, server);
|
||||
auto connection = new connection_t(server.loop, server);
|
||||
|
||||
uv_accept(server, connection->client);
|
||||
|
||||
uv_read_start(connection->client,
|
||||
HttpServer::on_alloc,
|
||||
HttpServer::on_read);
|
||||
uv_read_start(connection->client, server_t::on_alloc, server_t::on_read);
|
||||
}
|
||||
|
||||
void HttpServer::on_alloc(uv_handle_t*, size_t suggested_size, uv_buf_t* buf)
|
||||
template <class Req, class Res>
|
||||
void HttpServer<Req, Res>::
|
||||
on_alloc(uv_handle_t*, size_t, uv_buf_t* buf)
|
||||
{
|
||||
buf->base = static_cast<char*>(malloc(sizeof(char) * suggested_size));
|
||||
buf->len = suggested_size;
|
||||
buf->base = static_cast<char*>(buffer_allocator.acquire());
|
||||
buf->len = buffer_allocator.size;
|
||||
}
|
||||
|
||||
void HttpServer::on_read(uv_stream_t* tcp_client,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* buf)
|
||||
template <class Req, class Res>
|
||||
void HttpServer<Req, Res>::
|
||||
on_read(uv_stream_t* tcp_client, ssize_t nread, const uv_buf_t* buf)
|
||||
{
|
||||
HttpConnection& conn =
|
||||
*reinterpret_cast<HttpConnection*>(tcp_client->data);
|
||||
connection_t& conn = *reinterpret_cast<connection_t*>(tcp_client->data);
|
||||
|
||||
if(nread >= 0)
|
||||
conn.parser.execute(conn.server.settings, buf->base, nread);
|
||||
else
|
||||
conn.close();
|
||||
|
||||
delete buf->base;
|
||||
buffer_allocator.release(buf->base);
|
||||
//delete buf->base;
|
||||
}
|
||||
|
||||
void HttpServer::respond(HttpConnection& conn)
|
||||
template <class Req, class Res>
|
||||
void HttpServer<Req, Res>::respond(connection_t& conn)
|
||||
{
|
||||
conn.server.request_cb(conn.request, conn.response);
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ struct Request
|
||||
Method method;
|
||||
|
||||
std::string url;
|
||||
|
||||
std::map<std::string, std::string> headers;
|
||||
|
||||
std::string body;
|
||||
};
|
||||
|
||||
|
@ -9,25 +9,28 @@
|
||||
namespace http
|
||||
{
|
||||
|
||||
template <class Req, class Res>
|
||||
class HttpConnection;
|
||||
|
||||
template <class Req, class Res>
|
||||
class Response
|
||||
{
|
||||
using connection_t = HttpConnection<Req, Res>;
|
||||
using response_t = Response<Req, Res>;
|
||||
|
||||
public:
|
||||
Response(HttpConnection& connection);
|
||||
Response(connection_t& connection);
|
||||
|
||||
void send(const std::string& body);
|
||||
void send(Status code, const std::string& body);
|
||||
|
||||
Response& status(Status code);
|
||||
|
||||
std::map<std::string, std::string> headers;
|
||||
|
||||
private:
|
||||
HttpConnection& connection;
|
||||
uv::UvBuffer buffer;
|
||||
Status status;
|
||||
|
||||
Status code;
|
||||
private:
|
||||
connection_t& connection;
|
||||
uv::UvBuffer buffer;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -4,59 +4,62 @@
|
||||
#include "response.hpp"
|
||||
#include "httpconnection.hpp"
|
||||
|
||||
#include "utils/memory/block_allocator.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
Response::Response(HttpConnection& connection)
|
||||
: connection(connection), buffer(65536), code(Status::Ok) {}
|
||||
constexpr size_t buffer_size = 65536;
|
||||
|
||||
void Response::send(Status code, const std::string& body)
|
||||
static BlockAllocator<sizeof(uv_write_t)> write_req_allocator;
|
||||
|
||||
template <class Req, class Res>
|
||||
Response<Req, Res>::Response(connection_t& connection)
|
||||
: status(Status::Ok), connection(connection),
|
||||
buffer(buffer_size) {}
|
||||
|
||||
template <class Req, class Res>
|
||||
void Response<Req, Res>::send(Status status, const std::string& body)
|
||||
{
|
||||
this->status(code).send(body);
|
||||
this->status = status;
|
||||
this->send(body);
|
||||
}
|
||||
|
||||
void Response::send(const std::string& body)
|
||||
template <class Req, class Res>
|
||||
void Response<Req, Res>::send(const std::string& body)
|
||||
{
|
||||
uv_write_t* write_req =
|
||||
static_cast<uv_write_t*>(malloc(sizeof(uv_write_t)));
|
||||
static_cast<uv_write_t*>(write_req_allocator.acquire());
|
||||
|
||||
write_req->data = &connection;
|
||||
|
||||
// set the appropriate content length
|
||||
headers["Content-Length"] = std::to_string(body.size());
|
||||
buffer << "HTTP/1.1 " << to_string[status] << "\r\n";
|
||||
|
||||
// set the appropriate connection type
|
||||
headers["Connection"] = connection.keep_alive ? "Keep-Alive" : "Close";
|
||||
buffer << "Content-Length:" << std::to_string(body.size()) << "\r\n";
|
||||
|
||||
buffer << "HTTP/1.1 " << to_string[code] << "\r\n";
|
||||
buffer << "Connection:" << (connection.keep_alive ? "Keep-Alive" : "Close")
|
||||
<< "\r\n";
|
||||
|
||||
for(auto it = headers.begin(); it != headers.end(); ++it)
|
||||
buffer << it->first << ":" << it->second << "\r\n";
|
||||
|
||||
buffer << "\r\n" << body;
|
||||
|
||||
uv_write(write_req, connection.client, buffer, 1,
|
||||
uv_write(write_req, connection.client, buffer, 1,
|
||||
[](uv_write_t* write_req, int) {
|
||||
|
||||
HttpConnection& conn =
|
||||
*reinterpret_cast<HttpConnection*>(write_req->data);
|
||||
connection_t& conn = *reinterpret_cast<connection_t*>(write_req->data);
|
||||
|
||||
if(!conn.keep_alive)
|
||||
conn.close();
|
||||
|
||||
conn.response.code = Status::Ok;
|
||||
conn.response.status = Status::Ok;
|
||||
conn.response.buffer.clear();
|
||||
conn.response.headers.clear();
|
||||
std::free(write_req);
|
||||
write_req_allocator.release(write_req);
|
||||
});
|
||||
}
|
||||
|
||||
Response& Response::status(Status code)
|
||||
{
|
||||
this->code = code;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -4,15 +4,25 @@
|
||||
#include <list>
|
||||
#include <cassert>
|
||||
|
||||
#include "request.hpp"
|
||||
#include "response.hpp"
|
||||
|
||||
#include "utils/auto_scope.hpp"
|
||||
#include "http/http.hpp"
|
||||
|
||||
namespace r3 {
|
||||
#include "r3_include.h"
|
||||
}
|
||||
|
||||
namespace speedy
|
||||
namespace sp
|
||||
{
|
||||
|
||||
class R3Error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
class R3
|
||||
{
|
||||
public:
|
||||
@ -73,11 +83,11 @@ public:
|
||||
return route != nullptr;
|
||||
}
|
||||
|
||||
void operator()(http::Request& req, http::Response& res)
|
||||
void operator()(sp::Request& req, sp::Response& res)
|
||||
{
|
||||
assert(route != nullptr);
|
||||
|
||||
auto cb = reinterpret_cast<http::request_cb_t*>(route->data);
|
||||
auto cb = reinterpret_cast<sp::request_cb_t*>(route->data);
|
||||
cb->operator()(req, res);
|
||||
}
|
||||
|
||||
@ -104,7 +114,7 @@ public:
|
||||
other.root = nullptr;
|
||||
}
|
||||
|
||||
void insert(Method method, const std::string& path, http::request_cb_t cb)
|
||||
void insert(Method method, const std::string& path, sp::request_cb_t cb)
|
||||
{
|
||||
routes.push_back(cb);
|
||||
|
||||
@ -114,12 +124,41 @@ public:
|
||||
|
||||
Route match(Method method, const std::string& path)
|
||||
{
|
||||
auto entry = MatchEntry(method, path);
|
||||
return Route(r3::r3_tree_match_route(root, entry));
|
||||
auto entry_m = MatchEntry(method, path);
|
||||
return Route(r3::r3_tree_match_route(root, entry_m));
|
||||
|
||||
/* if(!route.exists()) */
|
||||
/* return route; */
|
||||
|
||||
/* r3::match_entry* entry = entry_m; */
|
||||
|
||||
/* std::cout << "Eetry Matched!" << std::endl */
|
||||
/* << "path = " << std::string(entry->path, entry->path_len) << std::endl */
|
||||
/* << "host = " << std::string(entry->host, entry->host_len) << std::endl */
|
||||
/* << "remote_addr = " << std::string(entry->remote_addr, entry->remote_addr_len) << std::endl */
|
||||
/* << "tokens_len = " << entry->vars->len << std::endl; */
|
||||
|
||||
/* for(int i = 0; i < entry->vars->len; ++i) */
|
||||
/* std::cout << "token " << i << " = " << std::string(entry->vars->tokens[i]) << std::endl; */
|
||||
|
||||
//return route;
|
||||
}
|
||||
|
||||
void compile()
|
||||
{
|
||||
char* error_string = nullptr;
|
||||
bool error = r3_tree_compile(root, &error_string);
|
||||
|
||||
if(!error)
|
||||
return;
|
||||
|
||||
Auto(free(error_string));
|
||||
|
||||
throw R3Error(std::string(error_string));
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<http::request_cb_t> routes;
|
||||
std::list<sp::request_cb_t> routes;
|
||||
r3::node* root;
|
||||
};
|
||||
|
||||
|
17
speedy/request.hpp
Normal file
17
speedy/request.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef MEMGRAPH_SPEEDY_REQUEST_HPP
|
||||
#define MEMGRAPH_SPEEDY_REQUEST_HPP
|
||||
|
||||
#include "http/request.hpp"
|
||||
|
||||
namespace sp
|
||||
{
|
||||
|
||||
class Request : public http::Request
|
||||
{
|
||||
public:
|
||||
using http::Request::Request;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
20
speedy/response.hpp
Normal file
20
speedy/response.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef MEMGRAPH_SPEEDY_RESPONSE_HPP
|
||||
#define MEMGRAPH_SPEEDY_RESPONSE_HPP
|
||||
|
||||
#include "request.hpp"
|
||||
#include "http/response.hpp"
|
||||
|
||||
namespace sp
|
||||
{
|
||||
|
||||
class Response : public http::Response<Request, Response>
|
||||
{
|
||||
public:
|
||||
using http::Response<Request, Response>::Response;
|
||||
};
|
||||
|
||||
using request_cb_t = std::function<void(Request&, Response&)>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -14,13 +14,17 @@
|
||||
#include "http/http.hpp"
|
||||
#include "r3.hpp"
|
||||
|
||||
namespace speedy
|
||||
#include "request.hpp"
|
||||
#include "response.hpp"
|
||||
|
||||
namespace sp
|
||||
{
|
||||
|
||||
class Speedy
|
||||
{
|
||||
public:
|
||||
using sptr = std::shared_ptr<Speedy>;
|
||||
using server_t = http::HttpServer<Request, Response>;
|
||||
using request_cb_t = server_t::request_cb_t;
|
||||
|
||||
Speedy(uv::UvLoop& loop, size_t capacity = 100)
|
||||
: server(loop), router(capacity) {}
|
||||
@ -28,29 +32,33 @@ public:
|
||||
Speedy(Speedy&) = delete;
|
||||
Speedy(Speedy&&) = delete;
|
||||
|
||||
void get(const std::string& path, http::request_cb_t cb)
|
||||
void get(const std::string& path, server_t::request_cb_t cb)
|
||||
{
|
||||
router.insert(R3::Method::GET, path, cb);
|
||||
}
|
||||
|
||||
void post(const std::string& path, http::request_cb_t cb)
|
||||
void post(const std::string& path, server_t::request_cb_t cb)
|
||||
{
|
||||
router.insert(R3::Method::POST, path, cb);
|
||||
}
|
||||
|
||||
void put(const std::string& path, http::request_cb_t cb)
|
||||
void put(const std::string& path, server_t::request_cb_t cb)
|
||||
{
|
||||
router.insert(R3::Method::PUT, path, cb);
|
||||
}
|
||||
|
||||
void del(const std::string& path, http::request_cb_t cb)
|
||||
void del(const std::string& path, server_t::request_cb_t cb)
|
||||
{
|
||||
router.insert(R3::Method::DELETE, path, cb);
|
||||
}
|
||||
|
||||
void listen(const http::Ipv4& ip)
|
||||
{
|
||||
server.listen(ip, [this](http::Request& req, http::Response& res) {
|
||||
router.compile();
|
||||
|
||||
server.listen(ip, [this](Request& req, Response& res) {
|
||||
return res.send("Hello World");
|
||||
|
||||
auto route = router.match(R3::to_r3_method(req.method), req.url);
|
||||
|
||||
if(!route.exists())
|
||||
@ -61,7 +69,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
http::HttpServer server;
|
||||
server_t server;
|
||||
R3 router;
|
||||
};
|
||||
|
||||
|
@ -1,50 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "data_structures/skiplist/skiplist.hpp"
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
xorshift::init();
|
||||
SkipList<int, int> skiplist;
|
||||
|
||||
int k1 = 1;
|
||||
int i1 = 10;
|
||||
|
||||
int k2 = 2;
|
||||
int i2 = 20;
|
||||
|
||||
std::cout << "find: " << k1 << " => " << (skiplist.find(&k1) == nullptr) << std::endl;
|
||||
std::cout << skiplist.insert(&k1, &i1) << std::endl;
|
||||
std::cout << "find: " << k1 << " => " << *skiplist.find(&k1)->item << std::endl;
|
||||
std::cout << "find: " << k2 << " => " << (skiplist.find(&k2) == nullptr) << std::endl;
|
||||
std::cout << skiplist.insert(&k2, &i2) << std::endl;
|
||||
std::cout << skiplist.insert(&k2, &i2) << std::endl;
|
||||
std::cout << skiplist.insert(&k1, &i1) << std::endl;
|
||||
std::cout << skiplist.insert(&k2, &i2) << std::endl;
|
||||
std::cout << "DELETE K1 " << skiplist.remove(&k1) << std::endl;
|
||||
std::cout << "find: " << k1 << " => " << (skiplist.find(&k1) == nullptr) << std::endl;
|
||||
std::cout << "find: " << k2 << " => " << *skiplist.find(&k2)->item << std::endl;
|
||||
|
||||
std::cout << skiplist.size() << std::endl;
|
||||
|
||||
auto* node = skiplist.header.load(std::memory_order_consume);
|
||||
|
||||
for(size_t i = 0; i < skiplist.size(); ++i)
|
||||
{
|
||||
node = node->forward(0);
|
||||
|
||||
if(node == nullptr)
|
||||
std::cout << "node == nullptr" << std::endl;
|
||||
|
||||
if(node->key == nullptr)
|
||||
std::cout << "node->key == nullptr" << std::endl;
|
||||
|
||||
if(node->item == nullptr)
|
||||
std::cout << "node->item == nullptr" << std::endl;
|
||||
|
||||
std::cout << *node->key << " => " << *node->item << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
60
utils/auto_scope.hpp
Normal file
60
utils/auto_scope.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef MEMGRAPH_UTILS_AUTO_SCOPE_HPP
|
||||
#define MEMGRAPH_UTILS_AUTO_SCOPE_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
/* @brief Calls a cleanup function on scope exit
|
||||
*
|
||||
* consider this example:
|
||||
*
|
||||
* void hard_worker()
|
||||
* {
|
||||
* resource.enable();
|
||||
* do_stuff(); // throws exception
|
||||
* resource.disable();
|
||||
* }
|
||||
*
|
||||
* if do_stuff throws an exception, resource.disable is never called
|
||||
* and the app is left in an inconsistent state. ideally, you would like
|
||||
* to call resource.disable regardles of the exception being thrown.
|
||||
* OnScopeExit makes this possible and very convenient via a 'Auto' macro
|
||||
*
|
||||
* void hard_worker()
|
||||
* {
|
||||
* resource.enable();
|
||||
* Auto(resource.disable());
|
||||
* do_stuff(); // throws exception
|
||||
* }
|
||||
*
|
||||
* now, resource.disable will be called every time it goes out of scope
|
||||
* regardless of the exception
|
||||
*
|
||||
* @tparam F Lambda which holds a wrapper function around the cleanup code
|
||||
*/
|
||||
template <class F>
|
||||
class OnScopeExit
|
||||
{
|
||||
public:
|
||||
OnScopeExit(F& f) : f(f) {}
|
||||
|
||||
~OnScopeExit()
|
||||
{
|
||||
// calls the cleanup function
|
||||
f();
|
||||
}
|
||||
|
||||
private:
|
||||
F& f;
|
||||
};
|
||||
|
||||
#define TOKEN_PASTEx(x, y) x##y
|
||||
#define TOKEN_PASTE(x, y) TOKEN_PASTEx(x, y)
|
||||
|
||||
#define Auto_INTERNAL(Destructor, counter) \
|
||||
auto TOKEN_PASTE(auto_func_, counter) = [&]() { Destructor; }; \
|
||||
OnScopeExit<decltype(TOKEN_PASTE(auto_func_, counter))> \
|
||||
TOKEN_PASTE(auto_, counter)(TOKEN_PASTE(auto_func_, counter));
|
||||
|
||||
#define Auto(Destructor) Auto_INTERNAL(Destructor, __COUNTER__)
|
||||
|
||||
#endif
|
54
utils/memory/block_allocator.hpp
Normal file
54
utils/memory/block_allocator.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef MEMGRAPH_UTILS_MEMORY_BLOCK_ALLOCATOR_HPP
|
||||
#define MEMGRAPH_UTILS_MEMORY_BLOCK_ALLOCATOR_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "utils/auto_scope.hpp"
|
||||
|
||||
template <size_t block_size>
|
||||
class BlockAllocator
|
||||
{
|
||||
struct Block
|
||||
{
|
||||
Block()
|
||||
{
|
||||
data = malloc(block_size);
|
||||
}
|
||||
|
||||
Block(void* ptr)
|
||||
{
|
||||
data = ptr;
|
||||
}
|
||||
|
||||
void* data;
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr size_t size = block_size;
|
||||
|
||||
BlockAllocator(size_t capacity = 0)
|
||||
{
|
||||
for(size_t i = 0; i < capacity; ++i)
|
||||
blocks.emplace_back();
|
||||
}
|
||||
|
||||
void* acquire()
|
||||
{
|
||||
if(blocks.size() == 0)
|
||||
blocks.emplace_back();
|
||||
|
||||
Auto(blocks.pop_back());
|
||||
return blocks.back().data;
|
||||
}
|
||||
|
||||
void release(void* ptr)
|
||||
{
|
||||
blocks.emplace_back(ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Block> blocks;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user