Initial commit

This commit is contained in:
WolverinDEV
2019-06-26 22:11:22 +02:00
commit ff36addda5
99 changed files with 18395 additions and 0 deletions
+119
View File
@@ -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) {}
}
+56
View File
@@ -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);
};
+299
View File
@@ -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;
}
}
}
+71
View File
@@ -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()); }
//AZ, az, 09, + 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()); }
+24
View File
@@ -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);
}
+58
View File
@@ -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
+110
View File
@@ -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;
}
+20
View File
@@ -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);
}
}
+59
View File
@@ -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...> { };
};
+227
View File
@@ -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
}
}
+30
View File
@@ -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
View File
@@ -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;
}
}
+527
View File
@@ -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_ */
+18
View File
@@ -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;
}
+8
View File
@@ -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);
+15
View File
@@ -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
+25
View File
@@ -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);
}
};
+38
View File
@@ -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
+59
View File
@@ -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;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <string>
#include <chrono>
#include <deque>
namespace period {
std::chrono::nanoseconds parse(const std::string&, std::string&);
}
+36
View File
@@ -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; \
})()