clean up and refactor half-baked development commits and squash them into a new version.
Changes: * support for multiple GTFS feeds as input in filtering, read default global and local configuration files * be a bit more memory conservative * make caching optional * dont delete orphan edge if it would transform a degree 3 node with a possible full turn into a degree 2 node eligible for contraction * dedicated filters for funicular and gondola * make max snap level option more intuitive * allow filter rules for level 0 * additional fallback for station snapping * dont try to route for MOT unequal to trip in -T mode, force-snap to orphaned OSM station if not snap was possible * write bounds to filtered osm * remove unused surrounding heuristic * use bus lanes info * be a bit more tolerant for bus oneway streets * create missing directories * error if no cfg is present, clean up evaluation Makefile
This commit is contained in:
parent
2cc2d2dc23
commit
63f0b61ea1
60 changed files with 4532 additions and 1576 deletions
|
|
@ -2,4 +2,11 @@ file(GLOB_RECURSE util_SRC *.cpp)
|
|||
list(REMOVE_ITEM util_SRC TestMain.cpp)
|
||||
add_library(util ${util_SRC})
|
||||
|
||||
find_package( ZLIB )
|
||||
if (ZLIB_FOUND)
|
||||
include_directories( ${ZLIB_INCLUDE_DIRS} )
|
||||
target_link_libraries( util ${ZLIB_LIBRARIES} )
|
||||
add_definitions( -DZLIB_FOUND=${ZLIB_FOUND} )
|
||||
endif( ZLIB_FOUND )
|
||||
|
||||
add_subdirectory(tests)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class Nullable {
|
|||
: val(other.val), null(other.isNull()) {}
|
||||
|
||||
Nullable& operator=(const Nullable& other) {
|
||||
val = other.get();
|
||||
if (!other.isNull()) val = other.get();
|
||||
null = other.isNull();
|
||||
return *this;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,6 +142,39 @@ inline size_t editDist(const std::string& s1, const std::string& s2) {
|
|||
return prev[len2];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline size_t prefixEditDist(const std::string& prefix, const std::string& s,
|
||||
size_t deltaMax) {
|
||||
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
|
||||
size_t len1 = prefix.size();
|
||||
size_t len2 = std::min(s.size(), prefix.size() + deltaMax + 1);
|
||||
std::vector<size_t> d((len1 + 1) * (len2 + 1));
|
||||
|
||||
d[0] = 0;
|
||||
for (size_t i = 1; i <= len1; ++i) d[i * (len2 + 1)] = i;
|
||||
for (size_t i = 1; i <= len2; ++i) d[ i] = i;
|
||||
|
||||
for (size_t i = 1; i <= len1; i++) {
|
||||
for (size_t j = 1; j <= len2; j++) {
|
||||
d[i * (len2 + 1) + j] = std::min(std::min(d[(i - 1) * (len2 + 1) + j] + 1, d[i * (len2 + 1) + j - 1] + 1),
|
||||
d[(i - 1) * (len2 + 1) + j - 1] + (prefix[i - 1] == s[j - 1] ? 0 : 1));
|
||||
}
|
||||
}
|
||||
|
||||
// take min of last row
|
||||
size_t deltaMin = std::max(std::max(deltaMax + 1, prefix.size()), s.size());
|
||||
for (size_t i = 0; i <= len2; i++) {
|
||||
if (d[len1 * (len2 + 1) + i] < deltaMin) deltaMin = d[len1 * (len2 + 1) + i];
|
||||
}
|
||||
|
||||
return deltaMin;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline size_t prefixEditDist(const std::string& prefix, const std::string& s) {
|
||||
return prefixEditDist(prefix, s, s.size());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <class Iter>
|
||||
inline std::string implode(Iter begin, const Iter& end, const char* del) {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ typedef Polygon<double> DPolygon;
|
|||
typedef Polygon<float> FPolygon;
|
||||
typedef Polygon<int> IPolygon;
|
||||
|
||||
const static double EPSILON = 0.00000000001;
|
||||
const static double EPSILON = 0.00001;
|
||||
const static double RAD = 0.017453292519943295; // PI/180
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
|
@ -236,7 +236,7 @@ inline RotatedBox<T> shrink(const RotatedBox<T>& b, double d) {
|
|||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline bool doubleEq(double a, double b) { return fabs(a - b) < 0.000001; }
|
||||
inline bool doubleEq(double a, double b) { return fabs(a - b) < EPSILON; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
|
|
@ -404,7 +404,7 @@ inline bool intersects(const LineSegment<T>& ls1, const LineSegment<T>& ls2) {
|
|||
// intersecting
|
||||
return intersects(getBoundingBox(ls1), getBoundingBox(ls2)) &&
|
||||
(((contains(ls1.first, ls2) ^ contains(ls1.second, ls2)) ^
|
||||
(contains(ls2.first, ls1) ^ contains(ls2.second, ls1))) ||
|
||||
(contains(ls2.first, ls1) ^ contains(ls2.second, ls1))) ||
|
||||
(((crossProd(ls1.first, ls2) < 0) ^
|
||||
(crossProd(ls1.second, ls2) < 0)) &&
|
||||
((crossProd(ls2.first, ls1) < 0) ^
|
||||
|
|
@ -1153,7 +1153,7 @@ inline size_t convexHullImpl(const MultiPoint<T>& a, size_t p1, size_t p2,
|
|||
for (const auto& p : a) {
|
||||
double tmpDist = distToSegment((*h)[p1], (*h)[p2], p);
|
||||
double cp = crossProd(p, LineSegment<T>((*h)[p1], (*h)[p2]));
|
||||
if (((cp > 0 && !d) || (cp < 0 && d)) && tmpDist > maxDist) {
|
||||
if (((cp > 0 && !d) || (cp < 0 && d)) && tmpDist >= maxDist + EPSILON) {
|
||||
pa = p;
|
||||
found = true;
|
||||
maxDist = tmpDist;
|
||||
|
|
@ -1462,7 +1462,8 @@ inline Point<T> latLngToWebMerc(double lat, double lng) {
|
|||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> webMercToLatLng(double x, double y) {
|
||||
double lat = 114.591559026 * (atan(exp(y / 6378137.0)) - 0.78539825);
|
||||
double lat =
|
||||
(1.5707963267948966 - (2.0 * atan(exp(-y / 6378137.0)))) * (180.0 / M_PI);
|
||||
double lon = x / 111319.4907932735677;
|
||||
return Point<T>(lon, lat);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#ifndef UTIL_GEO_POINT_H_
|
||||
#define UTIL_GEO_POINT_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace util {
|
||||
namespace geo {
|
||||
|
||||
|
|
|
|||
33
src/util/graph/Algorithm.h
Normal file
33
src/util/graph/Algorithm.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_ALGORITHM_H_
|
||||
#define UTIL_GRAPH_ALGORITHM_H_
|
||||
|
||||
#include <stack>
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/UndirGraph.h"
|
||||
#include "util/graph/Node.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
using util::graph::Graph;
|
||||
using util::graph::Node;
|
||||
using util::graph::Edge;
|
||||
|
||||
// collection of general graph algorithms
|
||||
class Algorithm {
|
||||
public:
|
||||
template <typename N, typename E>
|
||||
static std::vector<std::set<Node<N, E>*> > connectedComponents(
|
||||
const UndirGraph<N, E>& g);
|
||||
};
|
||||
|
||||
#include "util/graph/Algorithm.tpp"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GRAPH_ALGORITHM_H_
|
||||
32
src/util/graph/Algorithm.tpp
Normal file
32
src/util/graph/Algorithm.tpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
std::vector<std::set<Node<N, E>*> > Algorithm::connectedComponents(
|
||||
const UndirGraph<N, E>& g) {
|
||||
std::vector<std::set<Node<N, E>*>> ret;
|
||||
std::set<Node<N, E>*> visited;
|
||||
|
||||
for (auto* n : g.getNds()) {
|
||||
if (!visited.count(n)) {
|
||||
ret.resize(ret.size() + 1);
|
||||
std::stack<Node<N, E>*> q;
|
||||
q.push(n);
|
||||
while (!q.empty()) {
|
||||
Node<N, E>* cur = q.top();
|
||||
q.pop();
|
||||
|
||||
ret.back().insert(cur);
|
||||
visited.insert(cur);
|
||||
|
||||
for (auto* e : cur->getAdjList()) {
|
||||
if (!visited.count(e->getOtherNd(cur))) q.push(e->getOtherNd(cur));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#define UTIL_GRAPH_DIRNODE_H_
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "util/graph/Node.h"
|
||||
|
||||
namespace util {
|
||||
|
|
|
|||
|
|
@ -33,12 +33,15 @@ class Node {
|
|||
virtual void addEdge(Edge<N, E>* e) = 0;
|
||||
virtual void removeEdge(Edge<N, E>* e) = 0;
|
||||
|
||||
virtual ~Node() {};
|
||||
virtual ~Node() = 0;
|
||||
|
||||
virtual N& pl() = 0;
|
||||
virtual const N& pl() const = 0;
|
||||
};
|
||||
|
||||
template <typename N, typename E>
|
||||
inline Node<N, E>::~Node() {}
|
||||
|
||||
}}
|
||||
|
||||
#endif // UTIL_GRAPH_NODE_H_
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#define UTIL_GRAPH_UNDIRNODE_H_
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "util/graph/Node.h"
|
||||
|
||||
namespace util {
|
||||
|
|
|
|||
347
src/util/http/Server.cpp
Normal file
347
src/util/http/Server.cpp
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef ZLIB_CONST
|
||||
#define ZLIB_CONST
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#ifdef ZLIB_FOUND
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
#include <vector>
|
||||
#include "Server.h"
|
||||
#include "util/String.h"
|
||||
|
||||
using util::http::Socket;
|
||||
using util::http::Queue;
|
||||
using util::http::Req;
|
||||
using util::http::HttpErr;
|
||||
using util::http::HttpServer;
|
||||
using util::http::HeaderState;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
Socket::Socket(int port) {
|
||||
int y = 1;
|
||||
_sock = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (_sock < 0)
|
||||
throw std::runtime_error(std::string("Could not create socket (") +
|
||||
std::strerror(errno) + ")");
|
||||
|
||||
struct sockaddr_in addr;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
memset(&(addr.sin_zero), '\0', 8);
|
||||
|
||||
setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y));
|
||||
// https://news.ycombinator.com/item?id=10608356
|
||||
setsockopt(_sock, IPPROTO_TCP, TCP_QUICKACK, &y, sizeof(y));
|
||||
|
||||
if (bind(_sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
|
||||
throw std::runtime_error(std::string("Could not bind to port ") +
|
||||
std::to_string(port) + " (" +
|
||||
std::strerror(errno) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
Socket::~Socket() { close(_sock); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
int Socket::wait() {
|
||||
if (listen(_sock, BLOG) < 0)
|
||||
throw std::runtime_error(std::string("Cannot listen to socket (") +
|
||||
std::strerror(errno) + ")");
|
||||
sockaddr_in cli_addr;
|
||||
socklen_t clilen = sizeof(cli_addr);
|
||||
int sock = accept(_sock, reinterpret_cast<sockaddr*>(&cli_addr), &clilen);
|
||||
return sock;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void HttpServer::send(int sock, Answer* aw) {
|
||||
std::string enc = "identity";
|
||||
if (aw->gzip) aw->pl = compress(aw->pl, &enc);
|
||||
|
||||
aw->params["Content-Encoding"] = enc;
|
||||
aw->params["Content-Length"] = std::to_string(aw->pl.size());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "HTTP/1.1 " << aw->status << "\r\n";
|
||||
for (const auto& kv : aw->params)
|
||||
ss << kv.first << ": " << kv.second << "\r\n";
|
||||
ss << "\r\n" << aw->pl;
|
||||
std::string buff = ss.str();
|
||||
|
||||
size_t writes = 0;
|
||||
// https://news.ycombinator.com/item?id=10608356
|
||||
int y = 1;
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, &y, sizeof(y));
|
||||
|
||||
while (writes != buff.size()) {
|
||||
int64_t out = write(sock, buff.c_str() + writes, buff.size() - writes);
|
||||
if (out < 0) {
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) continue;
|
||||
throw std::runtime_error("Failed to write to socket");
|
||||
}
|
||||
writes += out;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void HttpServer::handle() {
|
||||
int connection = -1;
|
||||
while ((connection = _jobs.get()) != -1) {
|
||||
Answer answ;
|
||||
|
||||
try {
|
||||
Req req = getReq(connection);
|
||||
answ = _handler->handle(req, connection);
|
||||
answ.gzip = gzipSupport(req);
|
||||
} catch (HttpErr err) {
|
||||
answ = Answer{err.what(), err.what(), false, {}};
|
||||
} catch (...) {
|
||||
// catch everything to make sure the server continues running
|
||||
answ = Answer{
|
||||
"500 Internal Server Error", "500 Internal Server Error", false, {}};
|
||||
}
|
||||
|
||||
send(connection, &answ);
|
||||
close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool HttpServer::gzipSupport(const Req& req) {
|
||||
bool accepts = false;
|
||||
// decide according to
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||
for (const auto& kv : req.params) {
|
||||
if (kv.first == "Accept-Encoding") {
|
||||
for (const auto& encoding : split(kv.second, ',')) {
|
||||
std::vector<std::string> parts = split(encoding, ';');
|
||||
for (size_t i = 0; i < parts.size(); i++) {
|
||||
parts[i] = trim(parts[i]);
|
||||
}
|
||||
if (parts[0] == "*" && ((parts.size() == 1) || parts[1] != "q=0"))
|
||||
accepts = true;
|
||||
if (parts[0] == "gzip") accepts = true;
|
||||
if (parts.size() > 1 && parts[1] == "q=0") accepts = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return accepts;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
Req HttpServer::getReq(int connection) {
|
||||
char buf[BSIZE + 1];
|
||||
size_t rcvd = 0;
|
||||
int64_t curRcvd = 0;
|
||||
HeaderState state = NONE;
|
||||
Req ret{"", "", "", "", {}};
|
||||
char *tmp, *tmp2;
|
||||
char* brk = 0;
|
||||
|
||||
while ((curRcvd = read(connection, buf + rcvd, BSIZE - rcvd))) {
|
||||
if (curRcvd < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR) continue;
|
||||
throw HttpErr("500 Internal Server Error");
|
||||
}
|
||||
|
||||
// parse request
|
||||
for (int i = 0; i < curRcvd; i++) {
|
||||
if (brk) break;
|
||||
char* c = buf + rcvd + i;
|
||||
switch (state) {
|
||||
case NONE:
|
||||
state = I_COM;
|
||||
tmp = c;
|
||||
continue;
|
||||
case I_VER:
|
||||
if (*c == '\n') {
|
||||
*c = 0;
|
||||
ret.ver = trim(tmp);
|
||||
state = A_KEY;
|
||||
}
|
||||
continue;
|
||||
case I_URL:
|
||||
if (*c == ' ') {
|
||||
*c = 0, ret.url = trim(tmp);
|
||||
tmp = c + 1;
|
||||
state = I_VER;
|
||||
} else if (*c == '\n') {
|
||||
*c = 0, ret.url = trim(tmp);
|
||||
state = A_KEY;
|
||||
}
|
||||
continue;
|
||||
case I_COM:
|
||||
if (*c == ' ') {
|
||||
*c = 0, ret.cmd = trim(tmp);
|
||||
tmp = c + 1;
|
||||
state = I_URL;
|
||||
} else if (*c == '\n') {
|
||||
*c = 0, ret.cmd = trim(tmp);
|
||||
state = A_KEY;
|
||||
}
|
||||
continue;
|
||||
case A_KEY:
|
||||
if (*c == '\r') *c = ' ';
|
||||
if (*c == '\n')
|
||||
brk = c + 1;
|
||||
else if (*c != ' ') {
|
||||
state = I_KEY;
|
||||
tmp = c;
|
||||
}
|
||||
continue;
|
||||
case I_KEY:
|
||||
if (*c == ':') {
|
||||
*c = 0;
|
||||
state = A_VAL;
|
||||
}
|
||||
continue;
|
||||
case A_VAL:
|
||||
if (*c != ' ') {
|
||||
state = I_VAL;
|
||||
tmp2 = c;
|
||||
}
|
||||
continue;
|
||||
case I_VAL:
|
||||
if (*c == '\r') *c = ' ';
|
||||
if (*c == '\n') {
|
||||
*c = 0;
|
||||
ret.params[tmp] = trim(tmp2);
|
||||
state = A_KEY;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
rcvd += curRcvd;
|
||||
|
||||
// buffer is full
|
||||
if (rcvd == BSIZE) throw HttpErr("431 Request Header Fields Too Large");
|
||||
if (brk) break;
|
||||
}
|
||||
|
||||
// POST payload
|
||||
if (ret.cmd == "POST") {
|
||||
size_t size = 0;
|
||||
if (ret.params.count("Content-Length"))
|
||||
size = atoi(ret.params["Content-Length"].c_str());
|
||||
if (size) {
|
||||
char* postBuf = new char[size + 1];
|
||||
postBuf[size] = 0;
|
||||
size_t rem = 0;
|
||||
|
||||
// copy existing to new buffer
|
||||
if ((int)rcvd > brk - buf) {
|
||||
rem = std::min(size, rcvd - (brk - buf));
|
||||
memcpy(postBuf, brk, rem);
|
||||
}
|
||||
|
||||
rcvd = 0;
|
||||
|
||||
if (rem < size) {
|
||||
while ((curRcvd = read(connection, postBuf + rcvd + rem, size - rem))) {
|
||||
if (curRcvd == -1 && (errno == EAGAIN || errno == EINTR)) continue;
|
||||
if (curRcvd == -1) {
|
||||
postBuf[rcvd + 1] = 0;
|
||||
break;
|
||||
}
|
||||
rcvd += curRcvd;
|
||||
if (rcvd == size - rem) break;
|
||||
}
|
||||
}
|
||||
|
||||
ret.payload = postBuf;
|
||||
delete[] postBuf;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::string HttpServer::compress(const std::string& str, std::string* enc) {
|
||||
#ifdef ZLIB_FOUND
|
||||
// do not compress small payloads
|
||||
if (str.size() < 500) return str;
|
||||
|
||||
std::string ret;
|
||||
|
||||
// based on http://www.zlib.net/zlib_how.html
|
||||
z_stream defStr;
|
||||
defStr.zalloc = Z_NULL;
|
||||
defStr.zfree = Z_NULL;
|
||||
defStr.opaque = Z_NULL;
|
||||
defStr.avail_in = 0;
|
||||
defStr.next_in = Z_NULL;
|
||||
|
||||
// fail silently with no compression at all
|
||||
if (deflateInit2(&defStr, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8,
|
||||
Z_DEFAULT_STRATEGY) != Z_OK)
|
||||
return str;
|
||||
|
||||
defStr.next_in = reinterpret_cast<z_const Bytef*>(str.c_str());
|
||||
defStr.avail_in = static_cast<unsigned int>(str.size());
|
||||
|
||||
size_t cSize = 0;
|
||||
do {
|
||||
if (ret.size() < (cSize + BSIZE_C)) ret.resize(cSize + BSIZE_C);
|
||||
defStr.avail_out = BSIZE_C;
|
||||
defStr.next_out = reinterpret_cast<Bytef*>(&ret[0] + cSize);
|
||||
deflate(&defStr, Z_FINISH);
|
||||
cSize += BSIZE_C - defStr.avail_out;
|
||||
} while (defStr.avail_out == 0);
|
||||
|
||||
deflateEnd(&defStr);
|
||||
ret.resize(cSize);
|
||||
|
||||
if (ret.size() > str.size()) return str;
|
||||
*enc = "gzip";
|
||||
return ret;
|
||||
#else
|
||||
return str;
|
||||
#endif
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void HttpServer::run() {
|
||||
Socket socket(_port);
|
||||
|
||||
std::vector<std::thread> thrds(_threads);
|
||||
for (auto& thr : thrds) thr = std::thread(&HttpServer::handle, this);
|
||||
|
||||
while (1) _jobs.add(socket.wait());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Queue::add(int c) {
|
||||
if (c < 0) return;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mut);
|
||||
_jobs.push(c);
|
||||
}
|
||||
_hasNew.notify_one();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
int Queue::get() {
|
||||
std::unique_lock<std::mutex> lock(_mut);
|
||||
while (_jobs.empty()) _hasNew.wait(lock);
|
||||
int next = _jobs.front();
|
||||
_jobs.pop();
|
||||
return next;
|
||||
}
|
||||
134
src/util/http/Server.h
Normal file
134
src/util/http/Server.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#ifndef UTIL_HTTP_SERVER_H_
|
||||
#define UTIL_HTTP_SERVER_H_
|
||||
|
||||
namespace util {
|
||||
namespace http {
|
||||
|
||||
// socket backlog size
|
||||
const static size_t BLOG = 128;
|
||||
// socket read buffer size
|
||||
const static size_t BSIZE = 4 * 1024;
|
||||
// zlib compression buffer size
|
||||
const size_t BSIZE_C = 128 * 1024;
|
||||
|
||||
// states for HTTP header parser
|
||||
enum HeaderState { NONE, I_COM, I_URL, I_VER, A_KEY, I_KEY, A_VAL, I_VAL };
|
||||
|
||||
/*
|
||||
* HTTP Error
|
||||
*/
|
||||
class HttpErr : public std::exception {
|
||||
public:
|
||||
HttpErr(std::string msg) : _msg(msg) {}
|
||||
~HttpErr() throw() {}
|
||||
|
||||
virtual const char* what() const throw() { return _msg.c_str(); }
|
||||
|
||||
private:
|
||||
std::string _msg;
|
||||
uint16_t _code;
|
||||
};
|
||||
|
||||
/*
|
||||
* HTTP Request
|
||||
*/
|
||||
struct Req {
|
||||
std::string cmd, url, ver, payload;
|
||||
std::unordered_map<std::string, std::string> params;
|
||||
};
|
||||
|
||||
/*
|
||||
* HTTP Answer
|
||||
*/
|
||||
struct Answer {
|
||||
std::string status, pl;
|
||||
bool gzip;
|
||||
std::unordered_map<std::string, std::string> params;
|
||||
};
|
||||
|
||||
/*
|
||||
* Virtual handler provider class
|
||||
*/
|
||||
class Handler {
|
||||
public:
|
||||
virtual Answer handle(const Req& request, int connection) const = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Queue of connections to handle
|
||||
*/
|
||||
class Queue {
|
||||
public:
|
||||
void add(int c);
|
||||
int get();
|
||||
|
||||
private:
|
||||
std::mutex _mut;
|
||||
std::queue<int> _jobs;
|
||||
std::condition_variable _hasNew;
|
||||
};
|
||||
|
||||
/*
|
||||
* Socket wrapper
|
||||
*/
|
||||
class Socket {
|
||||
public:
|
||||
Socket(int port);
|
||||
~Socket();
|
||||
int wait();
|
||||
|
||||
private:
|
||||
int _sock;
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple HTTP server, must provide a pointer to a class instance implementing
|
||||
* virtual class Handler.
|
||||
*/
|
||||
class HttpServer {
|
||||
public:
|
||||
HttpServer(int port, const Handler* h) : HttpServer(port, h, 0) {}
|
||||
HttpServer(int port, const Handler* h, size_t threads)
|
||||
: _port(port), _handler(h), _threads(threads) {
|
||||
if (!_threads) _threads = 8 * std::thread::hardware_concurrency();
|
||||
}
|
||||
void run();
|
||||
|
||||
private:
|
||||
int _port;
|
||||
Queue _jobs;
|
||||
const Handler* _handler;
|
||||
size_t _threads;
|
||||
|
||||
void handle();
|
||||
|
||||
static void send(int sock, Answer* aw);
|
||||
static Req getReq(int connection);
|
||||
static std::string compress(const std::string& str, std::string* enc);
|
||||
static bool gzipSupport(const Req& req);
|
||||
};
|
||||
} // http
|
||||
} // util
|
||||
|
||||
#endif // UTIL_HTTP_SERVER_H_
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include "util/String.h"
|
||||
#include "util/geo/Geo.h"
|
||||
#include "util/json/Writer.h"
|
||||
#include "util/graph/Algorithm.h"
|
||||
#include "util/graph/DirGraph.h"
|
||||
#include "util/graph/UndirGraph.h"
|
||||
#include "util/graph/Dijkstra.h"
|
||||
|
|
@ -403,6 +404,46 @@ CASE("editdist") {
|
|||
EXPECT(util::editDist("hello", "hello") == (size_t)0);
|
||||
}},
|
||||
|
||||
// ___________________________________________________________________________
|
||||
{
|
||||
CASE("prefixeditdist") {
|
||||
EXPECT(util::prefixEditDist("hello", "hello", 0) == (size_t)0);
|
||||
EXPECT(util::prefixEditDist("hello", "hello", 100) == (size_t)0);
|
||||
EXPECT(util::prefixEditDist("hello", "hello") == (size_t)0);
|
||||
EXPECT(util::prefixEditDist("hel", "hello") == (size_t)0);
|
||||
EXPECT(util::prefixEditDist("hel", "hello", 0) == (size_t)0);
|
||||
EXPECT(util::prefixEditDist("hel", "hello", 1) == (size_t)0);
|
||||
EXPECT(util::prefixEditDist("hel", "hello", 2) == (size_t)0);
|
||||
EXPECT(util::prefixEditDist("hal", "hello", 2) == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("hal", "hello", 1) == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("hal", "hello", 0) > (size_t)0);
|
||||
EXPECT(util::prefixEditDist("fel", "hello", 0) > (size_t)0);
|
||||
EXPECT(util::prefixEditDist("fel", "hello", 1) == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("fel", "hello", 2) == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("fal", "hello", 2) == (size_t)2);
|
||||
EXPECT(util::prefixEditDist("fal", "hello", 1) > (size_t)1);
|
||||
EXPECT(util::prefixEditDist("fal", "hello", 0) > (size_t)0);
|
||||
EXPECT(util::prefixEditDist("far", "hello", 0) > (size_t)0);
|
||||
EXPECT(util::prefixEditDist("far", "hello", 1) > (size_t)1);
|
||||
EXPECT(util::prefixEditDist("far", "hello", 2) > (size_t)2);
|
||||
EXPECT(util::prefixEditDist("far", "hello", 3) == (size_t)3);
|
||||
EXPECT(util::prefixEditDist("far", "hello", 4) == (size_t)3);
|
||||
EXPECT(util::prefixEditDist("far", "hello") == (size_t)3);
|
||||
EXPECT(util::prefixEditDist("hefar", "hello") == (size_t)3);
|
||||
EXPECT(util::prefixEditDist("hefaree", "hello") == (size_t)5);
|
||||
EXPECT(util::prefixEditDist("helloo", "hello") == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("helloo", "hello", 0) > (size_t)0);
|
||||
EXPECT(util::prefixEditDist("helloo", "hello", 1) == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("helloo", "hello", 2) == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("", "hello", 2) == (size_t)0);
|
||||
EXPECT(util::prefixEditDist("e", "hello", 2) == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("el", "hello", 2) == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("ello", "hello", 2) == (size_t)1);
|
||||
EXPECT(util::prefixEditDist("hell", "hello", 2) == (size_t)0);
|
||||
EXPECT(util::prefixEditDist("hell", "", 2) > (size_t)2);
|
||||
EXPECT(util::prefixEditDist("hell", "") == (size_t)4);
|
||||
}},
|
||||
|
||||
// ___________________________________________________________________________
|
||||
{
|
||||
CASE("toString") {
|
||||
|
|
@ -447,6 +488,51 @@ CASE("replace") {
|
|||
EXPECT(b == "loree aaaau aaaau loree");
|
||||
}},
|
||||
|
||||
// ___________________________________________________________________________
|
||||
{
|
||||
CASE("Connected components undirected") {
|
||||
UndirGraph<std::string, int> g;
|
||||
|
||||
auto a = g.addNd("A");
|
||||
auto b = g.addNd("B");
|
||||
auto c = g.addNd("C");
|
||||
auto d = g.addNd("D");
|
||||
auto e = g.addNd("E");
|
||||
|
||||
g.addEdg(a, c, 1);
|
||||
g.addEdg(a, b, 5);
|
||||
g.addEdg(d, c, 1);
|
||||
g.addEdg(d, b, 3);
|
||||
g.addEdg(e, d, 1);
|
||||
g.addEdg(e, b, 1);
|
||||
|
||||
auto comps = util::graph::Algorithm::connectedComponents(g);
|
||||
|
||||
EXPECT(comps.size() == static_cast<size_t>(1));
|
||||
EXPECT(comps[0].count(a));
|
||||
EXPECT(comps[0].count(b));
|
||||
EXPECT(comps[0].count(c));
|
||||
EXPECT(comps[0].count(d));
|
||||
EXPECT(comps[0].count(e));
|
||||
|
||||
auto f = g.addNd("F");
|
||||
comps = util::graph::Algorithm::connectedComponents(g);
|
||||
EXPECT(comps.size() == static_cast<size_t>(2));
|
||||
|
||||
auto gn = g.addNd("G");
|
||||
comps = util::graph::Algorithm::connectedComponents(g);
|
||||
EXPECT(comps.size() == static_cast<size_t>(3));
|
||||
|
||||
g.addEdg(f, gn, 1);
|
||||
comps = util::graph::Algorithm::connectedComponents(g);
|
||||
EXPECT(comps.size() == static_cast<size_t>(2));
|
||||
|
||||
g.addEdg(f, a, 1);
|
||||
comps = util::graph::Algorithm::connectedComponents(g);
|
||||
EXPECT(comps.size() == static_cast<size_t>(1));
|
||||
|
||||
}},
|
||||
|
||||
// ___________________________________________________________________________
|
||||
{
|
||||
CASE("Edge-based Dijkstra directed, 1 to all") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue