Initial commit
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
#include <execinfo.h> // for backtrace
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
#include <src/log/LogUtils.h>
|
||||
|
||||
#include "TraceUtils.h"
|
||||
#include "../../../music/providers/shared/pstream.h"
|
||||
|
||||
#define READ_BUFFER_SIZE 128
|
||||
|
||||
namespace TraceUtils {
|
||||
static bool addr2line_present = true;
|
||||
inline std::string addr2lineInfo(StackTraceElement *element) {
|
||||
if(!addr2line_present)
|
||||
return "??\n??:0";
|
||||
|
||||
char buffer[READ_BUFFER_SIZE];
|
||||
|
||||
sprintf(buffer, "addr2line -Cif -e %s %p", element->file.c_str(), element->offset); //last parameter is the name of this app
|
||||
redi::pstream stream(buffer, redi::pstream::pstdout | redi::pstream::pstderr);
|
||||
|
||||
std::string result;
|
||||
std::string error;
|
||||
do {
|
||||
|
||||
auto read = stream.err().readsome(buffer, READ_BUFFER_SIZE);
|
||||
if(read > 0) error += string(buffer, read);
|
||||
|
||||
read = stream.out().readsome(buffer, READ_BUFFER_SIZE);
|
||||
if(read > 0) result += string(buffer, read);
|
||||
} while(stream.good());
|
||||
|
||||
if(!error.empty()) {
|
||||
while(!error.empty() && (error.back() == '\n' || error.back() == '\r')) error = error.substr(0, error.length() - 1);
|
||||
logError("Could not resolve symbols. (Error: " + error + ")");
|
||||
addr2line_present = false;
|
||||
return "??\n??:0";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void StackTrace::printStackTrace() {
|
||||
printStackTrace([](StackTraceElement* e) {
|
||||
cerr << " at "+e->getFunctionName()+"( " + e->getSourceFile() + ":" + to_string(e->getSourceLine()) + ")" << endl;
|
||||
});
|
||||
}
|
||||
|
||||
void StackTrace::printStackTrace(std::function<void(StackTraceElement*)> writeMessage) {
|
||||
for (int i = 0; i < stackSize; i++) {
|
||||
writeMessage((StackTraceElement*) elements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
StackTrace backTrace(int size) {
|
||||
int backtraceLength;
|
||||
void *buffer[BT_BUF_SIZE];
|
||||
char **symbols;
|
||||
|
||||
backtraceLength = backtrace(buffer, BT_BUF_SIZE);
|
||||
symbols = backtrace_symbols(buffer, backtraceLength);
|
||||
if (symbols == nullptr) {
|
||||
perror("backtrace_symbols");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
StackTrace out(backtraceLength);
|
||||
|
||||
for (int i = 0; i < backtraceLength; i++) {
|
||||
auto sym = std::string(symbols[i]);
|
||||
string file = "undefined";
|
||||
if (sym.find_first_of('(') != std::string::npos) file = sym.substr(0, sym.find_first_of('('));
|
||||
out.elements[i] = new StackTraceElement{i, buffer[i], file, sym};
|
||||
}
|
||||
|
||||
free(symbols);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
StackTrace::StackTrace(int size) : stackSize(size), elements(static_cast<const StackTraceElement **>(malloc(size * sizeof(void *)))) {}
|
||||
|
||||
StackTrace::~StackTrace() {
|
||||
for (int i = 0; i < this->stackSize; i++)
|
||||
if (this->elements[i]) delete this->elements[i];
|
||||
free(this->elements);
|
||||
}
|
||||
|
||||
void StackTraceElement::loadSymbols() {
|
||||
if (this->symbolLoadState == 0) {
|
||||
auto strInfo = addr2lineInfo(this);
|
||||
this->fnName = strInfo.substr(0, strInfo.find_first_of('\n'));
|
||||
|
||||
auto srcInfo = strInfo.substr(strInfo.find_first_of('\n') + 1);
|
||||
this->srcFile = srcInfo.substr(0, srcInfo.find_first_of(':'));
|
||||
this->srcLine = atoi(srcInfo.substr(srcInfo.find_first_of(':') + 1).c_str());
|
||||
this->symbolLoadState = 1;
|
||||
}
|
||||
}
|
||||
|
||||
string StackTraceElement::getFunctionName() {
|
||||
loadSymbols();
|
||||
return this->fnName;
|
||||
}
|
||||
|
||||
string StackTraceElement::getSourceFile() {
|
||||
loadSymbols();
|
||||
return this->srcFile;
|
||||
}
|
||||
|
||||
int StackTraceElement::getSourceLine() {
|
||||
loadSymbols();
|
||||
return this->srcLine;
|
||||
}
|
||||
|
||||
StackTraceElement::StackTraceElement(int elementIndex, void *offset, string file, string symbol) : elementIndex(elementIndex), offset(offset), file(file), symbol(symbol) {}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
#define BT_BUF_SIZE 100
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace TraceUtils {
|
||||
struct StackTraceElement {
|
||||
public:
|
||||
StackTraceElement(int elementIndex, void *offset,string file,string symbol);
|
||||
|
||||
int elementIndex;
|
||||
|
||||
void *offset;
|
||||
string file;
|
||||
|
||||
string symbol;
|
||||
|
||||
|
||||
string getFunctionName();
|
||||
string getSourceFile();
|
||||
int getSourceLine();
|
||||
|
||||
private:
|
||||
int symbolLoadState = 0;
|
||||
|
||||
string fnName;
|
||||
string srcFile;
|
||||
int srcLine;
|
||||
|
||||
void loadSymbols();
|
||||
};
|
||||
|
||||
struct StackTrace {
|
||||
public:
|
||||
explicit StackTrace(int size);
|
||||
~StackTrace();
|
||||
|
||||
const StackTraceElement** elements;
|
||||
const int stackSize;
|
||||
|
||||
void printStackTrace();
|
||||
|
||||
void printStackTrace(std::function<void(StackTraceElement*)> format);
|
||||
};
|
||||
|
||||
extern StackTrace backTrace(int size);
|
||||
};
|
||||
@@ -0,0 +1,299 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
|
||||
namespace std {
|
||||
template<class T, class Lock>
|
||||
struct lock_guarded {
|
||||
Lock l;
|
||||
T *t;
|
||||
|
||||
T *operator->() &&{ return t; }
|
||||
|
||||
template<class Arg>
|
||||
auto operator[](Arg &&arg) && -> decltype(std::declval<T &>()[std::declval<Arg>()]) {
|
||||
return (*t)[std::forward<Arg>(arg)];
|
||||
}
|
||||
|
||||
T &operator*() &&{ return *t; }
|
||||
};
|
||||
|
||||
template<class T, class Lock>
|
||||
struct lock_guarded_shared {
|
||||
Lock l;
|
||||
std::shared_ptr<T> t;
|
||||
|
||||
T *operator->() &&{ return t.operator->(); }
|
||||
|
||||
template<class Arg>
|
||||
auto operator[](Arg &&arg) && -> decltype(std::declval<T &>()[std::declval<Arg>()]) {
|
||||
return (*t)[std::forward<Arg>(arg)];
|
||||
}
|
||||
|
||||
T &operator*() &&{ return *t; }
|
||||
|
||||
operator bool() {
|
||||
return !!t;
|
||||
}
|
||||
|
||||
bool operator !() {
|
||||
return !t;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr struct emplace_t { } emplace{};
|
||||
|
||||
template<class T, class M>
|
||||
struct observer_locked {
|
||||
public:
|
||||
observer_locked(observer_locked &&o) : t(std::move(o.t)), m(std::move(o.m)) {}
|
||||
observer_locked(observer_locked const &o) : t(o.t), m(o.m) {}
|
||||
|
||||
observer_locked(M lock,T entry) : m(std::forward<M>(lock)), t(std::forward<T>(entry)) {}
|
||||
|
||||
observer_locked() = default;
|
||||
~observer_locked() = default;
|
||||
|
||||
T operator->() {
|
||||
return t;
|
||||
}
|
||||
|
||||
T const operator->() const {
|
||||
return t;
|
||||
}
|
||||
|
||||
T get() { return this->t; }
|
||||
T const get() const { return this->t; }
|
||||
|
||||
template<class F>
|
||||
std::result_of_t<F(T &)> operator->*(F &&f) {
|
||||
return std::forward<F>(f)(t);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
std::result_of_t<F(T const &)> operator->*(F &&f) const {
|
||||
return std::forward<F>(f)(t);
|
||||
}
|
||||
|
||||
observer_locked &operator=(observer_locked &&o) {
|
||||
this->m = std::move(o.m);
|
||||
this->t = std::move(o.t);
|
||||
return *this;
|
||||
}
|
||||
|
||||
observer_locked &operator=(observer_locked const &o) {
|
||||
this->m = o.m;
|
||||
this->t = o.t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
observer_locked &reset() {
|
||||
observer_locked empty(M(),NULL);
|
||||
*this = empty;
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
M m;
|
||||
T t;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct mutex_guarded {
|
||||
lock_guarded<T, std::unique_lock<std::mutex>> get_locked() {
|
||||
return {std::unique_lock{m}, &t};
|
||||
}
|
||||
|
||||
lock_guarded<T const, std::unique_lock<std::mutex>> get_locked() const {
|
||||
return {{m}, &t};
|
||||
}
|
||||
|
||||
lock_guarded<T, std::unique_lock<std::mutex>> operator->() {
|
||||
return get_locked();
|
||||
}
|
||||
|
||||
lock_guarded<T const, std::unique_lock<std::mutex>> operator->() const {
|
||||
return get_locked();
|
||||
}
|
||||
|
||||
template<class F>
|
||||
std::result_of_t<F(T &)> operator->*(F &&f) {
|
||||
return std::forward<F>(f)(*get_locked());
|
||||
}
|
||||
|
||||
template<class F>
|
||||
std::result_of_t<F(T const &)> operator->*(F &&f) const {
|
||||
return std::forward<F>(f)(*get_locked());
|
||||
}
|
||||
|
||||
template<class...Args>
|
||||
mutex_guarded(emplace_t, Args &&...args) : t(std::forward<Args>(args)...) {}
|
||||
|
||||
mutex_guarded(mutex_guarded &&o) : t(std::move(*o.get_locked())) {}
|
||||
|
||||
mutex_guarded(mutex_guarded const &o) : t(*o.get_locked()) {}
|
||||
|
||||
mutex_guarded() = default;
|
||||
|
||||
~mutex_guarded() = default;
|
||||
|
||||
mutex_guarded &operator=(mutex_guarded &&o) {
|
||||
T tmp = std::move(o.get_locked());
|
||||
*get_locked() = std::move(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
mutex_guarded &operator=(mutex_guarded const &o) {
|
||||
T tmp = o.get_locked();
|
||||
*get_locked() = std::move(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m;
|
||||
T t;
|
||||
};
|
||||
|
||||
class shared_recursive_mutex {
|
||||
std::shared_mutex handle;
|
||||
public:
|
||||
void lock(void) {
|
||||
std::thread::id this_id = std::this_thread::get_id();
|
||||
if (owner == this_id) {
|
||||
// recursive locking
|
||||
++count;
|
||||
} else {
|
||||
// normal locking
|
||||
if (shared_counts->count(this_id)) {//Already shared locked, write lock is not available
|
||||
#ifdef WIN32
|
||||
throw std::logic_error("resource_deadlock_would_occur");
|
||||
#else
|
||||
__throw_system_error(int(errc::resource_deadlock_would_occur));
|
||||
#endif
|
||||
}
|
||||
handle.lock(); //Now wait until everyone else has finished
|
||||
owner = this_id;
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void unlock(void) {
|
||||
std::thread::id this_id = std::this_thread::get_id();
|
||||
assert(this_id == this->owner);
|
||||
|
||||
if (count > 1) {
|
||||
// recursive unlocking
|
||||
count--;
|
||||
} else {
|
||||
// normal unlocking
|
||||
owner = std::thread::id();
|
||||
count = 0;
|
||||
handle.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void lock_shared() {
|
||||
std::thread::id this_id = std::this_thread::get_id();
|
||||
if(this->owner == this_id) {
|
||||
#ifdef WIN32
|
||||
throw std::logic_error("resource_deadlock_would_occur");
|
||||
#else
|
||||
__throw_system_error(int(errc::resource_deadlock_would_occur));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (shared_counts->count(this_id)) {
|
||||
++(shared_counts.get_locked()[this_id]);
|
||||
} else {
|
||||
handle.lock_shared();
|
||||
shared_counts.get_locked()[this_id] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared() {
|
||||
std::thread::id this_id = std::this_thread::get_id();
|
||||
auto it = shared_counts->find(this_id);
|
||||
if (it->second > 1) {
|
||||
--(it->second);
|
||||
} else {
|
||||
shared_counts->erase(it);
|
||||
handle.unlock_shared();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock() {
|
||||
std::thread::id this_id = std::this_thread::get_id();
|
||||
if (owner == this_id) {
|
||||
// recursive locking
|
||||
++count;
|
||||
return true;
|
||||
} else {
|
||||
// normal locking
|
||||
if (shared_counts->count(this_id)){ //Already shared locked, write lock is not available
|
||||
#ifdef WIN32
|
||||
throw std::logic_error("resource_deadlock_would_occur");
|
||||
#else
|
||||
__throw_system_error(int(errc::resource_deadlock_would_occur));
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!handle.try_lock()) return false;
|
||||
|
||||
owner = this_id;
|
||||
count = 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_shared() {
|
||||
std::thread::id this_id = std::this_thread::get_id();
|
||||
if(this->owner == this_id){
|
||||
#ifdef WIN32
|
||||
throw std::logic_error("resource_deadlock_would_occur");
|
||||
#else
|
||||
__throw_system_error(int(errc::resource_deadlock_would_occur));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (shared_counts->count(this_id)) {
|
||||
++(shared_counts.get_locked()[this_id]);
|
||||
} else {
|
||||
if(!handle.try_lock_shared()) return false;
|
||||
|
||||
shared_counts.get_locked()[this_id] = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<std::thread::id> owner;
|
||||
std::atomic<std::size_t> count;
|
||||
mutex_guarded<std::map<std::thread::id, std::size_t>> shared_counts;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline bool mutex_locked(T& mutex) {
|
||||
return true;
|
||||
try {
|
||||
unique_lock<T> lock_try(mutex, try_to_lock); /* should throw EDEADLK */
|
||||
return false;
|
||||
} catch(const std::system_error& ex) {
|
||||
return ex.code() == errc::resource_deadlock_would_occur;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool mutex_shared_locked(T& mutex) {
|
||||
return true;
|
||||
try {
|
||||
shared_lock<T> lock_try(mutex, try_to_lock); /* should throw EDEADLK */
|
||||
return false;
|
||||
} catch(const std::system_error& ex) {
|
||||
return ex.code() == errc::resource_deadlock_would_occur;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <tomcrypt.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace base64 {
|
||||
/**
|
||||
* Encodes a given string in Base64
|
||||
* @param input The input string to Base64-encode
|
||||
* @param inputSize The size of the input to decode
|
||||
* @return A Base64-encoded version of the encoded string
|
||||
*/
|
||||
inline std::string encode(const char* input, const unsigned long inputSize) {
|
||||
auto outlen = static_cast<unsigned long>(inputSize + (inputSize / 3.0) + 16);
|
||||
auto outbuf = new unsigned char[outlen]; //Reserve output memory
|
||||
if(base64_encode((unsigned char*) input, inputSize, outbuf, &outlen) != CRYPT_OK){
|
||||
std::cerr << "Invalid input '" << input << "'" << std::endl;
|
||||
return "";
|
||||
}
|
||||
std::string ret((char*) outbuf, outlen);
|
||||
delete[] outbuf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a given string in Base64
|
||||
* @param input The input string to Base64-encode
|
||||
* @return A Base64-encoded version of the encoded string
|
||||
*/
|
||||
inline std::string encode(const std::string& input) { return encode(input.c_str(), input.size()); }
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a Base64-encoded string.
|
||||
* @param input The input string to decode
|
||||
* @return A string (binary) that represents the Base64-decoded data of the input
|
||||
*/
|
||||
inline std::string decode(const char* input, size_t size) {
|
||||
auto out = new unsigned char[size];
|
||||
if(base64_strict_decode((unsigned char*) input, size, out, (unsigned long*) &size) != CRYPT_OK){
|
||||
std::cerr << "Invalid base 64 string '" << input << "'" << std::endl;
|
||||
return "";
|
||||
}
|
||||
std::string ret((char*) out, size);
|
||||
delete[] out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a Base64-encoded string.
|
||||
* @param input The input string to decode
|
||||
* @return A string (binary) that represents the Base64-decoded data of the input
|
||||
*/
|
||||
inline std::string decode(const std::string& input) { return decode(input.c_str(), input.size()); }
|
||||
|
||||
//A–Z, a–z, 0–9, + und /
|
||||
inline bool validate(const std::string& input) {
|
||||
for(char c : input) {
|
||||
if(c >= 'A' && c <= 'Z') continue;
|
||||
if(c >= 'a' && c <= 'z') continue;
|
||||
if(c >= '0' && c <= '9') continue;
|
||||
if(c == '+' || c == '/' || c == '=') continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
inline std::string base64_encode(const char* input, const unsigned long inputSize) { return base64::encode(input, inputSize); }
|
||||
inline std::string base64_encode(const std::string& input) { return base64::encode(input.c_str(), input.size()); }
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
template<class T, class U>
|
||||
inline std::shared_ptr <T> static_pointer_cast(const std::shared_ptr <U> &r) noexcept {
|
||||
auto p = static_cast<typename std::shared_ptr<T>::element_type *>(r.get());
|
||||
return std::shared_ptr<T>(r, p);
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
inline std::shared_ptr <T> dynamic_pointer_cast(const std::shared_ptr <U> &r) noexcept {
|
||||
if (auto p = dynamic_cast<typename std::shared_ptr<T>::element_type *>(r.get())) {
|
||||
return std::shared_ptr<T>(r, p);
|
||||
} else {
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
inline std::shared_ptr <T> const_pointer_cast(const std::shared_ptr <U> &r) noexcept {
|
||||
auto p = const_cast<typename std::shared_ptr<T>::element_type *>(r.get());
|
||||
return std::shared_ptr<T>(r, p);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef NO_OPEN_SSL
|
||||
#include <tomcrypt.h>
|
||||
|
||||
#define SHA_DIGEST_LENGTH 20
|
||||
#define SHA256_DIGEST_LENGTH 32
|
||||
#define SHA512_DIGEST_LENGTH 64
|
||||
|
||||
#define DECLARE_DIGEST(name, _unused_, digestLength) \
|
||||
inline std::string name(const std::string& input) { \
|
||||
hash_state hash{}; \
|
||||
\
|
||||
uint8_t buffer[digestLength]; \
|
||||
\
|
||||
name ##_init(&hash); \
|
||||
name ##_process(&hash, (uint8_t*) input.data(), input.length()); \
|
||||
name ##_done(&hash, buffer); \
|
||||
\
|
||||
return std::string((const char*) buffer, digestLength); \
|
||||
} \
|
||||
\
|
||||
inline std::string name(const char* input, int64_t length = -1) { \
|
||||
if(length == -1) length = strlen(input); \
|
||||
return name(std::string{input, (size_t) length}); \
|
||||
} \
|
||||
|
||||
#else
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#define DECLARE_DIGEST(name, method, digestLength) \
|
||||
inline std::string name(const std::string& input) { \
|
||||
u_char buffer[digestLength]; \
|
||||
method((u_char*) input.data(), input.length(), buffer); \
|
||||
return std::string((const char*) buffer, digestLength); \
|
||||
} \
|
||||
\
|
||||
inline std::string name(const char* input, ssize_t length = -1) { \
|
||||
if(length == -1) length = strlen(input); \
|
||||
return name(std::string(input, length)); \
|
||||
} \
|
||||
\
|
||||
inline void name(const char* input, size_t length, uint8_t(& result)[digestLength]) { \
|
||||
method((u_char*) input, length, result); \
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace digest {
|
||||
DECLARE_DIGEST(sha1, SHA1, SHA_DIGEST_LENGTH)
|
||||
DECLARE_DIGEST(sha256, SHA256, SHA256_DIGEST_LENGTH)
|
||||
DECLARE_DIGEST(sha512, SHA512, SHA512_DIGEST_LENGTH)
|
||||
}
|
||||
|
||||
#undef DECLARE_DIGEST
|
||||
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define _LE2BE(size, convert) \
|
||||
template <typename T = int, typename BufferType, typename std::enable_if< \
|
||||
std::is_same<typename std::remove_const<BufferType>::type, uint8_t>::value || \
|
||||
std::is_same<typename std::remove_const<BufferType>::type, int8_t>::value || \
|
||||
std::is_same<typename std::remove_const<BufferType>::type, char>::value || \
|
||||
std::is_same<typename std::remove_const<BufferType>::type, unsigned char>::value \
|
||||
, int>::type = 0> \
|
||||
inline void le2be ##size(uint ##size ##_t num, BufferType* buffer,T offset = 0, T* offsetCounter = nullptr){ \
|
||||
convert; \
|
||||
if(offsetCounter) *offsetCounter += (size) / 8; \
|
||||
}
|
||||
|
||||
#define _BE2LE(size, convert) \
|
||||
template <typename T = int, typename BufferType, typename std::enable_if< \
|
||||
std::is_same<typename std::remove_const<BufferType>::type, uint8_t>::value || \
|
||||
std::is_same<typename std::remove_const<BufferType>::type, int8_t>::value || \
|
||||
std::is_same<typename std::remove_const<BufferType>::type, char>::value || \
|
||||
std::is_same<typename std::remove_const<BufferType>::type, unsigned char>::value \
|
||||
, int>::type = 0, typename ResultType = uint ##size ##_t> \
|
||||
inline ResultType be2le ##size(BufferType* buffer,T offset = 0, T* offsetCounter = nullptr){ \
|
||||
ResultType result = 0; \
|
||||
convert; \
|
||||
if(offsetCounter) *offsetCounter += (size) / 8; \
|
||||
return result; \
|
||||
}
|
||||
|
||||
//LE -> BE
|
||||
_LE2BE(8, {
|
||||
buffer[offset + 0] = (BufferType) ((num) & 0xFF);
|
||||
});
|
||||
|
||||
_LE2BE(16, {
|
||||
buffer[offset + 0] = (BufferType) ((num >> 8) & 0xFF);
|
||||
buffer[offset + 1] = (BufferType) ((num >> 0) & 0xFF);
|
||||
});
|
||||
|
||||
_LE2BE(32, {
|
||||
buffer[offset + 0] = (BufferType) ((num >> 24) & 0xFF);
|
||||
buffer[offset + 1] = (BufferType) ((num >> 16) & 0xFF);
|
||||
buffer[offset + 2] = (BufferType) ((num >> 8) & 0xFF);
|
||||
buffer[offset + 3] = (BufferType) ((num >> 0) & 0xFF);
|
||||
});
|
||||
|
||||
_LE2BE(64, {
|
||||
buffer[offset + 0] = (BufferType) ((num >> 56) & 0xFF);
|
||||
buffer[offset + 1] = (BufferType) ((num >> 48) & 0xFF);
|
||||
buffer[offset + 2] = (BufferType) ((num >> 40) & 0xFF);
|
||||
buffer[offset + 3] = (BufferType) ((num >> 32) & 0xFF);
|
||||
buffer[offset + 4] = (BufferType) ((num >> 24) & 0xFF);
|
||||
buffer[offset + 5] = (BufferType) ((num >> 16) & 0xFF);
|
||||
buffer[offset + 6] = (BufferType) ((num >> 8) & 0xFF);
|
||||
buffer[offset + 7] = (BufferType) ((num >> 0) & 0xFF);
|
||||
});
|
||||
|
||||
|
||||
//BE -> LE
|
||||
_BE2LE(8, {
|
||||
result |= (ResultType) (uint8_t) buffer[offset + 0];
|
||||
});
|
||||
|
||||
_BE2LE(16, {
|
||||
result |= (ResultType) (uint8_t) buffer[offset + 0] << 8;
|
||||
result |= (ResultType) (uint8_t) buffer[offset + 1] << 0;
|
||||
});
|
||||
|
||||
_BE2LE(32, {
|
||||
result |= (ResultType) (uint8_t) buffer[offset + 0] << 24;
|
||||
result |= (ResultType) (uint8_t) buffer[offset + 1] << 16;
|
||||
result |= (ResultType) (uint8_t) buffer[offset + 2] << 8;
|
||||
result |= (ResultType) (uint8_t) buffer[offset + 3] << 0;
|
||||
});
|
||||
|
||||
_BE2LE(64, {
|
||||
result += (ResultType) (uint8_t) buffer[offset + 0] << 56;
|
||||
result += (ResultType) (uint8_t) buffer[offset + 1] << 48;
|
||||
result += (ResultType) (uint8_t) buffer[offset + 2] << 40;
|
||||
result += (ResultType) (uint8_t) buffer[offset + 3] << 32;
|
||||
result += (ResultType) (uint8_t) buffer[offset + 4] << 24;
|
||||
result += (ResultType) (uint8_t) buffer[offset + 5] << 16;
|
||||
result += (ResultType) (uint8_t) buffer[offset + 6] << 8;
|
||||
result += (ResultType) (uint8_t) buffer[offset + 7] << 0;
|
||||
});
|
||||
|
||||
template <typename T = uint32_t>
|
||||
inline void le2le16(uint16_t num, char *buffer,T offset = 0, T* offsetCounter = nullptr){
|
||||
buffer[offset + 0] = (char) (num >> 0);
|
||||
buffer[offset + 1] = (char) (num >> 8);
|
||||
if(offsetCounter) *offsetCounter += 2;
|
||||
static_assert(true, "");
|
||||
}
|
||||
|
||||
template <typename T = uint32_t>
|
||||
inline void le2le64(uint64_t num, char *buffer,T offset = 0, T* offsetCounter = nullptr){
|
||||
buffer[offset + 0] = (char) (num >> 0);
|
||||
buffer[offset + 1] = (char) (num >> 8);
|
||||
buffer[offset + 2] = (char) (num >> 16);
|
||||
buffer[offset + 3] = (char) (num >> 24);
|
||||
buffer[offset + 4] = (char) (num >> 32);
|
||||
buffer[offset + 5] = (char) (num >> 40);
|
||||
buffer[offset + 6] = (char) (num >> 48);
|
||||
buffer[offset + 7] = (char) (num >> 56);
|
||||
if(offsetCounter) *offsetCounter += 2;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
namespace hex {
|
||||
inline std::string hex(const std::string& input, char beg, char end){
|
||||
assert(end - beg == 16);
|
||||
|
||||
int len = input.length() * 2;
|
||||
char output[len];
|
||||
int idx = 0;
|
||||
for (char elm : input) {
|
||||
output[idx++] = static_cast<char>(beg + ((elm >> 4) & 0x0F));
|
||||
output[idx++] = static_cast<char>(beg + ((elm & 0x0F) >> 0));
|
||||
}
|
||||
|
||||
return std::string(output, len);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <regex>
|
||||
|
||||
//https://qiita.com/angeart/items/94734d68999eca575881
|
||||
namespace stx {
|
||||
namespace lambda_detail {
|
||||
template <typename...>
|
||||
struct member_type;
|
||||
|
||||
template <typename ret, typename klass, typename... args>
|
||||
struct member_type<std::true_type, ret, klass, args...> {
|
||||
using member = std::true_type;
|
||||
using invoker_function = std::function<ret(klass*, args...)>;
|
||||
};
|
||||
template <typename ret, typename klass, typename... args>
|
||||
struct member_type<std::false_type, ret, klass, args...> {
|
||||
using member = std::false_type;
|
||||
using invoker_function = std::function<ret(args...)>;
|
||||
};
|
||||
|
||||
template<typename t_member, class t_return_type, class t_klass, class flag_mutable, class... t_args>
|
||||
struct types : member_type<t_member, t_return_type, t_klass, t_args...> {
|
||||
public:
|
||||
static constexpr bool has_klass = std::is_class<t_klass>::value;
|
||||
static constexpr int argc = sizeof...(t_args);
|
||||
|
||||
using flag_member = t_member;
|
||||
using klass = t_klass;
|
||||
using return_type = t_return_type;
|
||||
using is_mutable = flag_mutable;
|
||||
|
||||
using args = std::tuple<t_args...>;
|
||||
|
||||
template<size_t i>
|
||||
struct arg {
|
||||
typedef typename std::tuple_element<i, std::tuple<t_args...>>::type type;
|
||||
};
|
||||
};
|
||||
|
||||
template<class lambda>
|
||||
struct lambda_type_impl;
|
||||
|
||||
template<class ret, class klass, class... args>
|
||||
struct lambda_type_impl<ret(klass::*)(args...) const> : lambda_detail::types<std::false_type, ret, klass, std::true_type, args...> {};
|
||||
}
|
||||
|
||||
template<class lambda>
|
||||
struct lambda_type : lambda_detail::lambda_type_impl<decltype(&lambda::operator())> { };
|
||||
|
||||
template<class ret, class klass, class... args>
|
||||
struct lambda_type<ret(klass::*)(args...)> : lambda_detail::types<typename std::is_member_function_pointer<ret(klass::*)(args...)>::type,ret,klass,std::true_type,args...> { };
|
||||
|
||||
template<class ret, class klass, class... args>
|
||||
struct lambda_type<ret(klass::*)(args...) const> : lambda_detail::types<typename std::is_member_function_pointer<ret(klass::*)(args...) const>::type, ret, klass, std::false_type, args...> { };
|
||||
|
||||
};
|
||||
@@ -0,0 +1,227 @@
|
||||
#include "log/LogUtils.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <typeindex>
|
||||
|
||||
#define TRACK_OBJECT_ALLOCATION
|
||||
#include "memtracker.h"
|
||||
|
||||
#define NO_IMPL //For fast disable (e.g. when you dont want to recompile the whole source)
|
||||
|
||||
#ifndef __GLIBC__
|
||||
#define _GLIBCXX_NOEXCEPT
|
||||
#endif
|
||||
#ifndef WIN32
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
typedef int64_t ssize_t;
|
||||
#endif
|
||||
|
||||
//#define MEMTRACK_VERBOSE
|
||||
|
||||
inline bool should_track_mangled(const char* mangled) {
|
||||
if(strstr(mangled, "ViewEntry")) return true;
|
||||
if(strstr(mangled, "ViewEntry")) return true;
|
||||
if(strstr(mangled, "ClientChannelView")) return true;
|
||||
if(strstr(mangled, "LinkedTreeEntry")) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
namespace memtrack {
|
||||
struct TypeInfo {
|
||||
const char* name;
|
||||
std::string mangled;
|
||||
|
||||
explicit TypeInfo(const char* name) : name(name) {}
|
||||
|
||||
bool operator==(const TypeInfo& other) {
|
||||
return other.name == this->name || strcmp(other.name, this->name) == 0;
|
||||
}
|
||||
bool operator!=(const TypeInfo& other) {
|
||||
return ! this->operator==(other);
|
||||
}
|
||||
|
||||
bool operator<(const TypeInfo& __rhs) const noexcept
|
||||
{ return this->before(__rhs); }
|
||||
|
||||
bool operator<=(const TypeInfo& __rhs) const noexcept
|
||||
{ return !__rhs.before(*this); }
|
||||
|
||||
bool operator>(const TypeInfo& __rhs) const noexcept
|
||||
{ return __rhs.before(*this); }
|
||||
|
||||
bool operator>=(const TypeInfo& __rhs) const noexcept
|
||||
{ return !this->before(__rhs); }
|
||||
|
||||
inline bool before(const TypeInfo& __arg) const _GLIBCXX_NOEXCEPT
|
||||
{ return (name[0] == '*' && __arg.name[0] == '*')
|
||||
? name < __arg.name
|
||||
: strcmp (name, __arg.name) < 0; }
|
||||
|
||||
inline std::string as_mangled() {
|
||||
#ifndef WIN32
|
||||
int status;
|
||||
std::unique_ptr<char[], void (*)(void*)> result(abi::__cxa_demangle(name, 0, 0, &status), std::free);
|
||||
if(status != 0)
|
||||
return "error: " + to_string(status);
|
||||
|
||||
this->mangled = result.get();
|
||||
#else
|
||||
//FIXME Implement!
|
||||
this->mangled = this->name;
|
||||
#endif
|
||||
return this->mangled;
|
||||
}
|
||||
};
|
||||
class entry {
|
||||
public:
|
||||
/* std::string name; */
|
||||
size_t type;
|
||||
void* address = nullptr;
|
||||
|
||||
entry() {}
|
||||
entry(size_t type, void* address) : type(type), address(address) {}
|
||||
~entry() {}
|
||||
};
|
||||
|
||||
template <int N>
|
||||
class brick {
|
||||
public:
|
||||
inline bool insert(size_t type, void* address) {
|
||||
auto slot = free_slot();
|
||||
if(slot == N) return false;
|
||||
entries[slot] = entry{type, address};
|
||||
findex = slot + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool remove(size_t type, void* address) {
|
||||
for(int index = 0; index < N; index++) {
|
||||
auto& e = entries[index];
|
||||
if(e.address == address && e.type == type) {
|
||||
e = entry{};
|
||||
findex = index;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int capacity() { return N; }
|
||||
|
||||
array<entry, N> entries;
|
||||
private:
|
||||
inline int free_slot() {
|
||||
while (findex < N && entries[findex].address) findex++;
|
||||
return findex;
|
||||
}
|
||||
int findex = 0;
|
||||
};
|
||||
typedef brick<1024> InfoBrick;
|
||||
|
||||
template <typename T, T N>
|
||||
struct DefaultValued {
|
||||
T value = N;
|
||||
};
|
||||
|
||||
|
||||
map<TypeInfo, DefaultValued<ssize_t, -1>> type_indexes;
|
||||
vector<InfoBrick*> bricks;
|
||||
mutex bricks_lock;
|
||||
|
||||
void allocated(const char* name, void* address) {
|
||||
#ifdef NO_IMPL
|
||||
return;
|
||||
#else
|
||||
#ifdef MEMTRACK_VERBOSE
|
||||
logTrace(lstream << "[MEMORY] Allocated a new instance of '" << name << "' at " << address);
|
||||
#endif
|
||||
if(!should_track_mangled(name)) return;
|
||||
|
||||
lock_guard<mutex> lock(bricks_lock);
|
||||
TypeInfo local_info(name);
|
||||
auto& type_index = type_indexes[local_info];
|
||||
if(type_index.value == -1) {
|
||||
type_index.value = type_indexes.size() - 1;
|
||||
}
|
||||
|
||||
auto _value = (size_t) type_index.value;
|
||||
for(auto it = bricks.begin(); it != bricks.end(); it++)
|
||||
if((*it)->insert(_value, address)) return;
|
||||
bricks.push_back(new InfoBrick{});
|
||||
auto success = bricks.back()->insert(type_index.value, address);
|
||||
assert(success);
|
||||
#endif
|
||||
}
|
||||
|
||||
void freed(const char* name, void* address) {
|
||||
#ifdef NO_IMPL
|
||||
return;
|
||||
#else
|
||||
#ifdef MEMTRACK_VERBOSE
|
||||
logTrace(lstream << "[MEMORY] Deallocated a instance of '" << name << "' at " << address);
|
||||
#endif
|
||||
if(!should_track_mangled(name)) return;
|
||||
|
||||
lock_guard<mutex> lock(bricks_lock);
|
||||
TypeInfo local_info(name);
|
||||
auto& type_index = type_indexes[local_info];
|
||||
if(type_index.value == -1)
|
||||
type_index.value = type_indexes.size() - 1;
|
||||
|
||||
auto _value = (size_t) type_index.value;
|
||||
for (auto &brick : bricks)
|
||||
if(brick->remove(_value, address)) return;
|
||||
logError(lstream << "[MEMORY] Got deallocated notify, but never the allocated! (Address: " << address << " Name: " << name << ")");
|
||||
#endif
|
||||
}
|
||||
|
||||
void statistics() {
|
||||
#ifdef NO_IMPL
|
||||
logError("memtracker::statistics() does not work due compiler flags (NO_IMPL)");
|
||||
return;
|
||||
#else
|
||||
map<size_t, deque<void*>> objects;
|
||||
map<size_t, std::string> mapping;
|
||||
|
||||
{
|
||||
lock_guard<mutex> lock(bricks_lock);
|
||||
for(auto& brick : bricks)
|
||||
for(auto& entry : brick->entries)
|
||||
if(entry.address) {
|
||||
objects[entry.type].push_back(entry.address);
|
||||
}
|
||||
for(auto& type : type_indexes)
|
||||
mapping[type.second.value] = type.first.as_mangled();
|
||||
}
|
||||
|
||||
logMessage("Allocated object types: " + to_string(objects.size()));
|
||||
for(const auto& entry : objects) {
|
||||
logMessage(" " + mapping[entry.first] + ": " + to_string(entry.second.size()));
|
||||
if (entry.second.size() < 50) {
|
||||
stringstream ss;
|
||||
for (int index = 0; index < entry.second.size(); index++) {
|
||||
if (index % 16 == 0) {
|
||||
if (index + 1 >= entry.second.size()) break;
|
||||
if (index != 0)
|
||||
logMessage(ss.str());
|
||||
ss = stringstream();
|
||||
ss << " ";
|
||||
}
|
||||
ss << entry.second[index] << " ";
|
||||
}
|
||||
if (!ss.str().empty())
|
||||
logMessage(ss.str());
|
||||
} else {
|
||||
logMessage("<snipped>");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
|
||||
namespace memtrack {
|
||||
#define TRACK_OBJECT_ALLOCATION
|
||||
#ifdef TRACK_OBJECT_ALLOCATION
|
||||
extern void allocated(const char* name, void* address);
|
||||
extern void freed(const char* name, void* address);
|
||||
template <typename T>
|
||||
void allocated(void* address) { allocated(typeid(T).name(), address); }
|
||||
|
||||
template <typename T>
|
||||
void freed(void* address) { freed(typeid(T).name(), address); }
|
||||
|
||||
void statistics();
|
||||
#else
|
||||
template <typename... T>
|
||||
inline void __empty(...) { }
|
||||
|
||||
#define freed __empty
|
||||
#define allocated __empty
|
||||
|
||||
#define allocated_mangled __empty
|
||||
#define freed_mangled __empty
|
||||
|
||||
inline void statistics() {}
|
||||
#endif
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <in6addr.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
namespace net {
|
||||
inline std::string to_string(const in6_addr& address) {
|
||||
char buffer[INET6_ADDRSTRLEN];
|
||||
if(!inet_ntop(AF_INET6, (void*) &address, buffer, INET6_ADDRSTRLEN)) return "";
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
inline std::string to_string(const in_addr& address) {
|
||||
char buffer[INET_ADDRSTRLEN];
|
||||
if(!inet_ntop(AF_INET, (void*) &address, buffer, INET_ADDRSTRLEN)) return "";
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
inline std::string to_string(const sockaddr_storage& address, bool port = true) {
|
||||
switch(address.ss_family) {
|
||||
case AF_INET:
|
||||
return to_string(((sockaddr_in*) &address)->sin_addr) + (port ? ":" + std::to_string(htons(((sockaddr_in*) &address)->sin_port)) : "");
|
||||
case AF_INET6:
|
||||
return to_string(((sockaddr_in6*) &address)->sin6_addr) + (port ? ":" + std::to_string(htons(((sockaddr_in6*) &address)->sin6_port)) : "");
|
||||
default:
|
||||
return "unknown_type";
|
||||
}
|
||||
}
|
||||
|
||||
inline uint16_t port(const sockaddr_storage& address) {
|
||||
switch(address.ss_family) {
|
||||
case AF_INET:
|
||||
return htons(((sockaddr_in*) &address)->sin_port);
|
||||
case AF_INET6:
|
||||
return htons(((sockaddr_in6*) &address)->sin6_port);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline socklen_t address_size(const sockaddr_storage& address) {
|
||||
switch (address.ss_family) {
|
||||
case AF_INET: return sizeof(sockaddr_in);
|
||||
case AF_INET6: return sizeof(sockaddr_in6);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool address_equal(const sockaddr_storage& a, const sockaddr_storage& b) {
|
||||
if(a.ss_family != b.ss_family) return false;
|
||||
if(a.ss_family == AF_INET) return ((sockaddr_in*) &a)->sin_addr.s_addr == ((sockaddr_in*) &b)->sin_addr.s_addr;
|
||||
else if(a.ss_family == AF_INET6) {
|
||||
#ifdef WIN32
|
||||
return memcmp(((sockaddr_in6*) &a)->sin6_addr.u.Byte, ((sockaddr_in6*) &b)->sin6_addr.u.Byte, 16) == 0;
|
||||
#else
|
||||
return memcmp(((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr8, ((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr8, 16) == 0;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool address_equal_ranged(const sockaddr_storage& a, const sockaddr_storage& b, uint8_t range) {
|
||||
if(a.ss_family != b.ss_family) return false;
|
||||
if(a.ss_family == AF_INET) {
|
||||
auto address_a = ((sockaddr_in*) &a)->sin_addr.s_addr;
|
||||
auto address_b = ((sockaddr_in*) &b)->sin_addr.s_addr;
|
||||
|
||||
if(range > 32)
|
||||
range = 32;
|
||||
|
||||
range = (uint8_t) (32 - range);
|
||||
|
||||
address_a <<= range;
|
||||
address_b <<= range;
|
||||
|
||||
return address_a == address_b;
|
||||
} else if(a.ss_family == AF_INET6) {
|
||||
#ifdef WIN32
|
||||
throw std::runtime_error("not implemented");
|
||||
//FIXME: Implement me!
|
||||
#elif defined(__x86_64__) && false
|
||||
static_assert(sizeof(__int128) == 16);
|
||||
auto address_a = (__int128) ((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr32;
|
||||
auto address_b = (__int128) ((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr32;
|
||||
|
||||
if(range > 128)
|
||||
range = 128;
|
||||
range = (uint8_t) (128 - range);
|
||||
|
||||
address_a <<= range;
|
||||
address_b <<= range;
|
||||
|
||||
return address_a == address_b;
|
||||
#else
|
||||
static_assert(sizeof(uint64_t) == 8);
|
||||
|
||||
if(range > 128)
|
||||
range = 128;
|
||||
range = (uint8_t) (128 - range);
|
||||
|
||||
|
||||
auto address_ah = (uint64_t) (((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr8 + 0);
|
||||
auto address_al = (uint64_t) (((sockaddr_in6*) &a)->sin6_addr.__in6_u.__u6_addr8 + 8);
|
||||
auto address_bh = (uint64_t) (((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr8 + 0);
|
||||
auto address_bl = (uint64_t) (((sockaddr_in6*) &b)->sin6_addr.__in6_u.__u6_addr8 + 8);
|
||||
|
||||
if(range > 64) {
|
||||
/* only lower counts */
|
||||
return (address_al << (range - 64)) == (address_bl << (range - 64));
|
||||
} else {
|
||||
return address_al == address_bl &&(address_bh << (range - 64)) == (address_ah << (range - 64));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
inline bool is_ipv6(const std::string& str) {
|
||||
sockaddr_in6 sa{};
|
||||
return inet_pton(AF_INET6, str.c_str(), &(sa.sin6_addr)) != 0;
|
||||
}
|
||||
|
||||
inline bool is_ipv4(const std::string& str) {
|
||||
sockaddr_in sa{};
|
||||
return inet_pton(AF_INET, str.c_str(), &(sa.sin_addr)) != 0;
|
||||
}
|
||||
|
||||
inline bool is_anybind(sockaddr_storage& storage) {
|
||||
if(storage.ss_family == AF_INET) {
|
||||
auto data = (sockaddr_in*) &storage;
|
||||
return data->sin_addr.s_addr == 0;
|
||||
} else if(storage.ss_family == AF_INET6) {
|
||||
auto data = (sockaddr_in6*) &storage;
|
||||
#ifdef WIN32
|
||||
auto& blocks = data->sin6_addr.u.Word;
|
||||
return
|
||||
blocks[0] == 0 &&
|
||||
blocks[1] == 0 &&
|
||||
blocks[2] == 0 &&
|
||||
blocks[3] == 0 &&
|
||||
blocks[4] == 0 &&
|
||||
blocks[5] == 0 &&
|
||||
blocks[6] == 0 &&
|
||||
blocks[7] == 0;
|
||||
#else
|
||||
auto& blocks = data->sin6_addr.__in6_u.__u6_addr32;
|
||||
return blocks[0] == 0 && blocks[1] == 0 && blocks[2] == 0 && blocks[3] == 0;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool resolve_address(const std::string& address, sockaddr_storage& result) {
|
||||
if(is_ipv4(address)) {
|
||||
sockaddr_in s{};
|
||||
s.sin_port = 0;
|
||||
s.sin_family = AF_INET;
|
||||
|
||||
auto record = gethostbyname(address.c_str());
|
||||
if(!record)
|
||||
return false;
|
||||
s.sin_addr.s_addr = ((in_addr*) record->h_addr)->s_addr;
|
||||
|
||||
memcpy(&result, &s, sizeof(s));
|
||||
return true;
|
||||
} else if(is_ipv6(address)) {
|
||||
sockaddr_in6 s{};
|
||||
s.sin6_family = AF_INET6;
|
||||
s.sin6_port = 0;
|
||||
s.sin6_flowinfo = 0;
|
||||
s.sin6_scope_id = 0;
|
||||
|
||||
#ifdef WIN32
|
||||
auto record = gethostbyname(address.c_str());
|
||||
#else
|
||||
auto record = gethostbyname2(address.c_str(), AF_INET6);
|
||||
#endif
|
||||
if(!record) return false;
|
||||
s.sin6_addr = *(in6_addr*) record->h_addr;
|
||||
|
||||
memcpy(&result, &s, sizeof(s));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,527 @@
|
||||
/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */
|
||||
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* lists, simple queues, tail queues, and circular queues.
|
||||
*
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
#ifdef QUEUE_MACRO_DEBUG
|
||||
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
|
||||
#else
|
||||
#define _Q_INVALIDATE(a)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List access methods.
|
||||
*/
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
#define SLIST_END(head) NULL
|
||||
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for((var) = SLIST_FIRST(head); \
|
||||
(var) != SLIST_END(head); \
|
||||
(var) = SLIST_NEXT(var, field))
|
||||
|
||||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
|
||||
for ((varp) = &SLIST_FIRST((head)); \
|
||||
((var) = *(varp)) != SLIST_END(head); \
|
||||
(varp) = &SLIST_NEXT((var), field))
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) { \
|
||||
SLIST_FIRST(head) = SLIST_END(head); \
|
||||
}
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_NEXT(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->slh_first; \
|
||||
\
|
||||
while (curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = \
|
||||
curelm->field.sle_next->field.sle_next; \
|
||||
_Q_INVALIDATE((elm)->field.sle_next); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List access methods
|
||||
*/
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
#define LIST_END(head) NULL
|
||||
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for((var) = LIST_FIRST(head); \
|
||||
(var)!= LIST_END(head); \
|
||||
(var) = LIST_NEXT(var, field))
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST(head) = LIST_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||
(elm2)->field.le_next->field.le_prev = \
|
||||
&(elm2)->field.le_next; \
|
||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm2)->field.le_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define SIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define SIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define SIMPLEQ_END(head) NULL
|
||||
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
|
||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||
for((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) != SIMPLEQ_END(head); \
|
||||
(var) = SIMPLEQ_NEXT(var, field))
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* tail queue access methods
|
||||
*/
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_END(head) NULL
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
/* XXX */
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
#define TAILQ_EMPTY(head) \
|
||||
(TAILQ_FIRST(head) == TAILQ_END(head))
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_NEXT(var, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_PREV(var, headname, field))
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm2)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
*(elm2)->field.tqe_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
||||
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue access methods
|
||||
*/
|
||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||
#define CIRCLEQ_END(head) ((void *)(head))
|
||||
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
||||
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
||||
#define CIRCLEQ_EMPTY(head) \
|
||||
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
|
||||
|
||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||
for((var) = CIRCLEQ_FIRST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_NEXT(var, field))
|
||||
|
||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
||||
for((var) = CIRCLEQ_LAST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_PREV(var, field))
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
(head)->cqh_first = CIRCLEQ_END(head); \
|
||||
(head)->cqh_last = CIRCLEQ_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
||||
if ((head)->cqh_last == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = CIRCLEQ_END(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head).cqh_last = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
|
||||
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head).cqh_first = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "rnd.h"
|
||||
|
||||
const char* rnd_string_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
std::string rnd_string(int length, const char* source) {
|
||||
char* buffer = new char[length];
|
||||
auto source_length = strlen(source);
|
||||
|
||||
std::default_random_engine generator{0};
|
||||
generator.seed(static_cast<unsigned long>(std::chrono::system_clock::now().time_since_epoch().count()));
|
||||
std::uniform_int_distribution<int> gen(0, static_cast<int>(source_length - 1));
|
||||
for(int i = 0; i < length; i++){
|
||||
buffer[i] = source[gen(generator)];
|
||||
}
|
||||
|
||||
auto result = std::string(buffer, static_cast<unsigned long>(length));
|
||||
delete[] buffer;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <random>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
|
||||
extern const char* rnd_string_chars; //"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
extern std::string rnd_string(int length = 20, const char* avariable = rnd_string_chars);
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
|
||||
//#define ALLOW_ASSERT
|
||||
#ifdef ALLOW_ASSERT
|
||||
#define sassert(exp) assert(exp)
|
||||
#else
|
||||
#define S(s) #s
|
||||
#define sassert(exp) \
|
||||
do { \
|
||||
if(!(exp)) \
|
||||
logCritical(0, "Soft assertion @{}:{} '{}' failed! This could cause fatal fails!", __FILE__, __LINE__, #exp); \
|
||||
} while(0)
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
class spin_lock {
|
||||
std::atomic_flag locked = ATOMIC_FLAG_INIT;
|
||||
public:
|
||||
void lock() {
|
||||
uint8_t round = 0;
|
||||
while (locked.test_and_set(std::memory_order_acquire)) {
|
||||
//Yield when we're using this lock for a longer time, which we usually not doing
|
||||
if(round++ % 8 == 0)
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool try_lock() {
|
||||
return !locked.test_and_set(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
locked.clear(std::memory_order_release);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#if __cplusplus <= 201103L
|
||||
namespace std {
|
||||
template<class T> struct _Unique_if {
|
||||
typedef unique_ptr<T> _Single_object;
|
||||
};
|
||||
|
||||
template<class T> struct _Unique_if<T[]> {
|
||||
typedef unique_ptr<T[]> _Unknown_bound;
|
||||
};
|
||||
|
||||
template<class T, size_t N> struct _Unique_if<T[N]> {
|
||||
typedef void _Known_bound;
|
||||
};
|
||||
|
||||
template<class T, class... Args>
|
||||
typename _Unique_if<T>::_Single_object
|
||||
make_unique(Args&&... args) {
|
||||
return unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename _Unique_if<T>::_Unknown_bound
|
||||
make_unique(size_t n) {
|
||||
typedef typename remove_extent<T>::type U;
|
||||
return unique_ptr<T>(new U[n]());
|
||||
}
|
||||
|
||||
template<class T, class... Args>
|
||||
typename _Unique_if<T>::_Known_bound
|
||||
make_unique(Args&&...) = delete;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "time.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace chrono;
|
||||
|
||||
struct TimeEntry {
|
||||
std::string indice;
|
||||
std::function<nanoseconds(const std::string&)> parser;
|
||||
};
|
||||
|
||||
auto parsers = std::vector<TimeEntry>({
|
||||
{"h", [](const std::string& number) -> nanoseconds { return hours(stoll(number)); }},
|
||||
{"m", [](const std::string& number) -> nanoseconds { return minutes(stoll(number)); }},
|
||||
{"s", [](const std::string& number) -> nanoseconds { return seconds(stoll(number)); }},
|
||||
{"ms", [](const std::string& number) -> nanoseconds { return milliseconds(stoll(number)); }},
|
||||
{"us", [](const std::string& number) -> nanoseconds { return microseconds(stoll(number)); }},
|
||||
{"ns", [](const std::string& number) -> nanoseconds { return nanoseconds(stoll(number)); }}
|
||||
});
|
||||
|
||||
std::chrono::nanoseconds period::parse(const std::string& input, std::string& error) {
|
||||
nanoseconds result{};
|
||||
|
||||
size_t index = 0;
|
||||
do {
|
||||
auto found = input.find(':', index);
|
||||
auto str = input.substr(index, found - index);
|
||||
|
||||
auto indiceIndex = str.find_first_not_of("0123456789");
|
||||
if(indiceIndex == std::string::npos) {
|
||||
error = "Missing indice for " + str + " at " + to_string(index);
|
||||
return nanoseconds(0);
|
||||
}
|
||||
auto indice = str.substr(indiceIndex);
|
||||
auto number = str.substr(0, indiceIndex);
|
||||
|
||||
bool foundIndice = false;
|
||||
for(const auto& parser : parsers) {
|
||||
if(parser.indice == indice) {
|
||||
if(number.length() == 0) {
|
||||
error = "Invalid number at " + to_string(index);
|
||||
return nanoseconds(0);
|
||||
}
|
||||
result += parser.parser(number);
|
||||
foundIndice = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!foundIndice) {
|
||||
error = "Invalid indice for " + str + " at " + to_string(index + indiceIndex);
|
||||
return nanoseconds(0);
|
||||
}
|
||||
|
||||
index = found + 1;
|
||||
} while(index != 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
|
||||
namespace period {
|
||||
std::chrono::nanoseconds parse(const std::string&, std::string&);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#define TIMING_START(_name) \
|
||||
struct { \
|
||||
struct entry { \
|
||||
std::string name; \
|
||||
std::chrono::system_clock::time_point ts; \
|
||||
}; \
|
||||
\
|
||||
std::string name; \
|
||||
std::chrono::system_clock::time_point begin; \
|
||||
std::chrono::system_clock::time_point end; \
|
||||
std::deque<entry> timings; \
|
||||
} _name ##_timings; \
|
||||
_name ##_timings.begin = std::chrono::system_clock::now(); \
|
||||
|
||||
#define TIMING_STEP(name, step) \
|
||||
name ##_timings.timings.push_back({step, std::chrono::system_clock::now()});
|
||||
|
||||
#define TIMING_FINISH(_name) \
|
||||
([&](){ \
|
||||
_name ##_timings.end = std::chrono::system_clock::now(); \
|
||||
std::string result; \
|
||||
result = "timings for " + _name ##_timings.name + ": "; \
|
||||
result += std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(_name ##_timings.end - _name ##_timings.begin).count()) + "ms"; \
|
||||
\
|
||||
auto tp = _name ##_timings.begin; \
|
||||
for(const auto& entry : _name ##_timings.timings) { \
|
||||
result += "\n "; \
|
||||
result += "- " + entry.name + ": "; \
|
||||
result += "@" + std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(entry.ts - _name ##_timings.begin).count()) + "ms"; \
|
||||
result += ": " + std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(entry.ts - tp).count()) + "ms"; \
|
||||
tp = entry.ts; \
|
||||
} \
|
||||
return result; \
|
||||
})()
|
||||
Reference in New Issue
Block a user