a wild random checkpoint commit appeared
This commit is contained in:
parent
f31f4693ce
commit
fef9953f45
@ -17,6 +17,7 @@ on a 64 bit linux kernel.
|
||||
* clang 3.5 or Gcc 4.8 (good c++11 support, especially lock free atomics)
|
||||
* boost 1.55 (or something, probably works with almost anything)
|
||||
* lexertl (2015-07-14)
|
||||
* lemon (parser generator)
|
||||
* catch (for compiling tests)
|
||||
|
||||
## build
|
||||
|
8
cypher/.gitignore
vendored
Normal file
8
cypher/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
lexertl
|
||||
lemon
|
||||
*.o
|
||||
cypher.cpp
|
||||
cypher.h
|
||||
parser
|
||||
cypher.out
|
||||
parser
|
29
cypher/Makefile
Normal file
29
cypher/Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
CXX = clang++
|
||||
CXXFLAGS = -std=c++11
|
||||
INC = -I../
|
||||
|
||||
parser: parser.o cypher.o
|
||||
$(CXX) parser.o cypher.o -o parser $(INC) $(CXXFLAGS)
|
||||
|
||||
cypher.o:
|
||||
$(CXX) cypher.cpp -c -o cypher.o $(INC) $(CXXFLAGS)
|
||||
|
||||
parser.o: parser.cpp cypher.hpp
|
||||
$(CXX) parser.cpp -c -o parser.o $(INC) $(CXXFLAGS)
|
||||
|
||||
cypher.cpp: lemonfiles
|
||||
|
||||
cypher.hpp: lemonfiles
|
||||
|
||||
.PHONY: lemonfiles
|
||||
lemonfiles: cypher.y
|
||||
lemon/lemon cypher.y -s
|
||||
mv cypher.c cypher.cpp
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f cypher.cpp
|
||||
rm -f cypher.h
|
||||
rm -f parser
|
||||
rm -f cypher.out
|
24
cypher/ast/accessor.hpp
Normal file
24
cypher/ast/accessor.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_AST_ACCESSOR_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_AST_ACCESSOR_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "expr.hpp"
|
||||
#include "identifier.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Accessor : public VisitableExpr<Accessor>
|
||||
{
|
||||
Accessor(Identifier* entity, Identifier* prop)
|
||||
: entity(entity), prop(prop) {}
|
||||
|
||||
Identifier::uptr entity;
|
||||
Identifier::uptr prop;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
13
cypher/ast/ast.hpp
Normal file
13
cypher/ast/ast.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_AST_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_AST_HPP
|
||||
|
||||
#include "accessor.hpp"
|
||||
#include "values.hpp"
|
||||
#include "identifier.hpp"
|
||||
#include "operators.hpp"
|
||||
#include "property.hpp"
|
||||
#include "relationship.hpp"
|
||||
#include "node.hpp"
|
||||
#include "return.hpp"
|
||||
|
||||
#endif
|
24
cypher/ast/ast_echo.hpp
Normal file
24
cypher/ast/ast_echo.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_PRINT_TREE_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_PRINT_TREE_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "ast_visitor.hpp"
|
||||
#include "values.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
class AstEcho : public AstVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void visit(Boolean& node)
|
||||
{
|
||||
std::cout << "Boolean: " << node.value << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
33
cypher/ast/ast_node.hpp
Normal file
33
cypher/ast/ast_node.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_AST_NODE_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_AST_NODE_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "utils/visitor/visitable.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
#include "ast_visitor.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct AstVisitor;
|
||||
|
||||
struct AstVisitable : public Visitable<AstVisitor>
|
||||
{
|
||||
using uptr = std::unique_ptr<AstVisitable>;
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
struct AstNode : public Crtp<Derived>, public AstVisitable
|
||||
{
|
||||
using uptr = std::unique_ptr<Derived>;
|
||||
|
||||
virtual void accept(AstVisitor& visitor)
|
||||
{
|
||||
visitor.visit(this->derived());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
52
cypher/ast/ast_visitor.hpp
Normal file
52
cypher/ast/ast_visitor.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_AST_VISITOR_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_AST_VISITOR_HPP
|
||||
|
||||
#include "utils/visitor/visitor.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Identifier;
|
||||
|
||||
// properties
|
||||
struct Property;
|
||||
struct PropertyList;
|
||||
struct Accessor;
|
||||
|
||||
// values
|
||||
struct Boolean;
|
||||
struct Float;
|
||||
struct Integer;
|
||||
struct String;
|
||||
|
||||
// operators
|
||||
struct And;
|
||||
struct Or;
|
||||
struct Lt;
|
||||
struct Gt;
|
||||
struct Ge;
|
||||
struct Le;
|
||||
struct Eq;
|
||||
struct Ne;
|
||||
struct Plus;
|
||||
struct Minus;
|
||||
struct Star;
|
||||
struct Slash;
|
||||
struct Rem;
|
||||
|
||||
struct RelationshipList;
|
||||
struct Relationship;
|
||||
|
||||
struct Node;
|
||||
struct LabelList;
|
||||
|
||||
struct ReturnList;
|
||||
|
||||
struct AstVisitor : Visitor<Accessor, Boolean, Float, Identifier, Integer,
|
||||
String, Property, And, Or, Lt, Gt, Ge, Le, Eq, Ne, Plus, Minus, Star,
|
||||
Slash, Rem, PropertyList, RelationshipList, Relationship, Node,
|
||||
LabelList, ReturnList> {};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
40
cypher/ast/expr.hpp
Normal file
40
cypher/ast/expr.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_EXPR_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_EXPR_HPP
|
||||
|
||||
#include "ast_node.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Expr : public AstVisitable {};
|
||||
|
||||
template <class Derived>
|
||||
struct VisitableExpr : public Crtp<Derived>, public Expr
|
||||
{
|
||||
using uptr = std::unique_ptr<Derived>;
|
||||
|
||||
virtual void accept(AstVisitor& visitor)
|
||||
{
|
||||
visitor.visit(this->derived());
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class Derived>
|
||||
struct LeafExpr : public VisitableExpr<Derived>
|
||||
{
|
||||
LeafExpr(T value) : value(value) {}
|
||||
T value;
|
||||
};
|
||||
|
||||
template <class Derived>
|
||||
struct BinaryExpr : public VisitableExpr<Derived>
|
||||
{
|
||||
BinaryExpr(Expr* left, Expr* right) : left(left), right(right) {}
|
||||
|
||||
Expr* left;
|
||||
Expr* right;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
19
cypher/ast/identifier.hpp
Normal file
19
cypher/ast/identifier.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_AST_IDENTIFIER_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_AST_IDENTIFIER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ast_node.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Identifier : public AstNode<Identifier>
|
||||
{
|
||||
Identifier(std::string name) : name(name) {}
|
||||
std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
21
cypher/ast/list.hpp
Normal file
21
cypher/ast/list.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_LIST_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_LIST_HPP
|
||||
|
||||
#include "ast_node.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
template <class T, class Derived>
|
||||
struct List : public AstNode<Derived>
|
||||
{
|
||||
List(T* value, Derived* next)
|
||||
: value(value), next(next) {}
|
||||
|
||||
T* value;
|
||||
Derived* next;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
27
cypher/ast/node.hpp
Normal file
27
cypher/ast/node.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_NODE_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_NODE_HPP
|
||||
|
||||
#include "list.hpp"
|
||||
#include "identifier.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct LabelList : public List<Identifier, LabelList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
struct Node : public AstNode<Node>
|
||||
{
|
||||
Node(Identifier* idn, LabelList* labels, PropertyList* props)
|
||||
: idn(idn), labels(labels), props(props) {}
|
||||
|
||||
Identifier* idn;
|
||||
LabelList* labels;
|
||||
PropertyList* props;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
76
cypher/ast/operators.hpp
Normal file
76
cypher/ast/operators.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_OPERATORS_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_OPERATORS_HPP
|
||||
|
||||
#include "expr.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct And : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Or : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Lt : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Gt : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Ge : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Le : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Eq : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Ne : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Plus : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Minus : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Star : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Slash : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
struct Rem : public BinaryExpr<And>
|
||||
{
|
||||
using BinaryExpr::BinaryExpr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
24
cypher/ast/pattern.hpp
Normal file
24
cypher/ast/pattern.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_PATTERN_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_PATTERN_HPP
|
||||
|
||||
#include "ast_node.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
template <class Derived>
|
||||
struct Direction : public AstNode<Derived> {};
|
||||
|
||||
struct DirectionLeft : public Direction<DirectionLeft> {};
|
||||
struct DirectionRight : public Direction<DirectionLeft> {};
|
||||
struct Unidirectional : public Direction<DirectionLeft> {};
|
||||
|
||||
struct Pattern : public AstNode<Pattern>
|
||||
{
|
||||
Node* node;
|
||||
Pattern* next;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
27
cypher/ast/property.hpp
Normal file
27
cypher/ast/property.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_AST_PROPERTY_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_AST_PROPERTY_HPP
|
||||
|
||||
#include "list.hpp"
|
||||
#include "identifier.hpp"
|
||||
#include "expr.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Property : public AstNode<Property>
|
||||
{
|
||||
Property(Identifier* idn, Expr* value)
|
||||
: idn(idn), value(value) {}
|
||||
|
||||
Identifier* idn;
|
||||
Expr* value;
|
||||
};
|
||||
|
||||
struct PropertyList : public List<Property, PropertyList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
32
cypher/ast/relationship.hpp
Normal file
32
cypher/ast/relationship.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_RELATIONSHIP_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_RELATIONSHIP_HPP
|
||||
|
||||
#include "list.hpp"
|
||||
#include "identifier.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct RelationshipList : public List<Identifier, RelationshipList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
struct RelationshipSpecs : public AstNode<RelationshipSpecs>
|
||||
{
|
||||
RelationshipSpecs(Identifier* idn, RelationshipList* types, PropertyList* props)
|
||||
: idn(idn), types(types), props(props) {}
|
||||
|
||||
Identifier* idn;
|
||||
RelationshipList* types;
|
||||
PropertyList* props;
|
||||
};
|
||||
|
||||
struct Relationship : public AstNode<Relationship>
|
||||
{
|
||||
Relationship()
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
17
cypher/ast/return.hpp
Normal file
17
cypher/ast/return.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_RETURN_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_RETURN_HPP
|
||||
|
||||
#include "list.hpp"
|
||||
#include "identifier.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct ReturnList : public List<Identifier, ReturnList>
|
||||
{
|
||||
using List::List;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
41
cypher/ast/tree.hpp
Normal file
41
cypher/ast/tree.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_TREE_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_TREE_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ast_node.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
class Ast
|
||||
{
|
||||
public:
|
||||
Ast() {}
|
||||
|
||||
AstVisitable::uptr root;
|
||||
|
||||
void traverse(AstVisitor& visitor)
|
||||
{
|
||||
root->accept(visitor);
|
||||
}
|
||||
|
||||
template <class T, typename... Args>
|
||||
T* create(Args&&... args)
|
||||
{
|
||||
auto node = new T(std::forward<Args>(args)...);
|
||||
items.push_back(std::unique_ptr<AstVisitable>(node));
|
||||
return node;
|
||||
}
|
||||
|
||||
private:
|
||||
// basically a gc vector that destroys all ast nodes once this object is
|
||||
// destroyed. parser generator is written in c and works only with raw
|
||||
// pointers so this is what makes it leak free after the parser finishes
|
||||
// parsing
|
||||
std::vector<AstVisitable::uptr> items;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
33
cypher/ast/values.hpp
Normal file
33
cypher/ast/values.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef MEMGRAPH_CYPHER_AST_VALUES_HPP
|
||||
#define MEMGRAPH_CYPHER_AST_VALUES_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "expr.hpp"
|
||||
|
||||
namespace ast
|
||||
{
|
||||
|
||||
struct Float : public LeafExpr<double, Float>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
struct Integer : public LeafExpr<int, Integer>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
struct Boolean : public LeafExpr<bool, Boolean>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
struct String : public LeafExpr<std::string, String>
|
||||
{
|
||||
using LeafExpr::LeafExpr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
17
cypher/ast/visitor.cpp
Normal file
17
cypher/ast/visitor.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "ast_visitor.hpp"
|
||||
#include "ast_node.hpp"
|
||||
#include "ast_echo.hpp"
|
||||
#include "boolean.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Token t;
|
||||
AstNode* root = new Boolean(t);
|
||||
|
||||
AstEcho echo;
|
||||
root->accept(echo);
|
||||
|
||||
return 0;
|
||||
}
|
291
cypher/cypher.y
Normal file
291
cypher/cypher.y
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
** This file contains memgraph's grammar for Cypher. Process this file
|
||||
** using the lemon parser generator to generate C code that runs
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
*/
|
||||
|
||||
%token_prefix TK_
|
||||
|
||||
%token_type {Token*}
|
||||
|
||||
%extra_argument {ast::Ast* ast}
|
||||
|
||||
%syntax_error
|
||||
{
|
||||
std::cout << "syntax error near '" << TOKEN->value << "'." << std::endl;
|
||||
throw "";
|
||||
}
|
||||
|
||||
%stack_overflow
|
||||
{
|
||||
std::cout << "parser stack overflow" << std::endl;
|
||||
}
|
||||
|
||||
%name cypher_parser
|
||||
|
||||
%include
|
||||
{
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "token.hpp"
|
||||
#include "ast/ast.hpp"
|
||||
#include "ast/tree.hpp"
|
||||
|
||||
#define DEBUG(X) std::cout << X << std::endl
|
||||
}
|
||||
|
||||
// define operator precedence
|
||||
%left OR.
|
||||
%left AND.
|
||||
%right NOT.
|
||||
%left IN IS_NULL IS_NOT_NULL NE EQ.
|
||||
%left GT LE LT GE.
|
||||
%left PLUS MINUS.
|
||||
%left STAR SLASH REM.
|
||||
|
||||
start ::= read_query.
|
||||
|
||||
read_query ::= match_clause return_clause.
|
||||
|
||||
match_clause ::= MATCH pattern where_clause.
|
||||
|
||||
%type pattern {ast::Pattern*}
|
||||
|
||||
// pattern specification
|
||||
pattern ::= node rel pattern. {
|
||||
|
||||
}
|
||||
|
||||
pattern ::= node. {
|
||||
|
||||
}
|
||||
|
||||
rel ::= MINUS rel_spec MINUS. { // unidirectional
|
||||
|
||||
}
|
||||
|
||||
rel ::= LT MINUS rel_spec MINUS { // left
|
||||
|
||||
}
|
||||
|
||||
rel(R) ::= MINUS rel_spec(S) MINUS GT { // right
|
||||
R = ast->create<ast::Relationship>(
|
||||
}
|
||||
|
||||
%type rel_spec {ast::RelationshipSpecs*}
|
||||
|
||||
rel_spec(R) ::= LSP rel_idn(I) rel_type(T) properties(P) RSP. {
|
||||
R = ast->create<ast::RelationshipSpecs>(I, T, P);
|
||||
}
|
||||
|
||||
rel_spec(R) ::= . {
|
||||
R = nullptr;
|
||||
}
|
||||
|
||||
%type rel_idn {ast::Identifier*}
|
||||
|
||||
rel_idn(R) ::= idn(I). {
|
||||
R = I;
|
||||
}
|
||||
|
||||
rel_idn(R) ::= . {
|
||||
R = nullptr;
|
||||
}
|
||||
|
||||
%type rel_type {ast::RelationshipList*}
|
||||
|
||||
rel_type(L) ::= COLON rel_list(R). {
|
||||
L = R;
|
||||
}
|
||||
|
||||
rel_type(L) ::= . {
|
||||
L = nullptr;
|
||||
}
|
||||
|
||||
%type rel_list {ast::RelationshipList*}
|
||||
|
||||
rel_list(L) ::= idn(I) PIPE rel_list(R). {
|
||||
L = ast->create<ast::RelationshipList>(I, R);
|
||||
}
|
||||
|
||||
rel_list(L) ::= idn(I). {
|
||||
L = ast->create<ast::RelationshipList>(I, nullptr);
|
||||
}
|
||||
|
||||
%type node {ast::Node*}
|
||||
|
||||
// node specification
|
||||
node(N) ::= LP node_idn(I) label_idn(L) properties(P) RP. {
|
||||
N = ast->create<ast::Node>(I, L, P);
|
||||
}
|
||||
|
||||
node(N) ::= idn(I). {
|
||||
N = ast->create<ast::Node>(I, nullptr, nullptr);
|
||||
}
|
||||
|
||||
%type node_idn {ast::Identifier*}
|
||||
|
||||
// a node identifier can be ommitted
|
||||
node_idn(N) ::= idn(I). {
|
||||
N = I;
|
||||
}
|
||||
|
||||
node_idn(N) ::= . {
|
||||
N = nullptr;
|
||||
}
|
||||
|
||||
%type label_idn {ast::LabelList*}
|
||||
|
||||
// a label can be ommited or there can be more of them
|
||||
label_idn(L) ::= COLON idn(I) label_idn(N). {
|
||||
L = ast->create<ast::LabelList>(I, N);
|
||||
}
|
||||
|
||||
label_idn(L) ::= . {
|
||||
L = nullptr;
|
||||
}
|
||||
|
||||
%type where_clause {ast::Expr*}
|
||||
|
||||
// where clause
|
||||
where_clause(W) ::= WHERE expr(E). {
|
||||
W = E;
|
||||
}
|
||||
|
||||
where_clause(W) ::= . {
|
||||
W = nullptr;
|
||||
}
|
||||
|
||||
%type return_clause {ast::ReturnList*}
|
||||
|
||||
// return clause
|
||||
return_clause(R) ::= RETURN return_list(L). {
|
||||
R = L;
|
||||
}
|
||||
|
||||
%type return_list {ast::ReturnList*}
|
||||
|
||||
return_list(R) ::= idn(I) COMMA return_list(N). {
|
||||
R = ast->create<ast::ReturnList>(I, N);
|
||||
}
|
||||
|
||||
return_list(R) ::= idn(I). {
|
||||
R = ast->create<ast::ReturnList>(I, nullptr);
|
||||
}
|
||||
|
||||
%type properties {ast::PropertyList*}
|
||||
|
||||
// '{' <property_list> '}'
|
||||
properties(P) ::= LCP property_list(L) RCP. {
|
||||
P = L;
|
||||
}
|
||||
|
||||
properties(P) ::= . {
|
||||
P = nullptr;
|
||||
}
|
||||
|
||||
%type property_list {ast::PropertyList*}
|
||||
|
||||
// <property> [[',' <property>]]*
|
||||
property_list(L) ::= property(P) COMMA property_list(N). {
|
||||
L = ast->create<ast::PropertyList>(P, N);
|
||||
}
|
||||
|
||||
property_list(L) ::= property(P). {
|
||||
L = ast->create<ast::PropertyList>(P, nullptr);
|
||||
}
|
||||
|
||||
%type property {ast::Property*}
|
||||
|
||||
// IDENTIFIER ':' <expression>
|
||||
property(P) ::= idn(I) COLON expr(E). {
|
||||
P = ast->create<ast::Property>(I, E);
|
||||
}
|
||||
|
||||
%type expr {ast::Expr*}
|
||||
|
||||
expr(E) ::= expr(L) AND expr(R). {
|
||||
E = ast->create<ast::And>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) OR expr(R). {
|
||||
E = ast->create<ast::Or>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) LT expr(R). {
|
||||
E = ast->create<ast::Lt>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) GT expr(R). {
|
||||
E = ast->create<ast::Gt>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) GE expr(R). {
|
||||
E = ast->create<ast::Ge>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) LE expr(R). {
|
||||
E = ast->create<ast::Le>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) EQ expr(R). {
|
||||
E = ast->create<ast::Eq>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) NE expr(R). {
|
||||
E = ast->create<ast::Ne>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) PLUS expr(R). {
|
||||
E = ast->create<ast::Plus>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) MINUS expr(R). {
|
||||
E = ast->create<ast::Minus>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) STAR expr(R). {
|
||||
E = ast->create<ast::Star>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) SLASH expr(R). {
|
||||
E = ast->create<ast::Slash>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= expr(L) REM expr(R). {
|
||||
E = ast->create<ast::Rem>(L, R);
|
||||
}
|
||||
|
||||
expr(E) ::= idn(I) DOT idn(P). {
|
||||
E = ast->create<ast::Accessor>(I, P);
|
||||
}
|
||||
|
||||
%type idn {ast::Identifier*}
|
||||
|
||||
idn(I) ::= IDN(X). {
|
||||
I = ast->create<ast::Identifier>(X->value);
|
||||
}
|
||||
|
||||
expr(E) ::= INT(V). {
|
||||
auto value = std::stoi(V->value);
|
||||
E = ast->create<ast::Integer>(value);
|
||||
}
|
||||
|
||||
expr(E) ::= FLOAT(V). {
|
||||
auto value = std::stod(V->value);
|
||||
E = ast->create<ast::Float>(value);
|
||||
}
|
||||
|
||||
expr(E) ::= STR(V). {
|
||||
auto value = V->value.substr(1, V->value.size() - 2);
|
||||
E = ast->create<ast::String>(value);
|
||||
}
|
||||
|
||||
expr(E) ::= BOOL(V). {
|
||||
auto value = V->value[0] == 't' || V->value[0] == 'T' ? true : false;
|
||||
E = ast->create<ast::Boolean>(value);
|
||||
}
|
||||
|
72
cypher/cypher_lexer.hpp
Normal file
72
cypher/cypher_lexer.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef MEMGRAPH_CYPHER_LEXER_CYPHER_LEXER_HPP
|
||||
#define MEMGRAPH_CYPHER_LEXER_CYPHER_LEXER_HPP
|
||||
|
||||
#include "cypher.h"
|
||||
|
||||
#include "lexer.hpp"
|
||||
|
||||
class CypherLexer : public Lexer
|
||||
{
|
||||
public:
|
||||
|
||||
CypherLexer()
|
||||
{
|
||||
// whitespace
|
||||
rule("\\s+", sm.skip());
|
||||
|
||||
// special characters
|
||||
rule("\\.", TK_DOT);
|
||||
rule(",", TK_COMMA);
|
||||
rule(":", TK_COLON);
|
||||
rule("\\|", TK_PIPE);
|
||||
rule("\\{", TK_LCP);
|
||||
rule("\\}", TK_RCP);
|
||||
rule("\\(", TK_LP);
|
||||
rule("\\)", TK_RP);
|
||||
rule("\\[", TK_LSP);
|
||||
rule("\\]", TK_RSP);
|
||||
|
||||
// operators
|
||||
rule("\\+", TK_PLUS);
|
||||
rule("-", TK_MINUS);
|
||||
rule("\\*", TK_STAR);
|
||||
rule("\\/", TK_SLASH);
|
||||
rule("%", TK_REM);
|
||||
|
||||
rule(">", TK_GT);
|
||||
rule("<", TK_LT);
|
||||
rule(">=", TK_GE);
|
||||
rule("<=", TK_LE);
|
||||
rule("=", TK_EQ);
|
||||
rule("<>", TK_NE);
|
||||
|
||||
// constants
|
||||
rule("(?i:TRUE)", TK_BOOL);
|
||||
rule("(?i:FALSE)", TK_BOOL);
|
||||
|
||||
// keywords
|
||||
rule("(?i:MATCH)", TK_MATCH);
|
||||
rule("(?i:WHERE)", TK_WHERE);
|
||||
rule("(?i:RETURN)", TK_RETURN);
|
||||
|
||||
rule("(?i:AND)", TK_AND);
|
||||
rule("(?i:OR)", TK_OR);
|
||||
|
||||
// string literal TODO single quote escape
|
||||
rule("'(.*?)'", TK_STR);
|
||||
|
||||
// string literal TODO double quote escape
|
||||
rule("\\\"(.*?)\\\"", TK_STR);
|
||||
|
||||
// number
|
||||
rule("\\d+", TK_INT);
|
||||
rule("\\d*[.]?\\d+", TK_FLOAT);
|
||||
|
||||
// identifier
|
||||
rule("[_a-zA-Z][_a-zA-Z0-9]{0,30}", TK_IDN);
|
||||
|
||||
build();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
56
cypher/lexer.cpp
Normal file
56
cypher/lexer.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include "lexertl/generator.hpp"
|
||||
#include <iostream>
|
||||
#include "lexertl/lookup.hpp"
|
||||
|
||||
#include "cypher_lexer.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
CypherLexer lexer;
|
||||
|
||||
auto tokenizer = lexer.tokenize("{name: 'Dominik', lastName: 'Tomicevic', age: 24 }");
|
||||
|
||||
while(true)
|
||||
{
|
||||
auto token = tokenizer.lookup();
|
||||
|
||||
if(token.id == 0)
|
||||
break;
|
||||
|
||||
std::cout << token.id << " -> " << token.value << std::endl;
|
||||
}
|
||||
|
||||
|
||||
lexertl::rules rules;
|
||||
lexertl::state_machine sm;
|
||||
|
||||
rules.push("\\s+", sm.skip());
|
||||
rules.push("MATCH", 1);
|
||||
rules.push("RETURN", 2);
|
||||
|
||||
rules.push("'(.*?)'", 4); // string literal TODO single quote escape
|
||||
rules.push("\\\"(.*?)\\\"", 4); // string literal TODO double quote escape
|
||||
rules.push("[-+]?(\\d*[.])?\\d+", 5); // number
|
||||
|
||||
rules.push("[_a-zA-Z][_a-zA-Z0-9]{0,30}", 3); // identifier
|
||||
|
||||
lexertl::generator::build(rules, sm);
|
||||
|
||||
std::string input("MATCH (user:User { name: 'Dominik', age: 24})-[has:HAS]->(item:Item) WHERE item.name = 'XPS 13', AND item.price = 14.99 RETURN user, has, item");
|
||||
lexertl::smatch results(input.begin(), input.end());
|
||||
|
||||
// Read ahead
|
||||
lexertl::lookup(sm, results);
|
||||
|
||||
while (results.id != 0)
|
||||
{
|
||||
std::cout << "Id: " << results.id << ", Token: '" <<
|
||||
results.str () << "'\n";
|
||||
lexertl::lookup(sm, results);
|
||||
}
|
||||
|
||||
std::cout << "-1 to uint64_t = " << uint64_t(-1) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
58
cypher/lexer.hpp
Normal file
58
cypher/lexer.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef MEMGRAPH_CYPHER_LEXER_HPP
|
||||
#define MEMGRAPH_CYPHER_LEXER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "lexertl/generator.hpp"
|
||||
#include "lexertl/lookup.hpp"
|
||||
|
||||
#include "lexical_error.hpp"
|
||||
#include "token.hpp"
|
||||
|
||||
class Lexer
|
||||
{
|
||||
public:
|
||||
|
||||
class Tokenizer
|
||||
{
|
||||
public:
|
||||
Tokenizer(const Lexer& lexer, const std::string& str)
|
||||
: lexer(lexer), results(str.begin(), str.end()) {}
|
||||
|
||||
Token* lookup()
|
||||
{
|
||||
lexertl::lookup(lexer.sm, results);
|
||||
auto token = new Token {results.id, results.str()};
|
||||
|
||||
if(results.id == static_cast<decltype(results.id)>(-1))
|
||||
throw LexicalError(*token);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
private:
|
||||
const Lexer& lexer;
|
||||
lexertl::smatch results;
|
||||
};
|
||||
|
||||
Tokenizer tokenize(const std::string& str)
|
||||
{
|
||||
return Tokenizer(*this, str);
|
||||
}
|
||||
|
||||
void build()
|
||||
{
|
||||
lexertl::generator::build(rules, sm);
|
||||
}
|
||||
|
||||
void rule(const std::string& regex, uint64_t id)
|
||||
{
|
||||
rules.push(regex, id);
|
||||
}
|
||||
|
||||
protected:
|
||||
lexertl::rules rules;
|
||||
lexertl::state_machine sm;
|
||||
};
|
||||
|
||||
#endif
|
15
cypher/lexical_error.hpp
Normal file
15
cypher/lexical_error.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef MEMGRAPH_CYPHER_LEXER_LEXICAL_ERROR_HPP
|
||||
#define MEMGRAPH_CYPHER_LEXER_LEXICAL_ERROR_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "token.hpp"
|
||||
|
||||
class LexicalError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
LexicalError(const Token& token)
|
||||
: std::runtime_error("Unrecognized token '" + token.value + "'.") {}
|
||||
};
|
||||
|
||||
#endif
|
55
cypher/parser.cpp
Normal file
55
cypher/parser.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <vector>
|
||||
|
||||
#include "cypher.h"
|
||||
#include "token.hpp"
|
||||
|
||||
#include "cypher_lexer.hpp"
|
||||
#include "ast/tree.hpp"
|
||||
|
||||
void* cypher_parserAlloc(void* (*allocProc)(size_t));
|
||||
void cypher_parser(void*, int, Token*, ast::Ast* ast);
|
||||
void cypher_parserFree(void*, void(*freeProc)(void*));
|
||||
|
||||
int main()
|
||||
{
|
||||
void* parser = cypher_parserAlloc(malloc);
|
||||
CypherLexer lexer;
|
||||
|
||||
//std::string input("matcH (user:User { name: 'Dominik', age: 8 + 4})-[has:HAS|IS|CAN { duration: 'PERMANENT'}]->(item:Item)--(shop)");
|
||||
|
||||
std::string input("MATCH (user:User { name: 'Dominik', age: 24})-[has:HAS]->(item:Item) WHERE item.name = 'XPS 13' AND item.price = 11999.99 RETURN user, has, item");
|
||||
|
||||
auto tokenizer = lexer.tokenize(input);
|
||||
|
||||
std::vector<Token*> v;
|
||||
|
||||
while(true)
|
||||
{
|
||||
v.push_back(tokenizer.lookup());
|
||||
auto token = v.back();
|
||||
|
||||
//std::cout << token->id << " -> " << token->value << std::endl;
|
||||
|
||||
auto ast = new ast::Ast();
|
||||
|
||||
try
|
||||
{
|
||||
cypher_parser(parser, token->id, token, ast);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
std::cout << "caught exception." << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(token->id == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
cypher_parserFree(parser, free);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
10
cypher/proba.cpp
Normal file
10
cypher/proba.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "ast/float.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
auto f = new Float(3);
|
||||
|
||||
return 0;
|
||||
}
|
13
cypher/token.hpp
Normal file
13
cypher/token.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef MEMGRAPH_CYPHER_TOKEN_HPP
|
||||
#define MEMGRAPH_CYPHER_TOKEN_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
struct Token
|
||||
{
|
||||
unsigned long id;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
#endif
|
@ -26,8 +26,6 @@ public:
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
|
||||
|
||||
return data.size();
|
||||
}
|
||||
|
||||
|
71
data_structures/queue/slqueue.hpp
Normal file
71
data_structures/queue/slqueue.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef MEMGRAPH_DATA_STRUCTURES_QUEUE_SLQUEUE_HPP
|
||||
#define MEMGRAPH_DATA_STRUCTURES_QUEUE_SLQUEUE_HPP
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
namespace spinlock
|
||||
{
|
||||
|
||||
template <class T>
|
||||
class Queue : Lockable<SpinLock>
|
||||
{
|
||||
public:
|
||||
|
||||
template <class... Args>
|
||||
void emplace(Args&&... args)
|
||||
{
|
||||
auto guard = acquire();
|
||||
queue.emplace(args...);
|
||||
}
|
||||
|
||||
void push(const T& item)
|
||||
{
|
||||
auto guard = acquire();
|
||||
queue.push(item);
|
||||
}
|
||||
|
||||
T front()
|
||||
{
|
||||
auto guard = acquire();
|
||||
return queue.front();
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
auto guard = acquire();
|
||||
queue.pop();
|
||||
}
|
||||
|
||||
bool pop(T& item)
|
||||
{
|
||||
auto guard = acquire();
|
||||
if(queue.empty())
|
||||
return false;
|
||||
|
||||
item = std::move(queue.front());
|
||||
queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty()
|
||||
{
|
||||
auto guard = acquire();
|
||||
return queue.empty();
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
auto guard = acquire();
|
||||
return queue.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<T> queue;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "utils/random/xorshift.hpp"
|
||||
|
||||
template <class randomizer_t>
|
||||
size_t new_height(int max_height)
|
||||
{
|
||||
// get 64 random bits (coin tosses)
|
||||
|
@ -3,10 +3,38 @@
|
||||
|
||||
#include <list>
|
||||
|
||||
template <class T,
|
||||
class allocator=std::allocator<T>>
|
||||
class SafeList
|
||||
#include "sync/spinlock.hpp"
|
||||
#include "sync/lockable.hpp"
|
||||
|
||||
template <class T>
|
||||
class SpinLockedList : Lockable<SpinLock>
|
||||
{
|
||||
public:
|
||||
|
||||
void push_front(T item)
|
||||
{
|
||||
auto guard = this->acquire();
|
||||
head = new Node(item, head);
|
||||
}
|
||||
|
||||
bool remove(const T&)
|
||||
{
|
||||
// HM!
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node : Lockable<SpinLock>
|
||||
{
|
||||
Node(T item, Node* next) : item(item), next(next) {}
|
||||
|
||||
T item;
|
||||
Node* next;
|
||||
};
|
||||
|
||||
Node* head;
|
||||
|
||||
// TODO add removed items to a list for garbage collection
|
||||
// std::list<Node*> removed;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
55
main.cpp
Normal file
55
main.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "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;
|
||||
}
|
17
server/http/http.hpp
Normal file
17
server/http/http.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "httpconnection.hpp"
|
||||
#include "method.hpp"
|
||||
#include "httpparser.hpp"
|
||||
#include "httpparsersettings.hpp"
|
||||
#include "httpserver.hpp"
|
||||
#include "version.hpp"
|
||||
#include "ipv4.hpp"
|
||||
#include "request.hpp"
|
||||
#include "response.hpp"
|
||||
#include "status_codes.hpp"
|
||||
|
||||
#include "httpconnection.inl"
|
||||
#include "httpparser.inl"
|
||||
#include "httpparsersettings.inl"
|
||||
#include "httpserver.inl"
|
||||
#include "ipv4.inl"
|
||||
#include "response.inl"
|
19
server/http/http_error.hpp
Normal file
19
server/http/http_error.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTP_ERROR_HPP
|
||||
#define MEMGRAPH_SERVER_HTTP_HTTP_ERROR_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
class HttpError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
HttpError(const std::string& message)
|
||||
: std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
35
server/http/httpconnection.hpp
Normal file
35
server/http/httpconnection.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_CONNECTION_HPP
|
||||
#define MEMGRAPH_SERVER_HTTP_CONNECTION_HPP
|
||||
|
||||
#include "server/uv/uv.hpp"
|
||||
|
||||
#include "httpparser.hpp"
|
||||
#include "request.hpp"
|
||||
#include "response.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
class HttpServer;
|
||||
|
||||
class HttpConnection
|
||||
{
|
||||
public:
|
||||
HttpConnection(uv::UvLoop& loop, HttpServer& server);
|
||||
|
||||
void close();
|
||||
|
||||
HttpServer& server;
|
||||
uv::TcpStream client;
|
||||
|
||||
HttpParser parser;
|
||||
|
||||
Request request;
|
||||
Response response;
|
||||
|
||||
bool keep_alive;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
27
server/http/httpconnection.inl
Normal file
27
server/http/httpconnection.inl
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_CONNECTION_INL
|
||||
#define MEMGRAPH_SERVER_HTTP_CONNECTION_INL
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include "httpconnection.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
HttpConnection::HttpConnection(uv::UvLoop& loop, HttpServer& server)
|
||||
: server(server), client(loop), response(*this)
|
||||
{
|
||||
client.data(this);
|
||||
parser.data(this);
|
||||
}
|
||||
|
||||
void HttpConnection::close()
|
||||
{
|
||||
client.close([](uv_handle_t* client) -> void {
|
||||
delete reinterpret_cast<HttpConnection*>(client->data);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
66
server/http/httpparser.hpp
Normal file
66
server/http/httpparser.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPPARSER_HPP
|
||||
#define MEMGRAPH_SERVER_HTTP_HTTPPARSER_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <http_parser.h>
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
class HttpParserSettings;
|
||||
|
||||
class HttpParser
|
||||
{
|
||||
friend class HttpParserSettings;
|
||||
public:
|
||||
HttpParser();
|
||||
|
||||
static void init();
|
||||
|
||||
size_t execute(HttpParserSettings& settings,
|
||||
const char* data,
|
||||
size_t size);
|
||||
|
||||
template <typename T>
|
||||
T* data();
|
||||
|
||||
template <typename T>
|
||||
void data(T* value);
|
||||
|
||||
private:
|
||||
http_parser parser;
|
||||
|
||||
std::string last_field;
|
||||
|
||||
static int on_message_begin(http_parser* parser);
|
||||
|
||||
static int on_url(http_parser* parser,
|
||||
const char* at,
|
||||
size_t length);
|
||||
|
||||
static int on_status_complete(http_parser* parser,
|
||||
const char* at,
|
||||
size_t length);
|
||||
|
||||
static int on_header_field(http_parser* parser,
|
||||
const char* at,
|
||||
size_t length);
|
||||
|
||||
static int on_header_value(http_parser* parser,
|
||||
const char* at,
|
||||
size_t length);
|
||||
|
||||
static int on_headers_complete(http_parser* parser);
|
||||
|
||||
static int on_body(http_parser* parser,
|
||||
const char* at,
|
||||
size_t length);
|
||||
|
||||
static int on_message_complete(http_parser* parser);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
110
server/http/httpparser.inl
Normal file
110
server/http/httpparser.inl
Normal file
@ -0,0 +1,110 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPSERVER_INL
|
||||
#define MEMGRAPH_SERVER_HTTP_HTTPSERVER_INL
|
||||
|
||||
#include "httpparser.hpp"
|
||||
#include "httpserver.hpp"
|
||||
#include "httpconnection.hpp"
|
||||
#include "httpparsersettings.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
HttpParser::HttpParser()
|
||||
{
|
||||
http_parser_init(&parser, HTTP_REQUEST);
|
||||
}
|
||||
|
||||
size_t HttpParser::execute(HttpParserSettings& settings,
|
||||
const char* data,
|
||||
size_t size)
|
||||
{
|
||||
return http_parser_execute(&parser, settings, data, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* HttpParser::data()
|
||||
{
|
||||
return reinterpret_cast<T*>(parser.data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void HttpParser::data(T* value)
|
||||
{
|
||||
parser.data = reinterpret_cast<void*>(value);
|
||||
}
|
||||
|
||||
int HttpParser::on_message_begin(http_parser*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_url(http_parser* parser,
|
||||
const char* at,
|
||||
size_t length)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
conn.request.url = std::string(at, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::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)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(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)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
conn.request.headers[conn.parser.last_field] = std::string(at, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_headers_complete(http_parser* parser)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
|
||||
conn.request.version.major = parser->http_major;
|
||||
conn.request.version.minor = parser->http_minor;
|
||||
|
||||
conn.request.method = static_cast<Method>(parser->method);
|
||||
conn.keep_alive = http_should_keep_alive(parser) == true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_body(http_parser* parser, const char* at, size_t length)
|
||||
{
|
||||
if(length == 0)
|
||||
return 0;
|
||||
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
conn.request.body.append(at, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HttpParser::on_message_complete(http_parser* parser)
|
||||
{
|
||||
HttpConnection& conn = *reinterpret_cast<HttpConnection*>(parser->data);
|
||||
conn.server.respond(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
22
server/http/httpparsersettings.hpp
Normal file
22
server/http/httpparsersettings.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPPARSERSETTINGS_HPP
|
||||
#define MEMGRAPH_SERVER_HTTP_HTTPPARSERSETTINGS_HPP
|
||||
|
||||
#include <http_parser.h>
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
class HttpParserSettings
|
||||
{
|
||||
public:
|
||||
HttpParserSettings();
|
||||
|
||||
operator http_parser_settings*();
|
||||
|
||||
private:
|
||||
http_parser_settings settings;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
29
server/http/httpparsersettings.inl
Normal file
29
server/http/httpparsersettings.inl
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPPARSERSETTINGS_INL
|
||||
#define MEMGRAPH_SERVER_HTTP_HTTPPARSERSETTINGS_INL
|
||||
|
||||
#include "httpparsersettings.hpp"
|
||||
#include "httpparser.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
HttpParserSettings::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;
|
||||
}
|
||||
|
||||
HttpParserSettings::operator http_parser_settings*()
|
||||
{
|
||||
return &settings;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
51
server/http/httpserver.hpp
Normal file
51
server/http/httpserver.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_HTTPSERVER_HPP
|
||||
#define MEMGRAPH_SERVER_HTTP_HTTPSERVER_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <uv.h>
|
||||
|
||||
#include "server/uv/uv.hpp"
|
||||
#include "httpparsersettings.hpp"
|
||||
#include "httpconnection.hpp"
|
||||
#include "request.hpp"
|
||||
#include "response.hpp"
|
||||
#include "ipv4.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
typedef std::function<void(Request&, Response&)> request_cb_t;
|
||||
|
||||
class HttpServer
|
||||
{
|
||||
friend class HttpParser;
|
||||
public:
|
||||
HttpServer(uv::UvLoop& loop);
|
||||
|
||||
void listen(const Ipv4& ip, request_cb_t callback);
|
||||
|
||||
operator uv_tcp_t*();
|
||||
operator uv_stream_t*();
|
||||
|
||||
private:
|
||||
uv::UvLoop& loop;
|
||||
uv::TcpStream stream;
|
||||
HttpParserSettings settings;
|
||||
|
||||
request_cb_t request_cb;
|
||||
|
||||
static void on_connect(uv_stream_t* tcp_server, int status);
|
||||
|
||||
static void on_read(uv_stream_t* tcp_client,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* buf);
|
||||
|
||||
static void on_alloc(uv_handle_t* tcp_client,
|
||||
size_t suggested_size,
|
||||
uv_buf_t* buf);
|
||||
|
||||
static void respond(HttpConnection& conn);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
72
server/http/httpserver.inl
Normal file
72
server/http/httpserver.inl
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef UVMACHINE_HTTP_HTTPSERVER_INL
|
||||
#define UVMACHINE_HTTP_HTTPSERVER_INL
|
||||
|
||||
#include "httpserver.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
HttpServer::HttpServer(uv::UvLoop& loop)
|
||||
: loop(loop), stream(loop) {}
|
||||
|
||||
void HttpServer::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);
|
||||
}
|
||||
|
||||
HttpServer::operator uv_tcp_t*()
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
|
||||
HttpServer::operator uv_stream_t*()
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
|
||||
void HttpServer::on_connect(uv_stream_t* tcp_server, int)
|
||||
{
|
||||
HttpServer& server = *reinterpret_cast<HttpServer*>(tcp_server->data);
|
||||
|
||||
auto connection = new HttpConnection(server.loop, server);
|
||||
|
||||
uv_accept(server, connection->client);
|
||||
|
||||
uv_read_start(connection->client,
|
||||
HttpServer::on_alloc,
|
||||
HttpServer::on_read);
|
||||
}
|
||||
|
||||
void HttpServer::on_alloc(uv_handle_t*, size_t suggested_size, uv_buf_t* buf)
|
||||
{
|
||||
buf->base = static_cast<char*>(malloc(sizeof(char) * suggested_size));
|
||||
buf->len = suggested_size;
|
||||
}
|
||||
|
||||
void HttpServer::on_read(uv_stream_t* tcp_client,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* buf)
|
||||
{
|
||||
HttpConnection& conn =
|
||||
*reinterpret_cast<HttpConnection*>(tcp_client->data);
|
||||
|
||||
if(nread >= 0)
|
||||
conn.parser.execute(conn.server.settings, buf->base, nread);
|
||||
else
|
||||
conn.close();
|
||||
|
||||
delete buf->base;
|
||||
}
|
||||
|
||||
void HttpServer::respond(HttpConnection& conn)
|
||||
{
|
||||
conn.server.request_cb(conn.request, conn.response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
33
server/http/ipv4.hpp
Normal file
33
server/http/ipv4.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_IPV4_HPP
|
||||
#define MEMGRAPH_SERVER_HTTP_IPV4_HPP
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <uv.h>
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
class Ipv4
|
||||
{
|
||||
public:
|
||||
Ipv4(const std::string& address, uint16_t port);
|
||||
|
||||
operator const sockaddr_in&() const;
|
||||
|
||||
operator const sockaddr*() const;
|
||||
|
||||
friend std::ostream& operator<< (std::ostream& stream, const Ipv4& ip) {
|
||||
return stream << ip.address << ':' << ip.port;
|
||||
}
|
||||
|
||||
protected:
|
||||
sockaddr_in socket_address;
|
||||
|
||||
const std::string& address;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
32
server/http/ipv4.inl
Normal file
32
server/http/ipv4.inl
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_IPV4_INL
|
||||
#define MEMGRAPH_SERVER_HTTP_IPV4_INL
|
||||
|
||||
#include "ipv4.hpp"
|
||||
#include "http_error.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
Ipv4::Ipv4(const std::string& address, uint16_t port)
|
||||
: address(address), port(port)
|
||||
{
|
||||
auto status = uv_ip4_addr(address.c_str(), port, &socket_address);
|
||||
|
||||
if(status != 0)
|
||||
throw HttpError("Not a valid IP address/port (" + address + ":"
|
||||
+ std::to_string(port) + ")");
|
||||
}
|
||||
|
||||
Ipv4::operator const sockaddr_in&() const
|
||||
{
|
||||
return socket_address;
|
||||
}
|
||||
|
||||
Ipv4::operator const sockaddr*() const
|
||||
{
|
||||
return (const struct sockaddr*)&socket_address;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
20
server/http/method.hpp
Normal file
20
server/http/method.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_METHOD_HPP
|
||||
#define MEMGRAPH_SERVER_HTTP_METHOD_HPP
|
||||
|
||||
#include <http_parser.h>
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
enum Method
|
||||
{
|
||||
GET = HTTP_GET,
|
||||
HEAD = HTTP_HEAD,
|
||||
POST = HTTP_POST,
|
||||
PUT = HTTP_PUT,
|
||||
DELETE = HTTP_DELETE,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
25
server/http/request.hpp
Normal file
25
server/http/request.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef UVMACHINE_HTTP_REQUEST_HPP
|
||||
#define UVMACHINE_HTTP_REQUEST_HPP
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "version.hpp"
|
||||
#include "method.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
struct Request
|
||||
{
|
||||
Version version;
|
||||
Method method;
|
||||
|
||||
std::string url;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
35
server/http/response.hpp
Normal file
35
server/http/response.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_RESPONSE_HPP
|
||||
#define MEMGRAPH_SERVER_HTTP_RESPONSE_HPP
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "server/uv/uv.hpp"
|
||||
#include "status_codes.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
class HttpConnection;
|
||||
|
||||
class Response
|
||||
{
|
||||
public:
|
||||
Response(HttpConnection& 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 code;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
62
server/http/response.inl
Normal file
62
server/http/response.inl
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_RESPONSE_INL
|
||||
#define MEMGRAPH_SERVER_HTTP_RESPONSE_INL
|
||||
|
||||
#include "response.hpp"
|
||||
#include "httpconnection.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
Response::Response(HttpConnection& connection)
|
||||
: connection(connection), buffer(65536), code(Status::Ok) {}
|
||||
|
||||
void Response::send(Status code, const std::string& body)
|
||||
{
|
||||
this->status(code).send(body);
|
||||
}
|
||||
|
||||
void Response::send(const std::string& body)
|
||||
{
|
||||
uv_write_t* write_req =
|
||||
static_cast<uv_write_t*>(malloc(sizeof(uv_write_t)));
|
||||
|
||||
write_req->data = &connection;
|
||||
|
||||
// set the appropriate content length
|
||||
headers["Content-Length"] = std::to_string(body.size());
|
||||
|
||||
// set the appropriate connection type
|
||||
headers["Connection"] = connection.keep_alive ? "Keep-Alive" : "Close";
|
||||
|
||||
buffer << "HTTP/1.1 " << to_string[code] << "\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_t* write_req, int) {
|
||||
|
||||
HttpConnection& conn =
|
||||
*reinterpret_cast<HttpConnection*>(write_req->data);
|
||||
|
||||
if(!conn.keep_alive)
|
||||
conn.close();
|
||||
|
||||
conn.response.code = Status::Ok;
|
||||
conn.response.buffer.clear();
|
||||
conn.response.headers.clear();
|
||||
std::free(write_req);
|
||||
});
|
||||
}
|
||||
|
||||
Response& Response::status(Status code)
|
||||
{
|
||||
this->code = code;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
83
server/http/status_codes.hpp
Normal file
83
server/http/status_codes.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef MEMGRAPH_SERVER_HTTP_STATUS_CODES_HPP
|
||||
#define MEMGRAPH_SERVER_HTTP_STATUS_CODES_HPP
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
#define HTTP_STATUS_CODES \
|
||||
CODE(Continue, 100, CONTINUE) \
|
||||
CODE(SwitchingProtocols, 101, SWITCHING PROTOCOLS) \
|
||||
CODE(Processing, 102, PROCESSING) \
|
||||
CODE(Ok, 200, OK) \
|
||||
CODE(Created, 201, CREATED) \
|
||||
CODE(Accepted, 202, ACCEPTED) \
|
||||
CODE(NonAuthoritativeInformation, 203, NON AUTHORITATIVE INFORMATION) \
|
||||
CODE(NoContent, 204, NO CONTENT) \
|
||||
CODE(ResetContent, 205, RESET CONTENT) \
|
||||
CODE(PartialContent, 206, PARTIAL CONTENT) \
|
||||
CODE(MultiStatus, 207, MULTI STATUS) \
|
||||
CODE(MultipleChoices, 300, MULTIPLE CHOICES) \
|
||||
CODE(MovedPermanently, 301, MOVED PERMANENTLY) \
|
||||
CODE(MovedTemporarily, 302, MOVED TEMPORARILY) \
|
||||
CODE(SeeOther, 303, SEE OTHER) \
|
||||
CODE(NotModified, 304, NOT MODIFIED) \
|
||||
CODE(UseProxy, 305, USE PROXY) \
|
||||
CODE(TemporaryRedirect, 307, TEMPORARY REDIRECT) \
|
||||
CODE(BadRequest, 400, BAD REQUEST) \
|
||||
CODE(Unauthorized, 401, UNAUTHORIZED) \
|
||||
CODE(PaymentRequired, 402, PAYMENT REQUIRED) \
|
||||
CODE(Forbidden, 403, FORBIDDEN) \
|
||||
CODE(NotFound, 404, NOT FOUND) \
|
||||
CODE(MethodNotAllowed, 405, METHOD NOT ALLOWED) \
|
||||
CODE(NotAcceptable, 406, NOT ACCEPTABLE) \
|
||||
CODE(ProxyAuthenticationRequired, 407, PROXY AUTHENTICATION REQUIRED) \
|
||||
CODE(RequestTimeOut, 408, REQUEST TIMEOUT) \
|
||||
CODE(Conflict, 409, CONFLICT) \
|
||||
CODE(Gone, 410, GONE) \
|
||||
CODE(LengthRequired, 411, LENGTH REQUIRED) \
|
||||
CODE(PreconditionFailed, 412, PRECONDITION FAILED) \
|
||||
CODE(RequestEntityTooLarge, 413, REQUEST ENTITY TOO LARGE) \
|
||||
CODE(RequestUriTooLarge, 414, REQUEST URI TOO LARGE) \
|
||||
CODE(UnsupportedMediaType, 415, UNSUPPORTED MEDIA TYPE) \
|
||||
CODE(RequestedRangeNotSatisfiable, 416, REQUESTED RANGE NOT SATISFIABLE) \
|
||||
CODE(ExpectationFailed, 417, EXPECTATION FAILED) \
|
||||
CODE(ImATeapot, 418, IM A TEAPOT) \
|
||||
CODE(UnprocessableEntity, 422, UNPROCESSABLE ENTITY) \
|
||||
CODE(Locked, 423, LOCKED) \
|
||||
CODE(FailedDependency, 424, FAILED DEPENDENCY) \
|
||||
CODE(UnorderedCollection, 425, UNORDERED COLLECTION) \
|
||||
CODE(UpgradeRequired, 426, UPGRADE REQUIRED) \
|
||||
CODE(PreconditionRequired, 428, PRECONDITION REQUIRED) \
|
||||
CODE(TooManyRequests, 429, TOO MANY REQUESTS) \
|
||||
CODE(RequestHeaderFieldsTooLarge, 431, REQUEST HEADER FIELDS TOO LARGE) \
|
||||
CODE(InternalServerError, 500, INTERNAL SERVER ERROR) \
|
||||
CODE(NotImplemented, 501, NOT IMPLEMENTED) \
|
||||
CODE(BadGateway, 502, BAD GATEWAY) \
|
||||
CODE(ServiceUnavailable, 503, SERVICE UNAVAILABLE) \
|
||||
CODE(GatewayTimeOut, 504, GATEWAY TIME OUT) \
|
||||
CODE(HttpVersionNotSupported, 505, HTTP VERSION NOT SUPPORTED) \
|
||||
CODE(VariantAlsoNegotiates, 506, VARIANT ALSO NEGOTIATES) \
|
||||
CODE(InsufficientStorage, 507, INSUFFICIENT STORAGE) \
|
||||
CODE(BandwidthLimitExceeded, 508, BANDWIDTH LIMIT EXCEEDED) \
|
||||
CODE(NotExtended, 510, NOT EXTENDED) \
|
||||
CODE(NetworkAuthenticationRequired, 511, NETWORK AUTHENTICATION REQUIRED)
|
||||
|
||||
enum Status
|
||||
{
|
||||
#define CODE(a, b, c) a = b,
|
||||
HTTP_STATUS_CODES
|
||||
#undef CODE
|
||||
};
|
||||
|
||||
static std::map<Status, std::string> to_string = {
|
||||
#define CODE(a, b, c) { Status::a, #b " " #c },
|
||||
HTTP_STATUS_CODES
|
||||
#undef CODE
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
15
server/http/version.hpp
Normal file
15
server/http/version.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef UVMACHINE_HTTP_HTTPVERSION_HPP
|
||||
#define UVMACHINE_HTTP_HTTPVERSION_HPP
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
struct Version
|
||||
{
|
||||
unsigned short major;
|
||||
unsigned short minor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
19
server/test.cpp
Normal file
19
server/test.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "uvmachine.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uv::UvLoop loop;
|
||||
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;
|
||||
}
|
13
server/uv/core.hpp
Normal file
13
server/uv/core.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef MEMGRAPH_SERVER_UV_CORE_HPP
|
||||
#define MEMGRAPH_SERVER_UV_CORE_HPP
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
using callback_t = void (*)(uv_handle_t *);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
35
server/uv/tcpstream.hpp
Normal file
35
server/uv/tcpstream.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef MEMGRAPH_SERVER_UV_TCPSTREAM_HPP
|
||||
#define MEMGRAPH_SERVER_UV_TCPSTREAM_HPP
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include "core.hpp"
|
||||
#include "uvloop.hpp"
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
class TcpStream
|
||||
{
|
||||
public:
|
||||
TcpStream(UvLoop& loop);
|
||||
|
||||
template <typename T>
|
||||
T* data();
|
||||
|
||||
template <typename T>
|
||||
void data(T* value);
|
||||
|
||||
void close(callback_t callback);
|
||||
|
||||
operator uv_handle_t*();
|
||||
operator uv_tcp_t*();
|
||||
operator uv_stream_t*();
|
||||
|
||||
private:
|
||||
uv_tcp_t stream;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
48
server/uv/tcpstream.inl
Normal file
48
server/uv/tcpstream.inl
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef MEMGRAPH_SERVER_UV_TCPSTREAM_INL
|
||||
#define MEMGRAPH_SERVER_UV_TCPSTREAM_INL
|
||||
|
||||
#include "tcpstream.hpp"
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
TcpStream::TcpStream(UvLoop& loop)
|
||||
{
|
||||
uv_tcp_init(loop, &stream);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* TcpStream::data()
|
||||
{
|
||||
return reinterpret_cast<T*>(stream.data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TcpStream::data(T* value)
|
||||
{
|
||||
stream.data = reinterpret_cast<void*>(value);
|
||||
}
|
||||
|
||||
void TcpStream::close(callback_t callback)
|
||||
{
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&stream), callback);
|
||||
}
|
||||
|
||||
TcpStream::operator uv_tcp_t*()
|
||||
{
|
||||
return &stream;
|
||||
}
|
||||
|
||||
TcpStream::operator uv_stream_t*()
|
||||
{
|
||||
return reinterpret_cast<uv_stream_t*>(&stream);
|
||||
}
|
||||
|
||||
TcpStream::operator uv_handle_t*()
|
||||
{
|
||||
return reinterpret_cast<uv_handle_t*>(&stream);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
6
server/uv/uv.hpp
Normal file
6
server/uv/uv.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "tcpstream.hpp"
|
||||
#include "uvbuffer.hpp"
|
||||
#include "uvloop.hpp"
|
||||
|
||||
#include "tcpstream.inl"
|
||||
#include "uvbuffer.inl"
|
19
server/uv/uv_error.hpp
Normal file
19
server/uv/uv_error.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef MEMGRAPH_SERVER_UV_UV_ERROR_HPP
|
||||
#define MEMGRAPH_SERVER_UV_UV_ERROR_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
class UvError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
UvError(const std::string& message)
|
||||
: std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
38
server/uv/uvbuffer.hpp
Normal file
38
server/uv/uvbuffer.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef MEMGRAPH_SERVER_UV_UVBUFFER_HPP
|
||||
#define MEMGRAPH_SERVER_UV_UVBUFFER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <uv.h>
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
class UvBuffer
|
||||
{
|
||||
public:
|
||||
UvBuffer();
|
||||
UvBuffer(size_t capacity);
|
||||
UvBuffer(const std::string& data);
|
||||
|
||||
~UvBuffer();
|
||||
|
||||
size_t size() const noexcept;
|
||||
size_t length() const noexcept;
|
||||
|
||||
void clear();
|
||||
|
||||
UvBuffer& append(const std::string& data);
|
||||
UvBuffer& append(const char* data, size_t n);
|
||||
|
||||
UvBuffer& operator<<(const std::string& data);
|
||||
|
||||
operator uv_buf_t*();
|
||||
|
||||
private:
|
||||
uv_buf_t buffer;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
91
server/uv/uvbuffer.inl
Normal file
91
server/uv/uvbuffer.inl
Normal file
@ -0,0 +1,91 @@
|
||||
#ifndef MEMGRAPH_SERVER_UV_UVBUFFER_INL
|
||||
#define MEMGRAPH_SERVER_UV_UVBUFFER_INL
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "uvbuffer.hpp"
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
UvBuffer::UvBuffer()
|
||||
: capacity(0)
|
||||
{
|
||||
buffer.len = 0;
|
||||
buffer.base = nullptr;
|
||||
}
|
||||
|
||||
UvBuffer::UvBuffer(size_t capacity)
|
||||
: capacity(capacity)
|
||||
{
|
||||
buffer.len = 0;
|
||||
buffer.base = static_cast<char*>(malloc(capacity));
|
||||
}
|
||||
|
||||
UvBuffer::UvBuffer(const std::string& data)
|
||||
: capacity(data.size())
|
||||
{
|
||||
buffer.len = data.size();
|
||||
buffer.base = static_cast<char*>(malloc(capacity));
|
||||
|
||||
std::memcpy(buffer.base, data.c_str(), buffer.len);
|
||||
}
|
||||
|
||||
UvBuffer::~UvBuffer()
|
||||
{
|
||||
if(buffer.base == nullptr)
|
||||
return;
|
||||
|
||||
free(buffer.base);
|
||||
}
|
||||
|
||||
size_t UvBuffer::size() const noexcept
|
||||
{
|
||||
return buffer.len;
|
||||
}
|
||||
|
||||
size_t UvBuffer::length() const noexcept
|
||||
{
|
||||
return this->size();
|
||||
}
|
||||
|
||||
void UvBuffer::clear()
|
||||
{
|
||||
buffer.len = 0;
|
||||
}
|
||||
|
||||
UvBuffer& UvBuffer::append(const std::string& data)
|
||||
{
|
||||
return this->append(data.c_str(), data.size());
|
||||
}
|
||||
|
||||
UvBuffer& UvBuffer::append(const char* data, size_t n)
|
||||
{
|
||||
auto new_size = size() + n;
|
||||
|
||||
if(capacity < new_size)
|
||||
{
|
||||
capacity = new_size;
|
||||
buffer.base = static_cast<char*>(realloc(buffer.base, new_size));
|
||||
}
|
||||
|
||||
std::memcpy(buffer.base + size(), data, n);
|
||||
buffer.len = new_size;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
UvBuffer& UvBuffer::operator<<(const std::string& data)
|
||||
{
|
||||
return this->append(data);
|
||||
}
|
||||
|
||||
UvBuffer::operator uv_buf_t*()
|
||||
{
|
||||
return &buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
60
server/uv/uvloop.hpp
Normal file
60
server/uv/uvloop.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef MEMGRAPH_SERVER_UV_UVLOOP_HPP
|
||||
#define MEMGRAPH_SERVER_UV_UVLOOP_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <uv.h>
|
||||
|
||||
#include "uv_error.hpp"
|
||||
|
||||
namespace uv
|
||||
{
|
||||
|
||||
class UvLoop final
|
||||
{
|
||||
public:
|
||||
enum Mode {
|
||||
Default = UV_RUN_DEFAULT,
|
||||
Once = UV_RUN_ONCE,
|
||||
NoWait = UV_RUN_NOWAIT
|
||||
};
|
||||
|
||||
UvLoop()
|
||||
{
|
||||
uv_loop = uv_default_loop();
|
||||
|
||||
if(uv_loop == nullptr)
|
||||
throw UvError("Failed to initialize libuv event loop");
|
||||
}
|
||||
|
||||
~UvLoop()
|
||||
{
|
||||
uv_loop_close(uv_loop);
|
||||
}
|
||||
|
||||
bool run(Mode mode)
|
||||
{
|
||||
return uv_run(uv_loop, static_cast<uv_run_mode>(mode));
|
||||
}
|
||||
|
||||
bool alive()
|
||||
{
|
||||
return uv_loop_alive(uv_loop);
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
uv_stop(uv_loop);
|
||||
}
|
||||
|
||||
operator uv_loop_t*()
|
||||
{
|
||||
return uv_loop;
|
||||
}
|
||||
|
||||
private:
|
||||
uv_loop_t* uv_loop;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
2
server/uvmachine.hpp
Normal file
2
server/uvmachine.hpp
Normal file
@ -0,0 +1,2 @@
|
||||
#include "uv/uv.hpp"
|
||||
#include "http/http.hpp"
|
@ -7,11 +7,11 @@
|
||||
|
||||
struct Vertex;
|
||||
|
||||
struct
|
||||
|
||||
struct Edge : public Record<Edge>
|
||||
{
|
||||
Edge(uint64_t id) : Record<Edge>(id) {}
|
||||
|
||||
// pointer to the vertex this edge points to
|
||||
Vertex* from;
|
||||
Vertex* to;
|
||||
};
|
||||
|
||||
|
51
storage/model/properties/properties.hpp
Normal file
51
storage/model/properties/properties.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef MEMGRAPH_STORAGE_MODEL_PROPERTIES_PROPERTIES_HPP
|
||||
#define MEMGRAPH_STORAGE_MODEL_PROPERTIES_PROPERTIES_HPP
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "property.hpp"
|
||||
|
||||
class Properties
|
||||
{
|
||||
using props_t = std::map<std::string, Property::sptr>;
|
||||
|
||||
public:
|
||||
props_t::iterator find(const std::string& key)
|
||||
{
|
||||
return props.find(key);
|
||||
}
|
||||
|
||||
Property* at(const std::string& key)
|
||||
{
|
||||
return props.at(key).get();
|
||||
}
|
||||
|
||||
void put(const std::string& key, Property::sptr value)
|
||||
{
|
||||
props[key] = std::move(value);
|
||||
}
|
||||
|
||||
void clear(const std::string& key)
|
||||
{
|
||||
props.erase(key);
|
||||
}
|
||||
|
||||
void dump(std::string& buffer)
|
||||
{
|
||||
buffer += '{';
|
||||
|
||||
for(auto& kvp : props)
|
||||
{
|
||||
buffer += '"'; buffer += kvp.first; buffer += "\":";
|
||||
kvp.second->dump(buffer); buffer += ',';
|
||||
}
|
||||
|
||||
buffer.pop_back(); // erase last comma
|
||||
buffer += '}';
|
||||
}
|
||||
|
||||
private:
|
||||
props_t props;
|
||||
};
|
||||
|
||||
#endif
|
30
storage/model/properties/property.hpp
Normal file
30
storage/model/properties/property.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef MEMGRAPH_STORAGE_MODEL_PROPERTIES_PROPERTY_HPP
|
||||
#define MEMGRAPH_STORAGE_MODEL_PROPERTIES_PROPERTY_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Property
|
||||
{
|
||||
public:
|
||||
// shared_ptr is being used because of MVCC - when you clone a record, you
|
||||
// clone it's properties. when a single property is updated, a lot of
|
||||
// memory is being wasted. this way it is shared until you need to change
|
||||
// something and shared ptr ensures it's being properly tracked and
|
||||
// cleaned up after no one is using it
|
||||
|
||||
using sptr = std::shared_ptr<Property>;
|
||||
|
||||
virtual ~Property() {}
|
||||
virtual void dump(std::string& buffer) = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class Value : public Property
|
||||
{
|
||||
public:
|
||||
Value(T value) : value(value) {}
|
||||
T value;
|
||||
};
|
||||
|
||||
#endif
|
17
storage/model/properties/string.hpp
Normal file
17
storage/model/properties/string.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef MEMGRAPH_STORAGE_MODEL_PROPERTIES_STRING_HPP
|
||||
#define MEMGRAPH_STORAGE_MODEL_PROPERTIES_STRING_HPP
|
||||
|
||||
#include "property.hpp"
|
||||
|
||||
class String : public Value<std::string>
|
||||
{
|
||||
public:
|
||||
using Value::Value;
|
||||
|
||||
virtual void dump(std::string& buffer)
|
||||
{
|
||||
buffer += '"'; buffer += value; buffer += '"';
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "storage/model/utils/mvcc.hpp"
|
||||
|
||||
#include "properties/properties.hpp"
|
||||
|
||||
template <class Derived>
|
||||
class Record
|
||||
: public Crtp<Derived>,
|
||||
@ -17,15 +19,7 @@ class Record
|
||||
Lockable<SpinLock>
|
||||
{
|
||||
public:
|
||||
Record(uint64_t id) : id(id) {}
|
||||
|
||||
// every node has a unique id. 2^64 = 1.8 x 10^19. that should be enough
|
||||
// for a looong time :) but keep in mind that some vacuuming would be nice
|
||||
// to reuse indices for deleted nodes.
|
||||
uint64_t id;
|
||||
|
||||
private:
|
||||
// TODO add real data here
|
||||
Properties props;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
19
storage/model/root.hpp
Normal file
19
storage/model/root.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef MEMGRAPH_STORAGE_MODEL_ROOT_HPP
|
||||
#define MEMGRAPH_STORAGE_MODEL_ROOT_HPP
|
||||
|
||||
#include "utils/version.hpp"
|
||||
|
||||
template <class T>
|
||||
class Root : public Version<T>
|
||||
{
|
||||
public:
|
||||
Root(uint64_t id, T* first)
|
||||
: Version<T>(first), id(id) {}
|
||||
|
||||
// every record has a unique id. 2^64 = 1.8 x 10^19. that should be enough
|
||||
// for a looong time :) but keep in mind that some vacuuming would be nice
|
||||
// to reuse indices for deleted nodes.
|
||||
uint64_t id;
|
||||
};
|
||||
|
||||
#endif
|
@ -8,9 +8,7 @@
|
||||
|
||||
struct Vertex : public Record<Vertex>
|
||||
{
|
||||
Vertex(uint64_t id) : Record<Vertex>(id) {}
|
||||
|
||||
// adjacency list containing pointers to outgoing edges from this vertex
|
||||
std::vector<Edge*> in;
|
||||
std::vector<Edge*> out;
|
||||
};
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "transaction/transaction.hpp"
|
||||
#include "storage/model/record.hpp"
|
||||
#include "storage/visible.hpp"
|
||||
#include "memory/memory_engine.hpp"
|
||||
#include "utils/counters/atomic_counter.hpp"
|
||||
|
||||
|
109
threading/pool.hpp
Normal file
109
threading/pool.hpp
Normal file
@ -0,0 +1,109 @@
|
||||
#ifndef MEMGRAPH_THREADING_POOL_HPP
|
||||
#define MEMGRAPH_THREADING_POOL_HPP
|
||||
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
|
||||
#include "data_structures/queue/slqueue.hpp"
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "worker.hpp"
|
||||
#include "data_structures/queue/slqueue.hpp"
|
||||
|
||||
class Pool : Lockable<std::mutex>
|
||||
{
|
||||
using task_t = std::function<void()>;
|
||||
|
||||
public:
|
||||
Pool(size_t n = std::thread::hardware_concurrency())
|
||||
: alive(true)
|
||||
{
|
||||
start(n);
|
||||
}
|
||||
|
||||
~Pool()
|
||||
{
|
||||
alive.store(false, std::memory_order_release);
|
||||
cond.notify_all();
|
||||
|
||||
for(auto& worker : workers)
|
||||
worker.join();
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
return workers.size();
|
||||
}
|
||||
|
||||
template <class F, class... Args>
|
||||
void execute(F&& f, Args&&... args)
|
||||
{
|
||||
{
|
||||
auto guard = acquire();
|
||||
tasks.emplace([f, args...]() { f(args...); });
|
||||
}
|
||||
|
||||
cond.notify_one();
|
||||
}
|
||||
|
||||
template <class F, class... Args>
|
||||
auto execute_with_result(F&& f, Args&&... args)
|
||||
-> std::future<typename std::result_of<F(Args...)>::type>
|
||||
{
|
||||
using ret_t = typename std::result_of<F(Args...)>::type;
|
||||
|
||||
auto task = std::make_shared<std::packaged_task<ret_t()>>
|
||||
(std::bind(f, args...));
|
||||
|
||||
auto result = task->get_future();
|
||||
|
||||
{
|
||||
auto guard = acquire();
|
||||
tasks.emplace([task]() { (*task)(); });
|
||||
}
|
||||
|
||||
cond.notify_one();
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
Pool(const Pool&) = delete;
|
||||
Pool(Pool&&) = delete;
|
||||
|
||||
std::vector<Worker<task_t>> workers;
|
||||
spinlock::Queue<task_t> tasks;
|
||||
|
||||
std::atomic<bool> alive;
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable cond;
|
||||
|
||||
void loop()
|
||||
{
|
||||
task_t task;
|
||||
|
||||
while(true)
|
||||
{
|
||||
while(tasks.pop(task))
|
||||
task();
|
||||
|
||||
auto guard = acquire();
|
||||
|
||||
cond.wait(guard, [this] {
|
||||
return !this->alive || !this->tasks.empty();
|
||||
});
|
||||
|
||||
if(!alive && tasks.empty())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void start(size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
workers.emplace_back([this]()->void { this->loop(); });
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
12
threading/task.hpp
Normal file
12
threading/task.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef MEMGRAPH_THREADING_TASK_HPP
|
||||
#define MEMGRAPH_THREADING_TASK_HPP
|
||||
|
||||
class Task
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
//std::function<
|
||||
};
|
||||
|
||||
#endif
|
BIN
threading/test
Executable file
BIN
threading/test
Executable file
Binary file not shown.
20
threading/test.cpp
Normal file
20
threading/test.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "pool.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
std::cout << "hardware_concurrency " << std::thread::hardware_concurrency() << std::endl;
|
||||
|
||||
auto size = 2048;
|
||||
auto N = 1000000;
|
||||
|
||||
Pool pool(size);
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
pool.execute([size](int) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}, i);
|
||||
|
||||
return 0;
|
||||
}
|
22
threading/worker.hpp
Normal file
22
threading/worker.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef MEMGRAPH_THREADING_WORKER_HPP
|
||||
#define MEMGRAPH_THREADING_WORKER_HPP
|
||||
|
||||
#include <thread>
|
||||
|
||||
template <class F, class... Args>
|
||||
class Worker
|
||||
{
|
||||
public:
|
||||
Worker(F&& f, Args&&... args)
|
||||
: thread(f, args...) {}
|
||||
|
||||
void join()
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
|
||||
private:
|
||||
std::thread thread;
|
||||
};
|
||||
|
||||
#endif
|
@ -5,10 +5,8 @@
|
||||
// curiously recurring template pattern
|
||||
|
||||
template <class Derived>
|
||||
class Crtp
|
||||
struct Crtp
|
||||
{
|
||||
public:
|
||||
|
||||
Derived& derived()
|
||||
{
|
||||
return *static_cast<Derived*>(this);
|
||||
|
11
utils/visitor/visitable.hpp
Normal file
11
utils/visitor/visitable.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef MEMGRAPH_UTILS_VISITOR_VISITABLE_HPP
|
||||
#define MEMGRAPH_UTILS_VISITOR_VISITABLE_HPP
|
||||
|
||||
template <class T>
|
||||
struct Visitable
|
||||
{
|
||||
virtual ~Visitable() {}
|
||||
virtual void accept(T& visitor) = 0;
|
||||
};
|
||||
|
||||
#endif
|
47
utils/visitor/visitor.hpp
Normal file
47
utils/visitor/visitor.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef MEMGRAPH_UTILS_VISITOR_VISITOR_HPP
|
||||
#define MEMGRAPH_UTILS_VISITOR_VISITOR_HPP
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct VisitorBase {
|
||||
virtual ~VisitorBase() {}
|
||||
|
||||
virtual void visit(T&) {}
|
||||
virtual void post_visit(T&) {}
|
||||
};
|
||||
|
||||
template<typename... T>
|
||||
struct RecursiveVisitorBase;
|
||||
|
||||
template <typename Head, typename... Tail>
|
||||
struct RecursiveVisitorBase<Head, Tail...>
|
||||
: VisitorBase<Head>, RecursiveVisitorBase<Tail...>
|
||||
{
|
||||
using VisitorBase<Head>::visit;
|
||||
using VisitorBase<Head>::post_visit;
|
||||
|
||||
using RecursiveVisitorBase<Tail...>::visit;
|
||||
using RecursiveVisitorBase<Tail...>::post_visit;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct RecursiveVisitorBase<T>
|
||||
: public VisitorBase<T>
|
||||
{
|
||||
using VisitorBase<T>::visit;
|
||||
using VisitorBase<T>::post_visit;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
struct Visitor
|
||||
: public detail::RecursiveVisitorBase<T...>
|
||||
{
|
||||
using detail::RecursiveVisitorBase<T...>::visit;
|
||||
using detail::RecursiveVisitorBase<T...>::post_visit;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user