#include "drvutil.hpp" #include #include #include #include #include #include #include namespace drvutil { inline static bool ascii_isspace(char c) { return (c==' ')||(c=='\t')||(c=='\r')||(c=='\n')||(c=='\f')||(c=='\v'); } std::string_view trim(std::string_view v) { while ((!v.empty()) && (ascii_isspace(v.front()))) { v.remove_prefix(1); } while ((!v.empty()) && (ascii_isspace(v.back()))) { v.remove_suffix(1); } return v; } static std::string_view read_to_line(std::string_view &source) { size_t pos = source.find('\n'); std::string_view result; if (pos == std::string_view::npos) { result = source; source = std::string_view(); } else { result = source.substr(0, pos); source = source.substr(pos + 1); } if ((!result.empty()) && (result.back() == '\r')) { result.remove_suffix(1); } return result; } std::vector split_view(std::string_view v, char sep) { std::vector result; while (true) { size_t pos = v.find(sep); if (pos == std::string_view::npos) break; result.push_back(v.substr(0, pos)); v = v.substr(pos + 1); } result.push_back(v); return result; } void split_target(std::string_view target, std::string &cert, std::string &host, std::string &port) { std::vector split = split_view(target, ':'); if (split.size() != 3) { cert.clear(); host.clear(); port.clear(); return; } if (split[0].empty() || split[1].empty() || split[2].empty()) { cert.clear(); host.clear(); port.clear(); return; } cert = std::string(split[0]); host = std::string(split[1]); port = std::string(split[2]); } static std::vector parse_control_lst(std::string_view ctrl) { std::vector result; while (!ctrl.empty()) { std::string_view line = read_to_line(ctrl); std::string_view trimmed = trim(line); if ((trimmed.size() > 0) && (trimmed[0] != '#')) { result.emplace_back(trimmed); } } return result; } // Read a source file into a string. // static std::string read_file(const char *fn, std::string &err) { std::ifstream t(fn); if (t.fail()) { err = std::string("Could not open ") + fn; return ""; } t.seekg(0, std::ios::end); size_t size = t.tellg(); std::string result(size, ' '); t.seekg(0); t.read(&result[0], size); if ((t.fail()) || (size_t(t.tellg()) != size)) { err = std::string("Could not read ") + fn; return ""; } err = ""; return result; } // This encoding can be read by StreamBuffer::read_uint32. // static void sbwrite_uint32(std::ostream *s, uint32_t v) { s->write((const char *)&v, 4); } // This encoding can be read by StreamBuffer::read_uint64. // static void sbwrite_uint64(std::ostream *s, uint64_t v) { s->write((const char *)&v, 8); } // This encoding can be read by StreamBuffer::read_string. // static void sbwrite_string(std::ostream *s, std::string_view sv) { s->put(0xFF); sbwrite_uint64(s, sv.size()); s->write(sv.data(), sv.size()); } // This encoding can be read by StreamBuffer::read_string. // static bool sbwrite_file(std::ostream *s, const char *fn) { s->put(0xFF); uint64_t pos1 = s->tellp(); sbwrite_uint64(s, 0); uint64_t pos2 = s->tellp(); std::ifstream t(fn); if (t.fail()) { return false; } *s << t.rdbuf(); if (t.fail()) { return false; } uint64_t pos3 = s->tellp(); s->seekp(pos1); sbwrite_uint64(s, pos3 - pos2); s->seekp(pos3); return true; } std::string package_lua_source(const std::string &base, std::ostream *s) { std::string err; std::string cfn = base + "/lua/control.lst"; std::string ctrl = read_file(cfn.c_str(), err); if (!err.empty()) { return err; } std::vector names = parse_control_lst(ctrl); sbwrite_uint32(s, names.size()); for (int i = 0; i < int(names.size()); i++) { sbwrite_string(s, names[i]); } for (int i = 0; i < int(names.size()); i++) { std::string lfn = base + "/lua/" + names[i]; if (!sbwrite_file(s, lfn.c_str())) { return std::string("Cannot read source file: ") + lfn; } } return ""; } // strerror has to be the most overcomplicated function imaginable. The simple // version, 'strerror', is not thread-safe, and the improved versions are all // incompatible from OS to OS. Even different versions of linux aren't // compatible. A lot of conditional compilation is needed. #if defined(__linux__) inline static void strerror_helper(int status, int errnum, char errbuf[256]) { if (status != 0) { snprintf(errbuf, 256, "unknown errno %d", errnum); } } inline static void strerror_helper(const char *result, int errnum, char errbuf[256]) { if (result != errbuf) { snprintf(errbuf, 256, "%s", result); } } void strerror_safe(int errnum, char errbuf[256]) { auto rval = strerror_r(errnum, errbuf, 256); strerror_helper(rval, errnum, errbuf); } #elif defined(_WIN32) void strerror_safe(int errnum, char errbuf[256]) { int status = strerror_s(errbuf, 256, errnum); if (status != 0) { snprintf(errbuf, 256, "unknown errno %d", errnum); } ); #endif std::string strerror_str(int errnum) { char buf[256]; strerror_safe(errnum, buf); return buf; } // The monotonic clock is required to start at zero at initialization time, // advance steadily, and never go backwards. It is okay, however, if it is a // little inaccurate, or if it drifts a little over time. #if defined(__linux__) class MonoClock { private: struct timespec base_; public: MonoClock() { int status = clock_gettime(CLOCK_MONOTONIC, &base_); assert(status == 0); } double get() { struct timespec t; int status = clock_gettime(CLOCK_MONOTONIC, &t); assert(status == 0); double tv_sec = t.tv_sec - base_.tv_sec; double tv_nsec = t.tv_nsec - base_.tv_nsec; return tv_sec + (tv_nsec * 1.0E-9); } }; #elif defined(_WIN32) class MonoClock { public: double freq_; LONGLONG base_; inline LONGLONG qpc() { LARGE_INTEGER x; BOOL status = QueryPerformanceCounter(&x); assert(status != 0); return x.QuadPart; } MonoClock() { LARGE_INTEGER x; BOOL status = QueryPerformanceFrequency(&x); assert(status != 0); freq_ = 1.0 / double(x.QuadPart); base_ = qpc(); } double get() { return (qpc() - base) * freq_; } }; #else #error "Only support __linux__ or _WIN32" #endif static MonoClock monoclock; double get_monotonic_clock() { return monoclock.get(); } } // namespace drv