#include "drvutil.hpp" #include #include #include #include #include #include #include #include #if defined(_WIN32) #include #include #elif defined(__linux__) #include #endif 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]); } bool is_single_wchar_t(char32_t c) { if ((c >= 0xD800) && (c <= 0xDFFF)) return false; if ((c >= 0) && (c <= 0xFFFF)) return true; return false; } static int buffer_codepoint_utf8(char32_t scp, char *buffer) { uint32_t cp = (uint32_t)scp; unsigned char *c = (unsigned char *)buffer; if (cp < 0) { return 0; } else if (cp <= 0x7F) { c[0] = cp; return 1; } else if (cp <= 0x7FF) { c[0] = (cp>>6)+192; c[1] = (cp&63)+128; return 2; } else if (cp <= 0xFFFF) { if ((cp >= 0xD800) && (cp <= 0xDFFF)) { return 0; } c[0] = (cp>>12)+224; c[1] = ((cp>>6)&63)+128; c[2] = (cp&63)+128; return 3; } else if (cp <= 0x10FFFF) { c[0] = (cp>>18)+240; c[1] = ((cp>>12)&63)+128; c[2] = ((cp>>6)&63)+128; c[3] = (cp&63)+128; return 4; } else { return 0; } } static int32_t read_codepoint_utf8(std::string_view &source) { size_t size = source.size(); if (size == 0) return -1; const unsigned char *bytes = (const unsigned char *)source.data(); int codepoint; size_t seqlen; if ((bytes[0] & 0x80) == 0x00) { // U+0000 to U+007F codepoint = (bytes[0] & 0x7F); seqlen = 1; } else if ((bytes[0] & 0xE0) == 0xC0) { // U+0080 to U+07FF codepoint = (bytes[0] & 0x1F); seqlen = 2; } else if ((bytes[0] & 0xF0) == 0xE0) { // U+0800 to U+FFFF codepoint = (bytes[0] & 0x0F); seqlen = 3; } else if ((bytes[0] & 0xF8) == 0xF0) { // U+10000 to U+10FFFF codepoint = (bytes[0] & 0x07); seqlen = 4; } else { // Bad character. return invalid CP. return -2; } if (seqlen > size) { return -1; } for (size_t i = 1; i < seqlen; ++i) { if ((bytes[i] & 0xC0) != 0x80) { // Bad character. return invalid CP. return -2; } codepoint = (codepoint << 6) | (bytes[i] & 0x3F); } if ((codepoint > 0x10FFFF) || ((codepoint >= 0xD800) && (codepoint <= 0xDFFF)) || ((codepoint <= 0x007F) && (seqlen != 1)) || ((codepoint >= 0x0080) && (codepoint <= 0x07FF) && (seqlen != 2)) || ((codepoint >= 0x0800) && (codepoint <= 0xFFFF) && (seqlen != 3)) || ((codepoint >= 0x10000) && (codepoint <= 0x1FFFFF) && (seqlen != 4))) { // Bad character. return invalid CP. return -2; } source.remove_prefix(seqlen); return codepoint; } std::string to_utf8(const std::u32string &s) { std::string result(s.size() * 4, 0); char *buffer = &result[0]; int len = 0; for (char32_t c : s) { int clen = buffer_codepoint_utf8(c, buffer + len); len += clen; } return result.substr(0, len); } std::u32string from_utf8(std::string_view s, int *consumed) { std::string_view rest = s; std::u32string result(s.size(), 0); int len = 0; while (true) { int32_t c = read_codepoint_utf8(rest); if (c == -1) { break; // EOF reached; } else if (c < 0) { rest.remove_prefix(1); } else { result[len++] = (char32_t)c; } } if (consumed != nullptr) { *consumed = s.size() - rest.size(); } return result.substr(0, len); } 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 std::filesystem::path &fn, std::string &err) { std::ifstream t(fn); if (t.fail()) { err = std::string("Could not open ") + fn.u8string(); 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.u8string(); 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 std::filesystem::path &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::filesystem::path &base, std::ostream *s) { std::string err; std::filesystem::path cfn = base / "lua/control.lst"; std::string ctrl = read_file(cfn, 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::filesystem::path lfn = base / "lua" / names[i]; if (!sbwrite_file(s, lfn)) { return std::string("Cannot read source file: ") + lfn.u8string(); } } 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