diff --git a/Core/Base/JSON.cpp b/Core/Base/JSON.cpp index 37601da..3395022 100644 --- a/Core/Base/JSON.cpp +++ b/Core/Base/JSON.cpp @@ -5,487 +5,536 @@ #include #define HANDLE_ERROR(reason) \ JSON::handle_error(__func__, reason, __FILE__, __LINE__); + namespace Psc { -std::function -JSON::handle_error = [](const char *function_name, - std::string_view reason, const char *file, - int line) { - std::ostringstream oss; - oss << "JSON::" << function_name << " 原因:" << reason << std::endl; - oss << "位置:" << file << ":" << line << std::endl; - std::cout << oss.str(); - Psc::fail_fast(); -}; -void skip_whitespace(std::string& s, size_t& i); -bool parse_string(std::string& s, size_t& i, std::string& out, - std::error_code& ec) noexcept; -bool parse_number(std::string& s, size_t& i, std::string& out, - std::error_code& ec) noexcept; -bool parse_object(std::string& s, size_t& i, JSON& out, - std::error_code& ec) noexcept; -bool parse_array(std::string& s, size_t& i, JSON& out, - std::error_code& ec) noexcept; -std::ostream& operator<<(std::ostream& os, const JSON& obj) { - os << const_cast(obj).to_json_string(); - return os; -} -JSON::operator std::string() const { - return const_cast(this)->to_json_string(); -} -PSC_DEFINE_TRIPLE_API_CONST_METHOD_FROM_BOOL(std::string, JSON, get_string, - PSC_JSON_KEY_PARAMS) -bool JSON::get_string_ec(std::string_view key, std::string& out, - std::error_code& ec) const noexcept { - const JSON *that = get(key); - if (!that) { - ec = std::make_error_code(std::errc::invalid_argument); - return false; + std::function + JSON::handle_error = [](const char *function_name, + std::string_view reason, const char *file, + int line) { + std::ostringstream oss; + oss << "JSON::" << function_name << " 原因:" << reason << std::endl; + oss << "位置:" << file << ":" << line << std::endl; + std::cout << oss.str(); + Psc::fail_fast(); + }; + + void skip_whitespace(std::string &s, size_t &i); + + bool parse_string(std::string &s, size_t &i, std::string &out, + std::error_code &ec) noexcept; + + bool parse_number(std::string &s, size_t &i, std::string &out, + std::error_code &ec) noexcept; + + bool parse_object(std::string &s, size_t &i, JSON &out, + std::error_code &ec) noexcept; + + bool parse_array(std::string &s, size_t &i, JSON &out, + std::error_code &ec) noexcept; + + std::ostream &operator<<(std::ostream &os, const JSON &obj) { + os << const_cast(obj).to_json_string(); + return os; } - out = that->val; - return true; -} -PSC_DEFINE_TRIPLE_API_CONST_METHOD_FROM_BOOL_0(bool, JSON, bool_val) -bool JSON::bool_val_ec(bool& out, std::error_code& ec) const noexcept { - if (val != "true" && val != "false") { - // HANDLE_ERROR(key + " Invalid bool argument: " + val) - ec = std::make_error_code(std::errc::invalid_argument); - return false; + + JSON::operator std::string() const { + return const_cast(this)->to_json_string(); } - out = (val == "true"); - return true; -} -PSC_DEFINE_TRIPLE_API_CONST_METHOD_FROM_BOOL(bool, JSON, get_bool, - PSC_JSON_KEY_PARAMS) -bool JSON::get_bool_ec(std::string_view key, bool& out, - std::error_code& ec) const noexcept { - const Psc::JSON *that = get(key); - if (!that) { - ec = std::make_error_code(std::errc::invalid_argument); - return false; - } - return that->bool_val_ec(out, ec); -} -JSON::JSON(std::string_view key, const JSON& children) : key(key), val(children.val), valueType(children.valueType), - children(children.children) {} -void JSON::append_list(const std::vector& arr) { -#ifdef _DEBUG - if (valueType != JsonType::Object && valueType != JsonType::Array) { - HANDLE_ERROR("append_list error! key:" + key + " not array or object " + - to_json_string()) - } -#else -#endif - for (const JSON& j : arr) { - children.push_back(j); - } -} -void JSON::append(const JSON& json) { -#ifdef _DEBUG - if (valueType != JsonType::Object && valueType != JsonType::Array) { - HANDLE_ERROR("append_list error! key:" + key + " not array or object " + - to_json_string()) - } -#else -#endif - children.push_back(json); -} -void JSON::prepend(const JSON& json) { -#ifdef _DEBUG - if (valueType != JsonType::Object && valueType != JsonType::Array) { - HANDLE_ERROR("append_list error! key:" + key + " not array or object " + - to_json_string()) - } -#else -#endif - children.insert(children.begin(), json); -} -bool JSON::has(std::string_view _key) const { - for (const JSON& child : children) { - if (child.key == _key) { - return true; - } - } - return false; -} -const JSON* JSON::get(std::string_view _key) const { - for (const JSON& child : children) { - if (child.key == _key) { - return &child; - } - } - // HANDLE_ERROR(_key + " not found!" + to_json_string()) - return nullptr; -} -JSON JSON::array(const std::vector& children) { - return array("", children); -} -JSON JSON::object(const std::vector& children) { - return object("", children); -} -JSON JSON::array(std::string_view key, const std::vector& children) { - JSON ret; - ret.key = std::string(key); - ret.valueType = Array; - ret.children = children; - return ret; -} -JSON JSON::object(std::string_view key, const std::vector& children) { - JSON ret; - ret.key = std::string(key); - ret.valueType = Object; - ret.children = children; - return ret; -} -std::string JSON::get_indent(const int& indent, std::string_view indentStr) { - std::string ret; - for (int i = 0; i < indent; ++i) { - ret.append(indentStr); - } - return ret; -} -void JSON::append_value_string(std::string& ret, const int& indent, bool isEnd, - std::string_view object_split, - std::string_view array_split, - std::string_view indentStr, - JsonType fatherType) const { - ret.append(get_indent(indent, indentStr)); - if (fatherType == Object) ret.append("\"" + key + "\"" + ": "); - const std::string split = std::string(fatherType == Array ? array_split : object_split); - if (valueType == String) { - ret.append("\"" + val + "\""); - goto end; - } - if (valueType == Null) { - ret.append("null"); - goto end; - } - if (valueType == Number || valueType == Bool) { - ret.append(val); - goto end; - } - if (valueType == Object) { - ret.append("{"); - ret.append(object_split); - size_t n = children.size(); - if (n) { - for (size_t i = 0; i < n - 1; ++i) { - children[i].append_value_string(ret, indent + 2, false, object_split, - array_split, indentStr, Object); - } - children[n - 1].append_value_string(ret, indent + 2, true, object_split, - array_split, indentStr, Object); - } - ret.append(get_indent(indent, indentStr) + "}"); - goto end; - } - if (valueType == Array) { - ret.append("["); - ret.append(array_split); - size_t n = children.size(); - if (n) { - for (size_t i = 0; i < n - 1; ++i) { - children[i].append_value_string(ret, indent + 1, false, object_split, - array_split, indentStr, Array); - } - children[n - 1].append_value_string(ret, indent + 1, true, object_split, - array_split, indentStr, Array); - } - ret.append(get_indent(indent, indentStr) + "]"); - goto end; - } -end: - if (!isEnd) ret.append(","); - ret.append(split); -} -void handleBlank(std::string& s) { - int n = static_cast(s.length()); - bool isInStr = false; - for (int i = n - 1; i >= 0; --i) { - const char& c = s[i]; - if (c == '\"') isInStr = !isInStr; - if (isInStr) continue; - if (c == '\n' || c == '\t' || c == ' ') { - s.erase(i, 1); - } - } -} -std::string JSON::to_json_string(std::string_view object_split, - std::string_view array_split, - std::string_view indentStr, - const int& indent) const { - if (valueType == Bool) return val; - if (valueType == String) return '\"' + val + '\"'; - if (valueType == Number) return val; - if (valueType == Null) { - if (children.size() != 0) { - HANDLE_ERROR("null 带有children 转成了字符串 (" + key + "," + val + ")" + - to_json_string()) - } - return "null"; - } - std::string ret; - append_value_string(ret, indent, true, object_split, array_split, indentStr, - Null); - return ret; -} -JSON::JSON() = default; -void copy(JSON& dest, const JSON& src) { - dest.key = src.key; - dest.valueType = src.valueType; - dest.val = src.val; - auto len = src.children.size(); - dest.children.resize(len); - for (size_t i = 0; i < len; ++i) { - dest.children[i] = src.children[i]; - } -} -JSON::JSON(const JSON& other) { - copy(*this, other); -} -JSON::JSON(JSON&& other) noexcept { - key = std::move(other.key); - valueType = std::move(other.valueType); - children = std::move(other.children); - val = std::move(other.val); -} -JSON& JSON::operator=(const JSON& other) { - if (this != &other) { - // 防止自赋值 - copy(*this, other); // 调用拷贝函数 - } - return *this; -} -JSON& JSON::operator=(JSON&& other) noexcept { - if (this != &other) { - key = std::move(other.key); - valueType = other.valueType; - children = std::move(other.children); - val = std::move(other.val); - } - return *this; -} -JSON& JSON::operator+=(const JSON& child) { - append_list(child.children); - return *this; -} -JSON& JSON::operator+=(JSON&& child) { - append_list(child.children); - return *this; -} -// 解析的分割线---------------------------- -// 如果未找到非空白字符,将 i 更新为 std::string::npos -void skip_whitespace(std::string& s, size_t& i) { - i = s.find_first_not_of(" \t\n\r\f\v", i); -} -// 传入的 i 是 "所在的index -bool parse_string(std::string& s, size_t& i, std::string& out, - std::error_code& ec) noexcept { - ec.clear(); - i++; // skip '"' - if (s[i] == '"') { - i++; - out.clear(); - return true; - } - std::string ret; - while (true) { - if (s[i] == '"' && s[i - 1] != '\\') { - break; - } - ret += s[i]; - i++; - } - i++; - out = std::move(ret); - return true; -} -bool parse_number(std::string& s, size_t& i, std::string& out, - std::error_code& ec) noexcept { - ec.clear(); - std::string num; - while (isdigit(s[i])) { - num += s[i++]; - } - if (s[i] == '.') { - i++; - num += '.'; - while (isdigit(s[i])) { - num += s[i++]; - } - } - out = std::move(num); - return true; -} -bool parse_json_EX(std::string& s, size_t& i, JSON& out, - std::error_code& ec) noexcept; -PSC_DEFINE_TRIPLE_API_FROM_BOOL(JSON, parse_json, PSC_JSON_PARSE_PARAMS) -bool parse_json_ec(std::string_view text, JSON& out, - std::error_code& ec) noexcept { - auto t = std::string(text); - size_t i = 0; - return parse_json_EX(t, i, out, ec); -} -bool parse_json_EX(std::string& s, size_t& i, JSON& out, - std::error_code& ec) noexcept { - skip_whitespace(s, i); - switch (s[i]) { - case 'n': i += 4; - out = JSON(nullptr); - return true; - case 't': i += 4; - out = JSON(true); - return true; - case 'f': i += 5; - out = JSON(false); - return true; - case '{': return parse_object(s, i, out, ec); - case '[': return parse_array(s, i, out, ec); - case '"': { - std::string str; - if (!parse_string(s, i, str, ec)) return false; - out = JSON(std::move(str)); - return true; - } - case '-': { - i++; // skip - - JSON ret(nullptr); - ret.valueType = Number; - std::string num; - if (!parse_number(s, i, num, ec)) return false; - ret.val = '-' + num; - out = std::move(ret); - return true; - } - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - JSON ret(nullptr); - ret.valueType = Number; - std::string num; - if (!parse_number(s, i, num, ec)) return false; - ret.val = std::move(num); - out = std::move(ret); - return true; - } - default: { - // HANDLE_ERROR("json 解析错误! " + s + "\n") - ec = std::make_error_code(std::errc::invalid_argument); - return false; - } - } -} -bool parse_object(std::string& s, size_t& i, JSON& out, - std::error_code& ec) noexcept { - i++; // skip '{' - skip_whitespace(s, i); - JSON ret = JSON::object(); - if (s[i] == '}') { - i++; - out = std::move(ret); - return true; - } - while (true) { - skip_whitespace(s, i); - std::string key; - if (!parse_string(s, i, key, ec)) return false; - skip_whitespace(s, i); - if (s[i] != ':') { + + PSC_DEFINE_TRIPLE_API_CONST_METHOD_FROM_BOOL(std::string, JSON, get_string, + PSC_JSON_KEY_PARAMS) + + bool JSON::get_string_ec(std::string_view key, std::string &out, + std::error_code &ec) const noexcept { + const JSON *that = get(key); + if (!that) { ec = std::make_error_code(std::errc::invalid_argument); return false; } - i++; - skip_whitespace(s, i); - JSON kv; - if (!parse_json_EX(s, i, kv, ec)) return false; - skip_whitespace(s, i); - kv.key = key; - ret.children.push_back(std::move(kv)); - if (s[i] == '}') { - i++; - break; + out = that->val; + return true; + } + + PSC_DEFINE_TRIPLE_API_CONST_METHOD_FROM_BOOL_0(bool, JSON, bool_val) + + bool JSON::bool_val_ec(bool &out, std::error_code &ec) const noexcept { + if (val != "true" && val != "false") { + // HANDLE_ERROR(key + " Invalid bool argument: " + val) + ec = std::make_error_code(std::errc::invalid_argument); + return false; } - if (s[i] == ',') { - i++; + out = (val == "true"); + return true; + } + + PSC_DEFINE_TRIPLE_API_CONST_METHOD_FROM_BOOL(bool, JSON, get_bool, + PSC_JSON_KEY_PARAMS) + + bool JSON::get_bool_ec(std::string_view key, bool &out, + std::error_code &ec) const noexcept { + const Psc::JSON *that = get(key); + if (!that) { + ec = std::make_error_code(std::errc::invalid_argument); + return false; + } + return that->bool_val_ec(out, ec); + } + + JSON::JSON(std::string_view key, const JSON &children) : key(key), val(children.val), valueType(children.valueType), + children(children.children) { + } + + void JSON::append_list(const std::vector &arr) { +#ifdef _DEBUG + if (valueType != JsonType::Object && valueType != JsonType::Array) { + HANDLE_ERROR("append_list error! key:" + key + " not array or object " + + to_json_string()) + } +#else +#endif + for (const JSON &j: arr) { + children.push_back(j); } } - out = std::move(ret); - return true; -} -bool parse_array(std::string& s, size_t& i, JSON& out, - std::error_code& ec) noexcept { - i++; // skip '[' - skip_whitespace(s, i); - JSON ret = JSON::array(); - if (s[i] == ']') { + + void JSON::append(const JSON &json) { +#ifdef _DEBUG + if (valueType != JsonType::Object && valueType != JsonType::Array) { + HANDLE_ERROR("append_list error! key:" + key + " not array or object " + + to_json_string()) + } +#else +#endif + children.push_back(json); + } + + void JSON::prepend(const JSON &json) { +#ifdef _DEBUG + if (valueType != JsonType::Object && valueType != JsonType::Array) { + HANDLE_ERROR("append_list error! key:" + key + " not array or object " + + to_json_string()) + } +#else +#endif + children.insert(children.begin(), json); + } + + bool JSON::has(std::string_view _key) const { + for (const JSON &child: children) { + if (child.key == _key) { + return true; + } + } + return false; + } + + const JSON *JSON::get(std::string_view _key) const { + for (const JSON &child: children) { + if (child.key == _key) { + return &child; + } + } + // HANDLE_ERROR(_key + " not found!" + to_json_string()) + return nullptr; + } + + JSON JSON::array(const std::vector &children) { + return array("", children); + } + + JSON JSON::object(const std::vector &children) { + return object("", children); + } + + JSON JSON::array(std::string_view key, const std::vector &children) { + JSON ret; + ret.key = std::string(key); + ret.valueType = Array; + ret.children = children; + return ret; + } + + JSON JSON::object(std::string_view key, const std::vector &children) { + JSON ret; + ret.key = std::string(key); + ret.valueType = Object; + ret.children = children; + return ret; + } + + std::string JSON::get_indent(const int &indent, std::string_view indentStr) { + std::string ret; + for (int i = 0; i < indent; ++i) { + ret.append(indentStr); + } + return ret; + } + + void JSON::append_value_string(std::string &ret, const int &indent, bool isEnd, + std::string_view object_split, + std::string_view array_split, + std::string_view indentStr, + JsonType fatherType) const { + ret.append(get_indent(indent, indentStr)); + if (fatherType == Object) ret.append("\"" + key + "\"" + ": "); + const std::string split = std::string(fatherType == Array ? array_split : object_split); + if (valueType == String) { + ret.append("\"" + val + "\""); + goto end; + } + if (valueType == Null) { + ret.append("null"); + goto end; + } + if (valueType == Number || valueType == Bool) { + ret.append(val); + goto end; + } + if (valueType == Object) { + ret.append("{"); + ret.append(object_split); + size_t n = children.size(); + if (n) { + for (size_t i = 0; i < n - 1; ++i) { + children[i].append_value_string(ret, indent + 2, false, object_split, + array_split, indentStr, Object); + } + children[n - 1].append_value_string(ret, indent + 2, true, object_split, + array_split, indentStr, Object); + } + ret.append(get_indent(indent, indentStr) + "}"); + goto end; + } + if (valueType == Array) { + ret.append("["); + ret.append(array_split); + size_t n = children.size(); + if (n) { + for (size_t i = 0; i < n - 1; ++i) { + children[i].append_value_string(ret, indent + 1, false, object_split, + array_split, indentStr, Array); + } + children[n - 1].append_value_string(ret, indent + 1, true, object_split, + array_split, indentStr, Array); + } + ret.append(get_indent(indent, indentStr) + "]"); + goto end; + } + end: + if (!isEnd) ret.append(","); + ret.append(split); + } + + void handleBlank(std::string &s) { + int n = static_cast(s.length()); + bool isInStr = false; + for (int i = n - 1; i >= 0; --i) { + const char &c = s[i]; + if (c == '\"') isInStr = !isInStr; + if (isInStr) continue; + if (c == '\n' || c == '\t' || c == ' ') { + s.erase(i, 1); + } + } + } + + std::string JSON::to_json_string(std::string_view object_split, + std::string_view array_split, + std::string_view indentStr, + const int &indent) const { + if (valueType == Bool) return val; + if (valueType == String) return '\"' + val + '\"'; + if (valueType == Number) return val; + if (valueType == Null) { + if (children.size() != 0) { + HANDLE_ERROR("null 带有children 转成了字符串 (" + key + "," + val + ")" + + to_json_string()) + } + return "null"; + } + std::string ret; + append_value_string(ret, indent, true, object_split, array_split, indentStr, + Null); + return ret; + } + + JSON::JSON() = default; + + void copy(JSON &dest, const JSON &src) { + dest.key = src.key; + dest.valueType = src.valueType; + dest.val = src.val; + auto len = src.children.size(); + dest.children.resize(len); + for (size_t i = 0; i < len; ++i) { + dest.children[i] = src.children[i]; + } + } + + JSON::JSON(const JSON &other) { + copy(*this, other); + } + + JSON::JSON(JSON &&other) noexcept { + key = std::move(other.key); + valueType = std::move(other.valueType); + children = std::move(other.children); + val = std::move(other.val); + } + + JSON &JSON::operator=(const JSON &other) { + if (this != &other) { + // 防止自赋值 + copy(*this, other); // 调用拷贝函数 + } + return *this; + } + + JSON &JSON::operator=(JSON &&other) noexcept { + if (this != &other) { + key = std::move(other.key); + valueType = other.valueType; + children = std::move(other.children); + val = std::move(other.val); + } + return *this; + } + + JSON &JSON::operator+=(const JSON &child) { + append_list(child.children); + return *this; + } + + JSON &JSON::operator+=(JSON &&child) { + append_list(child.children); + return *this; + } + + // 解析的分割线---------------------------- + // 如果未找到非空白字符,将 i 更新为 std::string::npos + void skip_whitespace(std::string &s, size_t &i) { + i = s.find_first_not_of(" \t\n\r\f\v", i); + } + + // 传入的 i 是 "所在的index + bool parse_string(std::string &s, size_t &i, std::string &out, + std::error_code &ec) noexcept { + ec.clear(); + i++; // skip '"' + if (s[i] == '"') { + i++; + out.clear(); + return true; + } + std::string ret; + while (true) { + if (s[i] == '"' && s[i - 1] != '\\') { + break; + } + ret += s[i]; + i++; + } i++; out = std::move(ret); return true; } - while (true) { + + bool parse_number(std::string &s, size_t &i, std::string &out, + std::error_code &ec) noexcept { + ec.clear(); + std::string num; + while (isdigit(s[i])) { + num += s[i++]; + } + if (s[i] == '.') { + i++; + num += '.'; + while (isdigit(s[i])) { + num += s[i++]; + } + } + out = std::move(num); + return true; + } + + bool parse_json_EX(std::string &s, size_t &i, JSON &out, + std::error_code &ec) noexcept; + + PSC_DEFINE_TRIPLE_API_FROM_BOOL(JSON, parse_json, PSC_JSON_PARSE_PARAMS) + + bool parse_json_ec(std::string_view text, JSON &out, + std::error_code &ec) noexcept { + auto t = std::string(text); + size_t i = 0; + return parse_json_EX(t, i, out, ec); + } + + bool parse_json_EX(std::string &s, size_t &i, JSON &out, + std::error_code &ec) noexcept { skip_whitespace(s, i); - // parseJson(s, i) - JSON kv; - if (!parse_json_EX(s, i, kv, ec)) return false; - ret.children.push_back(std::move(kv)); + switch (s[i]) { + case 'n': i += 4; + out = JSON(nullptr); + return true; + case 't': i += 4; + out = JSON(true); + return true; + case 'f': i += 5; + out = JSON(false); + return true; + case '{': return parse_object(s, i, out, ec); + case '[': return parse_array(s, i, out, ec); + case '"': { + std::string str; + if (!parse_string(s, i, str, ec)) return false; + out = JSON(std::move(str)); + return true; + } + case '-': { + i++; // skip - + JSON ret(nullptr); + ret.valueType = Number; + std::string num; + if (!parse_number(s, i, num, ec)) return false; + ret.val = '-' + num; + out = std::move(ret); + return true; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + JSON ret(nullptr); + ret.valueType = Number; + std::string num; + if (!parse_number(s, i, num, ec)) return false; + ret.val = std::move(num); + out = std::move(ret); + return true; + } + default: { + // HANDLE_ERROR("json 解析错误! " + s + "\n") + ec = std::make_error_code(std::errc::invalid_argument); + return false; + } + } + } + + bool parse_object(std::string &s, size_t &i, JSON &out, + std::error_code &ec) noexcept { + i++; // skip '{' skip_whitespace(s, i); + JSON ret = JSON::object(); + if (s[i] == '}') { + i++; + out = std::move(ret); + return true; + } + while (true) { + skip_whitespace(s, i); + std::string key; + if (!parse_string(s, i, key, ec)) return false; + skip_whitespace(s, i); + if (s[i] != ':') { + ec = std::make_error_code(std::errc::invalid_argument); + return false; + } + i++; + skip_whitespace(s, i); + JSON kv; + if (!parse_json_EX(s, i, kv, ec)) return false; + skip_whitespace(s, i); + kv.key = key; + ret.children.push_back(std::move(kv)); + if (s[i] == '}') { + i++; + break; + } + if (s[i] == ',') { + i++; + } + } + out = std::move(ret); + return true; + } + + bool parse_array(std::string &s, size_t &i, JSON &out, + std::error_code &ec) noexcept { + i++; // skip '[' + skip_whitespace(s, i); + JSON ret = JSON::array(); if (s[i] == ']') { i++; - break; + out = std::move(ret); + return true; } - if (s[i] == ',') { - i++; - } - } - out = std::move(ret); - return true; -} -PSC_DEFINE_TRIPLE_API_FROM_BOOL(JSON, parse_json_file, PSC_JSON_FILE_PARAMS) -bool parse_json_file_ec(std::string_view file_name, JSON& out, - std::error_code& ec) noexcept { - auto readFile = [](std::string_view path, std::string& content, - std::error_code& ec) noexcept { - std::ifstream infile(path, std::ios::binary | std::ios::ate); - if (!infile.is_open()) { - ec = std::make_error_code(std::errc::no_such_file_or_directory); - return false; - } - std::streamsize fileSize = infile.tellg(); - if (fileSize < 0) { - ec = std::make_error_code(std::errc::io_error); - return false; - } - infile.seekg(0, std::ios::beg); - content.resize(static_cast(fileSize)); - if (!infile.read(content.data(), fileSize)) { - ec = std::make_error_code(std::errc::io_error); - return false; + while (true) { + skip_whitespace(s, i); + // parseJson(s, i) + JSON kv; + if (!parse_json_EX(s, i, kv, ec)) return false; + ret.children.push_back(std::move(kv)); + skip_whitespace(s, i); + if (s[i] == ']') { + i++; + break; + } + if (s[i] == ',') { + i++; + } } + out = std::move(ret); return true; - }; - std::string text; - if (!readFile(file_name, text, ec)) return false; - return parse_json_ec(text, out, ec); -} -void check_json_assign_field(bool ok, const std::error_code& ec, - const Psc::JSON *that_json, const char *name) { - if (!ok) { - std::ostringstream oss; - oss << "json assign error:[" << name << "," - << Psc::platform_2_utf8(ec.message()) << "]"; - if (that_json != nullptr) { - oss << " from" << that_json->to_json_string(); - } - oss << std::endl; - throw json_assign_error(ec, oss.str()); } -} + + PSC_DEFINE_TRIPLE_API_FROM_BOOL(JSON, parse_json_file, PSC_JSON_FILE_PARAMS) + + bool parse_json_file_ec(std::string_view file_name, JSON &out, + std::error_code &ec) noexcept { + auto readFile = [](std::string_view path, std::string &content, + std::error_code &ec) noexcept { + std::ifstream infile(std::filesystem::path{path}, std::ios::binary | std::ios::ate); + if (!infile.is_open()) { + ec = std::make_error_code(std::errc::no_such_file_or_directory); + return false; + } + std::streamsize fileSize = infile.tellg(); + if (fileSize < 0) { + ec = std::make_error_code(std::errc::io_error); + return false; + } + infile.seekg(0, std::ios::beg); + content.resize(static_cast(fileSize)); + if (!infile.read(content.data(), fileSize)) { + ec = std::make_error_code(std::errc::io_error); + return false; + } + return true; + }; + std::string text; + if (!readFile(file_name, text, ec)) return false; + return parse_json_ec(text, out, ec); + } + + void check_json_assign_field(bool ok, const std::error_code &ec, + const Psc::JSON *that_json, const char *name) { + if (!ok) { + std::ostringstream oss; + oss << "json assign error:[" << name << "," + << Psc::platform_2_utf8(ec.message()) << "]"; + if (that_json != nullptr) { + oss << " from" << that_json->to_json_string(); + } + oss << std::endl; + throw json_assign_error(ec, oss.str()); + } + } } // namespace Psc #undef HANDLE_ERROR #ifdef _USE_GTEST @@ -497,33 +546,37 @@ void check_json_assign_field(bool ok, const std::error_code& ec, #include #include namespace { -std::string compact_json(const Psc::JSON& json) { - return json.to_json_string("", "", "", 0); -} -struct TempFileGuard { - const char *path; - ~TempFileGuard() { - std::remove(path); + std::string compact_json(const Psc::JSON &json) { + return json.to_json_string("", "", "", 0); } -}; -struct JsonCase { - const char *name; - const char *text; -}; -const std::vector& json_cases() { - static const std::vector cases = { - { - "object_basic", - R"({ + + struct TempFileGuard { + const char *path; + + ~TempFileGuard() { + std::remove(path); + } + }; + + struct JsonCase { + const char *name; + const char *text; + }; + + const std::vector &json_cases() { + static const std::vector cases = { + { + "object_basic", + R"({ "name": "psc", "debug": true, "version": 1, "empty": null })" - }, - { - "nested_object", - R"({ + }, + { + "nested_object", + R"({ "project": { "name": "psc", "enabled": true, @@ -535,10 +588,10 @@ const std::vector& json_cases() { }, "status": "ok" })" - }, - { - "array_with_objects", - R"({ + }, + { + "array_with_objects", + R"({ "users": [ {"name": "tom", "admin": true}, {"name": "jerry", "admin": false}, @@ -546,10 +599,10 @@ const std::vector& json_cases() { ], "count": 3 })" - }, - { - "deep_mixed", - R"({ + }, + { + "deep_mixed", + R"({ "app": { "name": "server", "modules": [ @@ -578,10 +631,10 @@ const std::vector& json_cases() { "ok": true, "none": null })" - }, - { - "root_array", - R"([ + }, + { + "root_array", + R"([ { "name": "one", "value": 1, @@ -602,27 +655,29 @@ const std::vector& json_cases() { 123, "text" ])" - } - }; - return cases; -} -void assert_parse_ok(const char *text, Psc::JSON& out) { - std::error_code ec; - ASSERT_TRUE(Psc::parse_json_ec(text, out, ec)) << ec.message(); - ASSERT_FALSE(ec) << ec.message(); -} -void assert_round_trip(const char *text) { - Psc::JSON first; - assert_parse_ok(text, first); - const std::string first_text = compact_json(first); - Psc::JSON second; - assert_parse_ok(first_text.c_str(), second); - const std::string second_text = compact_json(second); - EXPECT_EQ(second_text, first_text); -} + } + }; + return cases; + } + + void assert_parse_ok(const char *text, Psc::JSON &out) { + std::error_code ec; + ASSERT_TRUE(Psc::parse_json_ec(text, out, ec)) << ec.message(); + ASSERT_FALSE(ec) << ec.message(); + } + + void assert_round_trip(const char *text) { + Psc::JSON first; + assert_parse_ok(text, first); + const std::string first_text = compact_json(first); + Psc::JSON second; + assert_parse_ok(first_text.c_str(), second); + const std::string second_text = compact_json(second); + EXPECT_EQ(second_text, first_text); + } } // namespace TEST(JSONTest, ParseAndRoundTripComplexJsonList) { - for (const JsonCase& item : json_cases()) { + for (const JsonCase &item: json_cases()) { SCOPED_TRACE(item.name); assert_round_trip(item.text); } @@ -684,7 +739,7 @@ TEST(JSONTest, ParseNestedObjectAndGetTopFields) { EXPECT_TRUE(enabled); } TEST(JSONTest, CopyConstructorKeepsComplexJson) { - for (const JsonCase& item : json_cases()) { + for (const JsonCase &item: json_cases()) { SCOPED_TRACE(item.name); Psc::JSON original; assert_parse_ok(item.text, original); @@ -697,7 +752,7 @@ TEST(JSONTest, CopyConstructorKeepsComplexJson) { } } TEST(JSONTest, CopyAssignmentKeepsComplexJson) { - for (const JsonCase& item : json_cases()) { + for (const JsonCase &item: json_cases()) { SCOPED_TRACE(item.name); Psc::JSON source; Psc::JSON target; @@ -712,7 +767,7 @@ TEST(JSONTest, CopyAssignmentKeepsComplexJson) { } } TEST(JSONTest, MoveConstructorKeepsComplexJson) { - for (const JsonCase& item : json_cases()) { + for (const JsonCase &item: json_cases()) { SCOPED_TRACE(item.name); Psc::JSON source; assert_parse_ok(item.text, source); @@ -722,7 +777,7 @@ TEST(JSONTest, MoveConstructorKeepsComplexJson) { } } TEST(JSONTest, MoveAssignmentKeepsComplexJson) { - for (const JsonCase& item : json_cases()) { + for (const JsonCase &item: json_cases()) { SCOPED_TRACE(item.name); Psc::JSON source; Psc::JSON target; @@ -734,7 +789,7 @@ TEST(JSONTest, MoveAssignmentKeepsComplexJson) { } } TEST(JSONTest, SelfCopyAssignmentKeepsComplexJson) { - for (const JsonCase& item : json_cases()) { + for (const JsonCase &item: json_cases()) { SCOPED_TRACE(item.name); Psc::JSON json; assert_parse_ok(item.text, json); @@ -804,7 +859,7 @@ TEST(JSONTest, ParseMissingFileReturnsError) { Psc::JSON json; std::error_code ec; EXPECT_FALSE(Psc::parse_json_file_ec("__not_exist_psc_json_test_file__.json", - json, ec)); + json, ec)); EXPECT_EQ(ec, std::make_error_code(std::errc::no_such_file_or_directory)); } #endif diff --git a/Core/Serial/Serial_linux.cpp b/Core/Serial/Serial_linux.cpp index f702ef6..0e16ae5 100644 --- a/Core/Serial/Serial_linux.cpp +++ b/Core/Serial/Serial_linux.cpp @@ -7,13 +7,12 @@ #include #define SERIAL_END " " << __FILE__ << " " << __LINE__ << std::endl; #ifdef USE_SERIAL_STRUCT - #define FLAG flags +#define FLAG flags #else #define FLAG c_cflag #endif - //stty -F /dev/pts/1 9601 // sudo setserial /dev/pts/1 baud_base 115200 custom_divisor 1 // sudo setserial /dev/pts/1 -g @@ -21,615 +20,608 @@ namespace Psc::serial { -//#define LOG(MSG) log_serial_error(__func__, __FILE__, __LINE__, MSG); + //#define LOG(MSG) log_serial_error(__func__, __FILE__, __LINE__, MSG); -void set_binary_mode(Serial_Settings& serial_info) { + void set_binary_mode(Serial_Settings &serial_info) { #if defined(USE_TERMIOS2) || defined(USE_TERMIOS) - { - // -ignbrk: 启用时忽略断开信号(如挂断电话信号等),不做处理。 - // -brkint: 启用时接收到断开信号(如挂断电话信号)会中断程序。 - // -ignpar: 启用时,接收到具有错误奇偶校验的字符会被丢弃。 - // -parmrk: 启用时,接收到奇偶校验错误的字符会被标记为特殊字符(通常是某种转义字符),并且系统可能做出反应。 - // -inpck: 启用时,接收到的字符将不再进行奇偶校验检查。 - // -istrip: 启用时,接收到的每个字符的第 8 位会被去除(通常用于旧的串行协议或无符号字符的通信)。 - // -inlcr: 启用时,输入中的换行符(\n)(0x0A)不会被转换为回车符(\r)(0x0D),保留原样传递。 - // -igncr: 启用时,回车符(\r)会被忽略,不会传递给程序。 - // icrnl: 启用时,接收到的回车符(\r)会被转换为换行符(\n)。 - // ixon: 启用时,启用 XON/XOFF 流控制。接收端使用 XON(ASCII 17,Ctrl-Q, 0x11)和 XOFF(ASCII 19,Ctrl-S, 0x13)来控制数据流的暂停和恢复。 - // -ixoff: 启用时,启用 XOFF 流控制。与 ixon 配合使用,接收端通过发送 XOFF 来暂停数据传输,并等待 XON 来恢复数据流。 - // -iuclc: 启用时,所有输入字符都将以原始形式传递,不会自动转换成小写字母。 - // -ixany: 启用时,允许任何字符作为流控制符,通常用于自定义流控制字符。 - // -imaxbel: 启用时,接收到 BEL(响铃,ASCII 7)字符时会触发响铃或警告。 - // -iutf8: 启用时,禁用 UTF-8 编码字符的处理,串口通信将不支持 UTF-8 编码格式,而是使用传统字符编码格式。 - // 获取当前的输入模式标志 - auto& flag = serial_info.c_iflag; - // 禁用一些默认的转换设置,以便读取原始二进制数据流 - flag &= ~IGNBRK; // 禁用忽略断开信号 - flag &= ~BRKINT; // 禁用接收到断开信号中断程序 - flag &= ~IGNPAR; // 禁用忽略奇偶校验错误 - flag &= ~PARMRK; // 禁用标记奇偶校验错误字符 - flag &= ~INPCK; // 禁用奇偶校验检查 - flag &= ~ISTRIP; // 禁用去除字符的第 8 位 - flag &= ~INLCR; // 禁用输入中的换行符转换为回车符 - flag &= ~IGNCR; // 禁用忽略回车符 - flag &= ~ICRNL; // 禁用回车符转换为换行符 - flag &= ~IXON; // 禁用 XON 流控 - flag &= ~IXOFF; // 禁用 XOFF 流控 - flag &= ~IUCLC; // 禁用输入字符转换为小写字母 - flag &= ~IXANY; // 禁用允许任何字符作为流控制符 - flag &= ~IMAXBEL; // 禁用接收到 BEL 字符时触发响铃 - flag &= ~IUTF8; // 禁用 UTF-8 编码处理 - - } - { - // opost: 启用时,允许输出处理(例如处理换行符、回车符等)。 - // -olcuc: 启用时,将大写字母转换为小写字母(用于某些控制台模式)。 - // -ocrnl: 启用时,将输出中的换行符(\n)转换为回车符(\r)。 - // onlcr: 启用时,将输出中的换行符(\n)转换为回车换行符(\r\n),通常用于旧式终端。 - // -onocr: 启用时,回车符(\r)不会被输出到设备(即忽略回车符)。 - // -onlret: 启用时,输出的换行符(\n)会让设备进行回车操作,等同于输出换行时,光标回到行首。 - // -ofill: 启用时,输出时对填充字符(如0)进行处理(在某些协议中有用)。 - // -ofdel: 启用时,删除字符会被处理为“删除”操作(ASCII DEL字符)而不是其他字符。 - // nl0: 启用时,禁用换行符的处理,换行符不做任何转换。 - // cr0: 启用时,禁用回车符的处理,回车符不做任何转换。 - // tab0: 启用时,禁用制表符的处理,制表符不会做任何转换。 - // bs0: 启用时,禁用退格符的处理,退格符不会做任何转换。 - // vt0: 启用时,禁用垂直制表符(VT)的处理,垂直制表符不会做任何转换。 - // ff0: 启用时,禁用换页符(FF)的处理,换页符不会做任何转换。 - auto& flag = serial_info.c_oflag; - // 禁用所有的字符转换和处理 - flag &= ~OPOST; // 禁用输出处理 - flag &= ~OLCUC; // 禁用大写转换为小写 - flag &= ~OCRNL; // 禁用换行符转换为回车符 - flag &= ~ONLCR; // 禁用换行符转换为回车换行符 - flag &= ~ONOCR; // 禁用回车符不输出 - flag &= ~ONLRET; // 禁用换行符引起的回车操作 - flag &= ~OFILL; // 禁用填充字符的处理 - flag &= ~OFDEL; // 禁用删除字符的处理 - flag &= ~NL0; // 禁用换行符的处理 - flag &= ~CR0; // 禁用回车符的处理 - flag &= ~TAB0; // 禁用制表符的处理 - flag &= ~BS0; // 禁用退格符的处理 - flag &= ~VT0; // 禁用垂直制表符的处理 - flag &= ~FF0; // 禁用换页符的处理 - } - { - // isig: 启用终端信号处理(如 SIGINT, SIGQUIT 等)。 - // icanon: 启用规范模式,允许行编辑,按下退格键删除字符。 - // iexten: 启用扩展输入字符处理,如支持 `Ctrl`+`D` 等功能。 - // echo: 启用输入回显,输入的字符会在终端上显示。 - // echoe: 启用回显删除字符功能,按下退格键时字符被删除并显示删除过程。 - // echok: 启用回显回车字符,当按下回车键时,回显相应字符。 - // -echonl: 禁用回显换行符,换行符不会显示在终端上。 - // -noflsh: 禁用输入输出缓冲区的刷新操作,不自动同步数据到终端。 - // -xcase: 禁用大小写转换,输入的字母不会自动转换大小写。 - // tostop: 启用后台进程输出停止功能,后台进程生成输出时会被暂停。 - // -echoprt: 禁用回显打印字符,输出的字符不会显示在终端。 - // echoctl: 启用回显控制字符,按下 `Ctrl`+`C` 等控制字符时会显示。 - // echoke: 启用回显字符删除时的效果,删除字符时会显示删除过程。 - // -flusho: 禁用数据的实时刷新,数据不会立即显示到终端。 - // -extproc: 禁用外部处理功能,外部处理程序无法影响串口通信。 - auto& flag = serial_info.c_lflag; - // 将串口设置为原始输入模式 - flag &= ~ISIG; // 禁用终端信号处理(如 SIGINT, SIGQUIT 等) - flag &= ~ICANON; // 禁用规范模式,禁止行编辑,按下退格键不会删除字符 - flag &= ~IEXTEN; // 禁用扩展输入字符处理,如不支持 `Ctrl`+`D` 等功能 - flag &= ~ECHO; // 禁用输入回显,输入的字符不会显示在终端上 - flag &= ~ECHOE; // 禁用回显删除字符功能,按下退格键时字符不会被删除并显示删除过程 - flag &= ~ECHOK; // 禁用回显回车字符,按下回车键时不显示回车字符 - flag &= ~ECHONL; // 禁用回显换行符,换行符不会显示在终端上 - flag &= ~NOFLSH; // 禁用输入输出缓冲区的刷新操作,不自动同步数据到终端 - flag &= ~XCASE; // 禁用大小写转换,输入的字母不会自动转换大小写 - flag &= ~TOSTOP; // 禁用后台进程输出停止功能,后台进程的输出不会被暂停 - flag &= ~ECHOPRT; // 禁用回显打印字符,输出的字符不会显示在终端 - flag &= ~ECHOCTL; // 禁用回显控制字符,按下 `Ctrl`+`C` 等控制字符时不会显示 - flag &= ~ECHOKE; // 禁用回显字符删除时的效果,删除字符时不会显示删除过程 - flag &= ~FLUSHO; // 禁用数据的实时刷新,数据不会立即显示到终端 - flag &= ~EXTPROC; // 禁用外部处理功能,外部处理程序无法影响串口通信 - } + { + // -ignbrk: 启用时忽略断开信号(如挂断电话信号等),不做处理。 + // -brkint: 启用时接收到断开信号(如挂断电话信号)会中断程序。 + // -ignpar: 启用时,接收到具有错误奇偶校验的字符会被丢弃。 + // -parmrk: 启用时,接收到奇偶校验错误的字符会被标记为特殊字符(通常是某种转义字符),并且系统可能做出反应。 + // -inpck: 启用时,接收到的字符将不再进行奇偶校验检查。 + // -istrip: 启用时,接收到的每个字符的第 8 位会被去除(通常用于旧的串行协议或无符号字符的通信)。 + // -inlcr: 启用时,输入中的换行符(\n)(0x0A)不会被转换为回车符(\r)(0x0D),保留原样传递。 + // -igncr: 启用时,回车符(\r)会被忽略,不会传递给程序。 + // icrnl: 启用时,接收到的回车符(\r)会被转换为换行符(\n)。 + // ixon: 启用时,启用 XON/XOFF 流控制。接收端使用 XON(ASCII 17,Ctrl-Q, 0x11)和 XOFF(ASCII 19,Ctrl-S, 0x13)来控制数据流的暂停和恢复。 + // -ixoff: 启用时,启用 XOFF 流控制。与 ixon 配合使用,接收端通过发送 XOFF 来暂停数据传输,并等待 XON 来恢复数据流。 + // -iuclc: 启用时,所有输入字符都将以原始形式传递,不会自动转换成小写字母。 + // -ixany: 启用时,允许任何字符作为流控制符,通常用于自定义流控制字符。 + // -imaxbel: 启用时,接收到 BEL(响铃,ASCII 7)字符时会触发响铃或警告。 + // -iutf8: 启用时,禁用 UTF-8 编码字符的处理,串口通信将不支持 UTF-8 编码格式,而是使用传统字符编码格式。 + // 获取当前的输入模式标志 + auto &flag = serial_info.c_iflag; + // 禁用一些默认的转换设置,以便读取原始二进制数据流 + flag &= ~IGNBRK; // 禁用忽略断开信号 + flag &= ~BRKINT; // 禁用接收到断开信号中断程序 + flag &= ~IGNPAR; // 禁用忽略奇偶校验错误 + flag &= ~PARMRK; // 禁用标记奇偶校验错误字符 + flag &= ~INPCK; // 禁用奇偶校验检查 + flag &= ~ISTRIP; // 禁用去除字符的第 8 位 + flag &= ~INLCR; // 禁用输入中的换行符转换为回车符 + flag &= ~IGNCR; // 禁用忽略回车符 + flag &= ~ICRNL; // 禁用回车符转换为换行符 + flag &= ~IXON; // 禁用 XON 流控 + flag &= ~IXOFF; // 禁用 XOFF 流控 + flag &= ~IUCLC; // 禁用输入字符转换为小写字母 + flag &= ~IXANY; // 禁用允许任何字符作为流控制符 + flag &= ~IMAXBEL; // 禁用接收到 BEL 字符时触发响铃 + flag &= ~IUTF8; // 禁用 UTF-8 编码处理 + } + { + // opost: 启用时,允许输出处理(例如处理换行符、回车符等)。 + // -olcuc: 启用时,将大写字母转换为小写字母(用于某些控制台模式)。 + // -ocrnl: 启用时,将输出中的换行符(\n)转换为回车符(\r)。 + // onlcr: 启用时,将输出中的换行符(\n)转换为回车换行符(\r\n),通常用于旧式终端。 + // -onocr: 启用时,回车符(\r)不会被输出到设备(即忽略回车符)。 + // -onlret: 启用时,输出的换行符(\n)会让设备进行回车操作,等同于输出换行时,光标回到行首。 + // -ofill: 启用时,输出时对填充字符(如0)进行处理(在某些协议中有用)。 + // -ofdel: 启用时,删除字符会被处理为“删除”操作(ASCII DEL字符)而不是其他字符。 + // nl0: 启用时,禁用换行符的处理,换行符不做任何转换。 + // cr0: 启用时,禁用回车符的处理,回车符不做任何转换。 + // tab0: 启用时,禁用制表符的处理,制表符不会做任何转换。 + // bs0: 启用时,禁用退格符的处理,退格符不会做任何转换。 + // vt0: 启用时,禁用垂直制表符(VT)的处理,垂直制表符不会做任何转换。 + // ff0: 启用时,禁用换页符(FF)的处理,换页符不会做任何转换。 + auto &flag = serial_info.c_oflag; + // 禁用所有的字符转换和处理 + flag &= ~OPOST; // 禁用输出处理 + flag &= ~OLCUC; // 禁用大写转换为小写 + flag &= ~OCRNL; // 禁用换行符转换为回车符 + flag &= ~ONLCR; // 禁用换行符转换为回车换行符 + flag &= ~ONOCR; // 禁用回车符不输出 + flag &= ~ONLRET; // 禁用换行符引起的回车操作 + flag &= ~OFILL; // 禁用填充字符的处理 + flag &= ~OFDEL; // 禁用删除字符的处理 + flag &= ~NL0; // 禁用换行符的处理 + flag &= ~CR0; // 禁用回车符的处理 + flag &= ~TAB0; // 禁用制表符的处理 + flag &= ~BS0; // 禁用退格符的处理 + flag &= ~VT0; // 禁用垂直制表符的处理 + flag &= ~FF0; // 禁用换页符的处理 + } + { + // isig: 启用终端信号处理(如 SIGINT, SIGQUIT 等)。 + // icanon: 启用规范模式,允许行编辑,按下退格键删除字符。 + // iexten: 启用扩展输入字符处理,如支持 `Ctrl`+`D` 等功能。 + // echo: 启用输入回显,输入的字符会在终端上显示。 + // echoe: 启用回显删除字符功能,按下退格键时字符被删除并显示删除过程。 + // echok: 启用回显回车字符,当按下回车键时,回显相应字符。 + // -echonl: 禁用回显换行符,换行符不会显示在终端上。 + // -noflsh: 禁用输入输出缓冲区的刷新操作,不自动同步数据到终端。 + // -xcase: 禁用大小写转换,输入的字母不会自动转换大小写。 + // tostop: 启用后台进程输出停止功能,后台进程生成输出时会被暂停。 + // -echoprt: 禁用回显打印字符,输出的字符不会显示在终端。 + // echoctl: 启用回显控制字符,按下 `Ctrl`+`C` 等控制字符时会显示。 + // echoke: 启用回显字符删除时的效果,删除字符时会显示删除过程。 + // -flusho: 禁用数据的实时刷新,数据不会立即显示到终端。 + // -extproc: 禁用外部处理功能,外部处理程序无法影响串口通信。 + auto &flag = serial_info.c_lflag; + // 将串口设置为原始输入模式 + flag &= ~ISIG; // 禁用终端信号处理(如 SIGINT, SIGQUIT 等) + flag &= ~ICANON; // 禁用规范模式,禁止行编辑,按下退格键不会删除字符 + flag &= ~IEXTEN; // 禁用扩展输入字符处理,如不支持 `Ctrl`+`D` 等功能 + flag &= ~ECHO; // 禁用输入回显,输入的字符不会显示在终端上 + flag &= ~ECHOE; // 禁用回显删除字符功能,按下退格键时字符不会被删除并显示删除过程 + flag &= ~ECHOK; // 禁用回显回车字符,按下回车键时不显示回车字符 + flag &= ~ECHONL; // 禁用回显换行符,换行符不会显示在终端上 + flag &= ~NOFLSH; // 禁用输入输出缓冲区的刷新操作,不自动同步数据到终端 + flag &= ~XCASE; // 禁用大小写转换,输入的字母不会自动转换大小写 + flag &= ~TOSTOP; // 禁用后台进程输出停止功能,后台进程的输出不会被暂停 + flag &= ~ECHOPRT; // 禁用回显打印字符,输出的字符不会显示在终端 + flag &= ~ECHOCTL; // 禁用回显控制字符,按下 `Ctrl`+`C` 等控制字符时不会显示 + flag &= ~ECHOKE; // 禁用回显字符删除时的效果,删除字符时不会显示删除过程 + flag &= ~FLUSHO; // 禁用数据的实时刷新,数据不会立即显示到终端 + flag &= ~EXTPROC; // 禁用外部处理功能,外部处理程序无法影响串口通信 + } #endif -} - -void set_buffer_byte_size(Serial_FD fd, int byte_size) { - struct serial_struct serinfo; - if (ioctl(fd, TIOCGSERIAL, &serinfo) == -1) { - LOG_ERROR(VAR_STR_2(fd, byte_size) + " 设置串口buffer_byte_size失败!") - return; } - serinfo.xmit_fifo_size = byte_size; - if (ioctl(fd, TIOCSSERIAL, &serinfo) == -1) { - LOG_ERROR(VAR_STR_2(fd, byte_size) + " 设置串口buffer_byte_size失败!") - } -} - - -// #if defined(USE_TERMIOS) || defined(USE_TERMIOS2) -#if defined(USE_TERMIOS) -static std::vector> list{ - {0, B0}, - {50, B50}, - {75, B75}, - {110, B110}, - {134, B134}, - {150, B150}, - {200, B200}, - {300, B300}, - {600, B600}, - {1200, B1200}, - {1800, B1800}, - {2400, B2400}, - {4800, B4800}, - {9600, B9600}, - {19200, B19200}, - {38400, B38400}, - {57600, B57600}, - {115200, B115200}, - {230400, B230400}, - {460800, B460800}, - {500000, B500000}, - {576000, B576000}, - {921600, B921600}, - {1000000, B1000000}, - {1152000, B1152000}, - {1500000, B1500000}, - {2000000, B2000000}, - {2500000, B2500000}, - {3000000, B3000000}, - {3500000, B3500000}, - {4000000, B4000000} -}; - - -long baud_to_value(Baud_Rate_Type baud_rate) { - static std::map map = []() { - std::map map; - for (auto& item : list) { - map[item.second] = item.first; - } - return map; - }(); - auto iter = map.find(baud_rate); - if (iter != map.end()) { - return iter->second; - } else { - LOG_ERROR(VAR_STR_1(baud_rate) + " 不支持的波特率!") - return -1; - } -} - -std::optional value_to_baud(const long baud_rate) { - static std::map map = []() { - std::map map; - for (auto& item : list) { - map[item.first] = item.second; - } - return map; - }(); - auto iter = map.find(baud_rate); - if (iter != map.end()) { - return iter->second; - } else { - return std::nullopt; - } -} -#endif - - - -std::string get_serial_info_str(std::string_view serial_name) { - Serial serial; - serial.set_serial_name(std::move(serial_name)); - serial.open(); - std::string ret = serial.to_string(); - serial.close(); - return ret; -} - -int open(std::string_view serial_name) { - // 打开串口设备 O_RDWR | O_NOCTTY | O_CLOEXEC - Serial_FD fd = ::open(serial_name.c_str(), O_RDWR | O_NOCTTY | O_CLOEXEC); // O_RDWR: 可读可写, O_NOCTTY: 不设置为控制终端, O_NDELAY: 非阻塞 - if (fd == -1) { - LOG_ERROR(VAR_STR_1(serial_name) + "串口打开失败!") - Psc::fail_fast(); - } - return fd; -} - - -// 设置串口阻塞/非阻塞模式 -void set_block(Serial_FD fd, bool block) { - int flags = fcntl(fd, F_GETFL); // 获取当前文件描述符的标志 - if (flags == -1) { - LOG_ERROR(VAR_STR_2(fd, block) + "串口打开失败!") - } - if (block) { - flags &= ~O_NONBLOCK; // 如果需要阻塞模式,则移除 O_NONBLOCK 标志 - } else { - flags |= O_NONBLOCK; // 如果需要非阻塞模式,则设置 O_NONBLOCK 标志 - } - if (fcntl(fd, F_SETFL, flags) == -1) { - LOG_ERROR(VAR_STR_2(fd, block)) - } -} - -void close(Serial_FD fd) { - ::close(fd); -} - -Serial_Settings get_serial_info(Serial_FD fd) { - Serial_Settings serial_info{}; - #if defined(USE_TERMIOS) - if (tcgetattr(fd, &serial_info) != 0) { - LOG_ERROR("") - } - #elif defined(USE_TERMIOS2) - if (ioctl(fd, TCGETS2, &serial_info) < 0) { - perror(("获取串口信息失败! fd:[" + std::to_string(fd) + "]").c_str()); - } - #elif defined(USE_SERIAL_STRUCT) - if (ioctl(fd, TIOCGSERIAL, &serial_info) == -1) { - perror(("获取串口信息失败! fd:[" + std::to_string(fd) + "]").c_str()); - } - #endif - return serial_info; -} - -void reset(Serial_Settings& serial_info) { - cfmakeraw(&serial_info); -} - -void set_serial_info(Serial_FD fd, Serial_Settings serial_info) { - #if defined(USE_TERMIOS) - if (tcsetattr(fd, TCSANOW, &serial_info) != 0) { - LOG_ERROR(VAR_STR_1(fd)) - } - #elif defined(USE_TERMIOS2) - if (ioctl(fd, TCSETS2, &serial_info) < 0) { - perror(("设置串口信息失败! fd:[" + std::to_string(fd) + "]").c_str()); - } - #elif defined(USE_SERIAL_STRUCT) - if (ioctl(fd, TIOCSSERIAL, &serial_info) == -1) { - perror(("设置串口信息失败! fd:[" + std::to_string(fd) + "]").c_str()); - } - #endif -} - -// #define BOTHER 0010000 -void set_baud_rate(Serial_Settings& serial_info, Serial_FD fd, Baud_Rate_Type baud_rate) { - #if defined(USE_TERMIOS) - auto& flags = serial_info.c_cflag; - flags |= CBAUDEX; - // flags &= ~BOTHER; - auto br = value_to_baud(static_cast(baud_rate)); - - if (br.has_value()) - { - if (cfsetispeed(&serial_info, br.value()) != 0) { - LOG_ERROR(VAR_STR_1(baud_rate) + "cfsetispeed") - } - if (cfsetospeed(&serial_info, br.value()) != 0) { - LOG_ERROR(VAR_STR_1(baud_rate) + "cfsetospeed") - } - //LOG_ERROR(VAR_STR_1(baud_rate) + " Failed to set buffer size!") - } else - { + void set_buffer_byte_size(Serial_FD fd, int byte_size) { struct serial_struct serinfo; - - // 获取当前串口配置 - if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0) { - LOG_ERROR(VAR_STR_1(fd) + "获取串口信息失败") + if (ioctl(fd, TIOCGSERIAL, &serinfo) == -1) { + LOG_ERROR(VAR_STR_2(fd, byte_size) + " 设置串口buffer_byte_size失败!") + return; } - - // 设置自定义波特率 - serinfo.flags = (serinfo.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST; - // 计算分频值,并确保不小于1 - int tmp = serinfo.baud_base / baud_rate; - serinfo.custom_divisor = std::max(1, tmp); - int actual_baud = serinfo.baud_base / serinfo.custom_divisor; - double error = std::abs(1.0 - (double)actual_baud / baud_rate); - if (error > 0.02) { - LOG_ERROR("无法精确设置请求的波特率(误差" + std::to_string(error * 100) + "%)") - - }else{ - std::cout << "已设置波特率为" + std::to_string(actual_baud) + "bps" << std::endl; - } - if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0) { - LOG_ERROR("设置自定义波特率失败"); - - } - if (cfsetispeed(&serial_info, B38400) != 0) { - LOG_ERROR(VAR_STR_1(baud_rate) + "cfsetispeed") - } - if (cfsetospeed(&serial_info, B38400) != 0) { - LOG_ERROR(VAR_STR_1(baud_rate) + "cfsetospeed") + serinfo.xmit_fifo_size = byte_size; + if (ioctl(fd, TIOCSSERIAL, &serinfo) == -1) { + LOG_ERROR(VAR_STR_2(fd, byte_size) + " 设置串口buffer_byte_size失败!") } } - #elif defined(USE_TERMIOS2) - auto& flags = serial_info.c_cflag; - flags &= ~CBAUDEX; - flags |= BOTHER; - serial_info.c_ispeed = baud_rate; - serial_info.c_ospeed = baud_rate; - // serial_info.c_cc[VMIN] = 25; - // serial_info.c_cc[VTIME] = 0; - #elif defined(USE_SERIAL_STRUCT) - // baud_rate = baud_base / custom_divisor - // baud_base = baud_rate * custom_divisor - // custom_divisor = baud_base / baud_rate - std::cout << "baud_rate " << baud_rate << std::endl; - std::cout << "baud_base " << serial_info.baud_base << std::endl; - std::cout << "custom_divisor " << serial_info.custom_divisor << std::endl; - serial_info.custom_divisor = serial_info.baud_base / baud_rate; - std::cout << "after baud_base " << serial_info.baud_base << std::endl; - std::cout << "after custom_divisor " << serial_info.custom_divisor << std::endl; - //serial_info.baud_base = baud * 100; - #endif -} -Baud_Rate_Type get_baud_rate(Serial_Settings& serial_info) { - #if defined(USE_SERIAL_STRUCT) - return serial_info.baud_base / serial_info.custom_divisor; - #elif defined(USE_TERMIOS2) - return serial_info.c_ispeed; - #elif defined(USE_TERMIOS) - return baud_to_value(cfgetispeed(&serial_info)); - #endif -} + // #if defined(USE_TERMIOS) || defined(USE_TERMIOS2) +#if defined(USE_TERMIOS) + static std::vector > list{ + {0, B0}, + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {57600, B57600}, + {115200, B115200}, + {230400, B230400}, + {460800, B460800}, + {500000, B500000}, + {576000, B576000}, + {921600, B921600}, + {1000000, B1000000}, + {1152000, B1152000}, + {1500000, B1500000}, + {2000000, B2000000}, + {2500000, B2500000}, + {3000000, B3000000}, + {3500000, B3500000}, + {4000000, B4000000} + }; -void set_data_bits(Serial_Settings& serial_info, DataBits data_bits) { - auto& flag = serial_info.FLAG; - // 清除当前的数据位设置 - flag &= ~CSIZE; // 清除数据位标志 - switch (data_bits) { - case DataBits::Data5: flag |= CS5; - break; - case DataBits::Data6: flag |= CS6; - break; - case DataBits::Data7: flag |= CS7; - break; - case DataBits::Data8: flag |= CS8; - break; - case DataBits::UnknownDataBits: - default: - static Log_Type log_type({}, {{"POS", LOG_POS}}); - serial_logger->error("", log_type, VAR_STR_1(data_bits) + get_error_message()); - return; - } -} -DataBits get_data_bits(Serial_Settings& serial_info) { - auto& flag = serial_info.FLAG; - switch (flag & CSIZE) { - case CS5: return DataBits::Data5; - case CS6: return DataBits::Data6; - case CS7: return DataBits::Data7; - case CS8: return DataBits::Data8; - default: return DataBits::UnknownDataBits; // 如果数据位设置不正确,则返回 Unknown - } -} - -void set_stop_bits(Serial_Settings& serial_info, StopBits stop_bits) { - auto& flag = serial_info.FLAG; - // 清除当前停止位设置 - flag &= ~CSTOPB; // 清除 2 停止位标志 - switch (stop_bits) { - case StopBits::OneStop: - // 设置为 1 个停止位,不需要更改 c_cflag,因为 1 个停止位是默认值 - break; - case StopBits::OneAndHalfStop: - // 1.5 停止位不是所有系统都支持,如果不支持,可能会默认为 2 停止位 - // Linux 系统一般没有明确的 1.5 停止位设置,可能会自动转换为 2 停止位 - { - static Log_Type log_type({}, {{"POS", LOG_POS}}); - serial_logger->error("", log_type, VAR_STR_1(stop_bits) + get_error_message() + "Linux 系统一般没有明确的 1.5 停止位设置,自动转换为 2 停止位"); + long baud_to_value(Baud_Rate_Type baud_rate) { + static std::map map = []() { + std::map map; + for (auto &item: list) { + map[item.second] = item.first; + } + return map; + }(); + auto iter = map.find(baud_rate); + if (iter != map.end()) { + return iter->second; + } else { + LOG_ERROR(VAR_STR_1(baud_rate) + " 不支持的波特率!") + return -1; } - // 使用 2 停止位标志 - flag |= CSTOPB; - break; - case StopBits::TwoStop: - // 设置为 2 个停止位 - flag |= CSTOPB; - break; - case StopBits::UnknownStopBits: - default: std::cerr << "Unknown stop bits configuration." << std::endl; - { - static Log_Type log_type({}, {{"POS", LOG_POS}}); - serial_logger->error("", log_type, VAR_STR_1(stop_bits) + get_error_message() + " 未知的停止位!"); - } - return; } -} -StopBits get_stop_bits(Serial_Settings& serial_info) { - auto& flag = serial_info.FLAG; - if (flag & CSTOPB) { - // 如果 CSTOPB 设置了,表示是 2 停止位或者 1.5 停止位 + std::optional value_to_baud(const long baud_rate) { + static std::map map = []() { + std::map map; + for (auto &item: list) { + map[item.first] = item.second; + } + return map; + }(); + auto iter = map.find(baud_rate); + if (iter != map.end()) { + return iter->second; + } else { + return std::nullopt; + } + } +#endif + + + std::string get_serial_info_str(std::string_view serial_name) { + Serial serial; + serial.set_serial_name(std::move(serial_name)); + serial.open(); + std::string ret = serial.to_string(); + serial.close(); + return ret; + } + + int open(std::string_view serial_name) { + // 打开串口设备 O_RDWR | O_NOCTTY | O_CLOEXEC + Serial_FD fd = ::open(serial_name.data(), O_RDWR | O_NOCTTY | O_CLOEXEC); + // O_RDWR: 可读可写, O_NOCTTY: 不设置为控制终端, O_NDELAY: 非阻塞 + if (fd == -1) { + LOG_ERROR(VAR_STR_1(serial_name.data()) + "串口打开失败!") + Psc::fail_fast(); + } + return fd; + } + + + // 设置串口阻塞/非阻塞模式 + void set_block(Serial_FD fd, bool block) { + int flags = fcntl(fd, F_GETFL); // 获取当前文件描述符的标志 + if (flags == -1) { + LOG_ERROR(VAR_STR_2(fd, block) + "串口打开失败!") + } + if (block) { + flags &= ~O_NONBLOCK; // 如果需要阻塞模式,则移除 O_NONBLOCK 标志 + } else { + flags |= O_NONBLOCK; // 如果需要非阻塞模式,则设置 O_NONBLOCK 标志 + } + if (fcntl(fd, F_SETFL, flags) == -1) { + LOG_ERROR(VAR_STR_2(fd, block)) + } + } + + void close(Serial_FD fd) { + ::close(fd); + } + + Serial_Settings get_serial_info(Serial_FD fd) { + Serial_Settings serial_info{}; +#if defined(USE_TERMIOS) + if (tcgetattr(fd, &serial_info) != 0) { + LOG_ERROR("") + } +#elif defined(USE_TERMIOS2) + if (ioctl(fd, TCGETS2, &serial_info) < 0) { + perror(("获取串口信息失败! fd:[" + std::to_string(fd) + "]").c_str()); + } +#elif defined(USE_SERIAL_STRUCT) + if (ioctl(fd, TIOCGSERIAL, &serial_info) == -1) { + perror(("获取串口信息失败! fd:[" + std::to_string(fd) + "]").c_str()); + } +#endif + return serial_info; + } + + void reset(Serial_Settings &serial_info) { + cfmakeraw(&serial_info); + } + + void set_serial_info(Serial_FD fd, Serial_Settings serial_info) { +#if defined(USE_TERMIOS) + if (tcsetattr(fd, TCSANOW, &serial_info) != 0) { + LOG_ERROR(VAR_STR_1(fd)) + } +#elif defined(USE_TERMIOS2) + if (ioctl(fd, TCSETS2, &serial_info) < 0) { + perror(("设置串口信息失败! fd:[" + std::to_string(fd) + "]").c_str()); + } +#elif defined(USE_SERIAL_STRUCT) + if (ioctl(fd, TIOCSSERIAL, &serial_info) == -1) { + perror(("设置串口信息失败! fd:[" + std::to_string(fd) + "]").c_str()); + } +#endif + } + + // #define BOTHER 0010000 + void set_baud_rate(Serial_Settings &serial_info, Serial_FD fd, Baud_Rate_Type baud_rate) { +#if defined(USE_TERMIOS) + auto &flags = serial_info.c_cflag; + flags |= CBAUDEX; + // flags &= ~BOTHER; + auto br = value_to_baud(static_cast(baud_rate)); + + if (br.has_value()) { + if (cfsetispeed(&serial_info, br.value()) != 0) { + LOG_ERROR(VAR_STR_1(baud_rate) + "cfsetispeed") + } + if (cfsetospeed(&serial_info, br.value()) != 0) { + LOG_ERROR(VAR_STR_1(baud_rate) + "cfsetospeed") + } + //LOG_ERROR(VAR_STR_1(baud_rate) + " Failed to set buffer size!") + } else { + struct serial_struct serinfo; + + // 获取当前串口配置 + if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0) { + LOG_ERROR(VAR_STR_1(fd) + "获取串口信息失败") + } + + // 设置自定义波特率 + serinfo.flags = (serinfo.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST; + // 计算分频值,并确保不小于1 + int tmp = serinfo.baud_base / baud_rate; + serinfo.custom_divisor = std::max(1, tmp); + int actual_baud = serinfo.baud_base / serinfo.custom_divisor; + double error = std::abs(1.0 - (double) actual_baud / baud_rate); + if (error > 0.02) { + LOG_ERROR("无法精确设置请求的波特率(误差" + std::to_string(error * 100) + "%)") + } else { + std::cout << "已设置波特率为" + std::to_string(actual_baud) + "bps" << std::endl; + } + if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0) { + LOG_ERROR("设置自定义波特率失败"); + } + if (cfsetispeed(&serial_info, B38400) != 0) { + LOG_ERROR(VAR_STR_1(baud_rate) + "cfsetispeed") + } + if (cfsetospeed(&serial_info, B38400) != 0) { + LOG_ERROR(VAR_STR_1(baud_rate) + "cfsetospeed") + } + } + +#elif defined(USE_TERMIOS2) + auto &flags = serial_info.c_cflag; + flags &= ~CBAUDEX; + flags |= BOTHER; + serial_info.c_ispeed = baud_rate; + serial_info.c_ospeed = baud_rate; + // serial_info.c_cc[VMIN] = 25; + // serial_info.c_cc[VTIME] = 0; +#elif defined(USE_SERIAL_STRUCT) + // baud_rate = baud_base / custom_divisor + // baud_base = baud_rate * custom_divisor + // custom_divisor = baud_base / baud_rate + std::cout << "baud_rate " << baud_rate << std::endl; + std::cout << "baud_base " << serial_info.baud_base << std::endl; + std::cout << "custom_divisor " << serial_info.custom_divisor << std::endl; + serial_info.custom_divisor = serial_info.baud_base / baud_rate; + std::cout << "after baud_base " << serial_info.baud_base << std::endl; + std::cout << "after custom_divisor " << serial_info.custom_divisor << std::endl; + //serial_info.baud_base = baud * 100; +#endif + } + + Baud_Rate_Type get_baud_rate(Serial_Settings &serial_info) { +#if defined(USE_SERIAL_STRUCT) + return serial_info.baud_base / serial_info.custom_divisor; +#elif defined(USE_TERMIOS2) + return serial_info.c_ispeed; +#elif defined(USE_TERMIOS) + return baud_to_value(cfgetispeed(&serial_info)); +#endif + } + + void set_data_bits(Serial_Settings &serial_info, DataBits data_bits) { + auto &flag = serial_info.FLAG; + // 清除当前的数据位设置 + flag &= ~CSIZE; // 清除数据位标志 + switch (data_bits) { + case DataBits::Data5: flag |= CS5; + break; + case DataBits::Data6: flag |= CS6; + break; + case DataBits::Data7: flag |= CS7; + break; + case DataBits::Data8: flag |= CS8; + break; + case DataBits::UnknownDataBits: + default: + static Log_Type log_type({}, {{"POS", LOG_POS}}); + serial_logger->error("", log_type, VAR_STR_1(data_bits) + get_error_message()); + return; + } + } + + DataBits get_data_bits(Serial_Settings &serial_info) { + auto &flag = serial_info.FLAG; + switch (flag & CSIZE) { + case CS5: return DataBits::Data5; + case CS6: return DataBits::Data6; + case CS7: return DataBits::Data7; + case CS8: return DataBits::Data8; + default: return DataBits::UnknownDataBits; // 如果数据位设置不正确,则返回 Unknown + } + } + + void set_stop_bits(Serial_Settings &serial_info, StopBits stop_bits) { + auto &flag = serial_info.FLAG; + // 清除当前停止位设置 + flag &= ~CSTOPB; // 清除 2 停止位标志 + switch (stop_bits) { + case StopBits::OneStop: + // 设置为 1 个停止位,不需要更改 c_cflag,因为 1 个停止位是默认值 + break; + case StopBits::OneAndHalfStop: + // 1.5 停止位不是所有系统都支持,如果不支持,可能会默认为 2 停止位 + // Linux 系统一般没有明确的 1.5 停止位设置,可能会自动转换为 2 停止位 + { + static Log_Type log_type({}, {{"POS", LOG_POS}}); + serial_logger->error("", log_type, + VAR_STR_1(stop_bits) + get_error_message() + + "Linux 系统一般没有明确的 1.5 停止位设置,自动转换为 2 停止位"); + } + // 使用 2 停止位标志 + flag |= CSTOPB; + break; + case StopBits::TwoStop: + // 设置为 2 个停止位 + flag |= CSTOPB; + break; + case StopBits::UnknownStopBits: + default: std::cerr << "Unknown stop bits configuration." << std::endl; + { + static Log_Type log_type({}, {{"POS", LOG_POS}}); + serial_logger->error("", log_type, VAR_STR_1(stop_bits) + get_error_message() + " 未知的停止位!"); + } + return; + } + } + + StopBits get_stop_bits(Serial_Settings &serial_info) { + auto &flag = serial_info.FLAG; if (flag & CSTOPB) { - return StopBits::TwoStop; + // 如果 CSTOPB 设置了,表示是 2 停止位或者 1.5 停止位 + if (flag & CSTOPB) { + return StopBits::TwoStop; + } + return StopBits::UnknownStopBits; // 如果无法明确,返回未知停止位 + } else { + // 如果 CSTOPB 没有设置,表示是 1 停止位 + return StopBits::OneStop; } - return StopBits::UnknownStopBits; // 如果无法明确,返回未知停止位 - } else { - // 如果 CSTOPB 没有设置,表示是 1 停止位 - return StopBits::OneStop; } -} - -void set_parity(Serial_Settings& serial_info, Parity parity) { - auto& flag = serial_info.FLAG; - // 清除原有的校验位设置 - flag &= ~(PARENB | PARODD | CMSPAR); // 清除 PARENB, PARODD 和 CMSPAR - switch (parity) { - case Parity::NoParity: - // 无校验 - break; - case Parity::EvenParity: - // 偶校验 - flag |= PARENB; // 启用校验 - flag &= ~PARODD; // 设置为偶校验 - break; - case Parity::OddParity: - // 奇校验 - flag |= PARENB; // 启用校验 - flag |= PARODD; // 设置为奇校验 - break; - case Parity::SpaceParity: - // 空格校验(校验位固定为 0) - flag |= PARENB; // 启用校验 - flag |= CMSPAR; // 强制校验位为 0 - flag &= ~PARODD; // 设置为偶校验(只需设置 CMSPAR 即可) - break; - case Parity::MarkParity: - // 标记校验(校验位固定为 1) - flag |= PARENB; // 启用校验 - flag |= CMSPAR; // 强制校验位为 1 - flag |= PARODD; // 设置为奇校验(只需设置 CMSPAR 即可) - break; - case Parity::UnknownParity: - default: - static Log_Type log_type({}, {{"POS", LOG_POS}}); - serial_logger->error("", log_type, VAR_STR_1(parity) + get_error_message() + " 未知的停止位!"); - return; + void set_parity(Serial_Settings &serial_info, Parity parity) { + auto &flag = serial_info.FLAG; + // 清除原有的校验位设置 + flag &= ~(PARENB | PARODD | CMSPAR); // 清除 PARENB, PARODD 和 CMSPAR + switch (parity) { + case Parity::NoParity: + // 无校验 + break; + case Parity::EvenParity: + // 偶校验 + flag |= PARENB; // 启用校验 + flag &= ~PARODD; // 设置为偶校验 + break; + case Parity::OddParity: + // 奇校验 + flag |= PARENB; // 启用校验 + flag |= PARODD; // 设置为奇校验 + break; + case Parity::SpaceParity: + // 空格校验(校验位固定为 0) + flag |= PARENB; // 启用校验 + flag |= CMSPAR; // 强制校验位为 0 + flag &= ~PARODD; // 设置为偶校验(只需设置 CMSPAR 即可) + break; + case Parity::MarkParity: + // 标记校验(校验位固定为 1) + flag |= PARENB; // 启用校验 + flag |= CMSPAR; // 强制校验位为 1 + flag |= PARODD; // 设置为奇校验(只需设置 CMSPAR 即可) + break; + case Parity::UnknownParity: + default: + static Log_Type log_type({}, {{"POS", LOG_POS}}); + serial_logger->error("", log_type, VAR_STR_1(parity) + get_error_message() + " 未知的停止位!"); + return; + } } -} -Parity get_parity(Serial_Settings& serial_info) { - auto& flag = serial_info.FLAG; - if (!(flag & PARENB)) { - return Parity::NoParity; - } - // 如果启用了校验并且设置了 CMSPAR - if (flag & CMSPAR) { - // 空格校验或者标记校验 + Parity get_parity(Serial_Settings &serial_info) { + auto &flag = serial_info.FLAG; + if (!(flag & PARENB)) { + return Parity::NoParity; + } + // 如果启用了校验并且设置了 CMSPAR + if (flag & CMSPAR) { + // 空格校验或者标记校验 + if (flag & PARODD) { + return Parity::MarkParity; // 设置 PARODD 表示标记校验 + } else { + return Parity::SpaceParity; // 未设置 PARODD 表示空格校验 + } + } + // 启用了校验且没有设置 CMSPAR if (flag & PARODD) { - return Parity::MarkParity; // 设置 PARODD 表示标记校验 + return Parity::OddParity; // 奇校验 } else { - return Parity::SpaceParity; // 未设置 PARODD 表示空格校验 + return Parity::EvenParity; // 偶校验 } } - // 启用了校验且没有设置 CMSPAR - if (flag & PARODD) { - return Parity::OddParity; // 奇校验 - } else { - return Parity::EvenParity; // 偶校验 - } -} -void set_flow_control(Serial_Settings& serial_info, FlowControl flow_control) { + void set_flow_control(Serial_Settings &serial_info, FlowControl flow_control) { #if defined(USE_TERMIOS2) || defined(USE_TERMIOS) - auto& flag = serial_info.FLAG; - flag &= ~(CRTSCTS); // 清除硬件流控设置 - serial_info.c_iflag &= ~(IXON | IXOFF | IXANY); // 清除软件流控设置 - switch (flow_control) { - case FlowControl::NoFlowControl: - // 不启用任何流控,默认设置 - break; - case FlowControl::HardwareControl: - // 启用硬件流控 (RTS/CTS) - flag |= CRTSCTS; - break; - case FlowControl::SoftwareControl: - // 启用软件流控 (XON/XOFF) - serial_info.c_iflag |= IXON|IXOFF|IXANY; - break; - case FlowControl::UnknownFlowControl: - default: - static Log_Type log_type({}, {{"POS", LOG_POS}}); - serial_logger->error("", log_type, VAR_STR_1(flow_control) + get_error_message() + " 未知的流控类型!"); - return; - } -#endif -} - -FlowControl get_flow_control(Serial_Settings& serial_info) { -#if defined(USE_TERMIOS2) || defined(USE_TERMIOS) - auto& flag = serial_info.FLAG; - // 检查硬件流控 (RTS/CTS) - if (flag & CRTSCTS) { - return FlowControl::HardwareControl; - } - // 检查软件流控 (XON/XOFF) - if (serial_info.c_iflag & (IXON | IXOFF)) { - return FlowControl::SoftwareControl; - } -#endif - // 如果没有流控设置 - return FlowControl::NoFlowControl; -} - - -// 在 Linux 系统中,SIOCINQ 和 SIOCOUTQ 是用于通过 ioctl() 系统调用获取套接字缓冲区中数据量的命令。 -// -// 🔍 SIOCINQ 和 SIOCOUTQ 的作用 -// TIOCINQ 和 TIOCOUTQ。 -// FIONREAD FIONWRITE - -// 读取数据接口实现 -int get_available_bytes(Serial_FD fd) { - int bytes_available = 0; - // 获取串口缓冲区中待读取的字节数 - if (ioctl(fd, FIONREAD, &bytes_available) == -1) { - LOG_ERROR("获取可用字节数错误!") - return -1; - } - return bytes_available; -} - -int get_written_bytes(Serial_FD fd) { - int bytes_written = 0; - // 获取串口发送缓冲区中待发送的字节数 - if (ioctl(fd, TIOCOUTQ, &bytes_written) == -1) { - LOG_ERROR(VAR_STR_2(fd, bytes_written) + "获取写缓冲区字节数错误!") - return -1; - } - return bytes_written; -} - - -int get_buffer_byte_size(Serial_FD fd) { - struct serial_struct serinfo; - // 获取串口的当前设置 - if (ioctl(fd, TIOCGSERIAL, &serinfo) == -1) { - LOG_ERROR(VAR_STR_2(fd, serinfo.xmit_fifo_size)) - return -1; - } - // 返回缓冲区大小 - return serinfo.xmit_fifo_size; -} - -// int64_t read_data(Serial_FD fd, char* data, int64_t maxSize) { -// // 从文件描述符读取数据 -// ssize_t bytesRead = read(fd, data, maxSize); -// if (bytesRead < 0) { -// // ,返回 -1 -// static Log_Type log_type({}, {{"POS", LOG_POS}}); -// serial_logger->error("", log_type, VAR_STR_2(fd, maxSize)+ "读取错误!"); -// return 0; -// } -// return bytesRead; // 返回实际读取的字节数 -// } - -int64_t read_data(Serial_FD fd, char* data, int64_t maxSize) { - ssize_t bytesRead = read(fd, data, maxSize); - if (bytesRead < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return 0; // 非阻塞模式下没有数据可读时返回 0 - } else { - LOG_ERROR("read!") - return -1; // 其他错误 + auto &flag = serial_info.FLAG; + flag &= ~(CRTSCTS); // 清除硬件流控设置 + serial_info.c_iflag &= ~(IXON | IXOFF | IXANY); // 清除软件流控设置 + switch (flow_control) { + case FlowControl::NoFlowControl: + // 不启用任何流控,默认设置 + break; + case FlowControl::HardwareControl: + // 启用硬件流控 (RTS/CTS) + flag |= CRTSCTS; + break; + case FlowControl::SoftwareControl: + // 启用软件流控 (XON/XOFF) + serial_info.c_iflag |= IXON | IXOFF | IXANY; + break; + case FlowControl::UnknownFlowControl: + default: + static Log_Type log_type({}, {{"POS", LOG_POS}}); + serial_logger->error("", log_type, VAR_STR_1(flow_control) + get_error_message() + " 未知的流控类型!"); + return; } +#endif } - return bytesRead; // 返回读取的字节数 -} -int64_t write(Serial_FD fd, const char* data, int64_t maxSize) { - return ::write(fd, data, maxSize); -} + FlowControl get_flow_control(Serial_Settings &serial_info) { +#if defined(USE_TERMIOS2) || defined(USE_TERMIOS) + auto &flag = serial_info.FLAG; + // 检查硬件流控 (RTS/CTS) + if (flag & CRTSCTS) { + return FlowControl::HardwareControl; + } + // 检查软件流控 (XON/XOFF) + if (serial_info.c_iflag & (IXON | IXOFF)) { + return FlowControl::SoftwareControl; + } +#endif + // 如果没有流控设置 + return FlowControl::NoFlowControl; + } + // 在 Linux 系统中,SIOCINQ 和 SIOCOUTQ 是用于通过 ioctl() 系统调用获取套接字缓冲区中数据量的命令。 + // + // 🔍 SIOCINQ 和 SIOCOUTQ 的作用 + // TIOCINQ 和 TIOCOUTQ。 + // FIONREAD FIONWRITE + + // 读取数据接口实现 + int get_available_bytes(Serial_FD fd) { + int bytes_available = 0; + // 获取串口缓冲区中待读取的字节数 + if (ioctl(fd, FIONREAD, &bytes_available) == -1) { + LOG_ERROR("获取可用字节数错误!") + return -1; + } + return bytes_available; + } + + int get_written_bytes(Serial_FD fd) { + int bytes_written = 0; + // 获取串口发送缓冲区中待发送的字节数 + if (ioctl(fd, TIOCOUTQ, &bytes_written) == -1) { + LOG_ERROR(VAR_STR_2(fd, bytes_written) + "获取写缓冲区字节数错误!") + return -1; + } + return bytes_written; + } + + + int get_buffer_byte_size(Serial_FD fd) { + struct serial_struct serinfo; + // 获取串口的当前设置 + if (ioctl(fd, TIOCGSERIAL, &serinfo) == -1) { + LOG_ERROR(VAR_STR_2(fd, serinfo.xmit_fifo_size)) + return -1; + } + // 返回缓冲区大小 + return serinfo.xmit_fifo_size; + } + + // int64_t read_data(Serial_FD fd, char* data, int64_t maxSize) { + // // 从文件描述符读取数据 + // ssize_t bytesRead = read(fd, data, maxSize); + // if (bytesRead < 0) { + // // ,返回 -1 + // static Log_Type log_type({}, {{"POS", LOG_POS}}); + // serial_logger->error("", log_type, VAR_STR_2(fd, maxSize)+ "读取错误!"); + // return 0; + // } + // return bytesRead; // 返回实际读取的字节数 + // } + + int64_t read_data(Serial_FD fd, char *data, int64_t maxSize) { + ssize_t bytesRead = read(fd, data, maxSize); + if (bytesRead < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; // 非阻塞模式下没有数据可读时返回 0 + } else { + LOG_ERROR("read!") + return -1; // 其他错误 + } + } + return bytesRead; // 返回读取的字节数 + } + + int64_t write(Serial_FD fd, const char *data, int64_t maxSize) { + return ::write(fd, data, maxSize); + } } #undef FLAG #undef SERIAL_END @@ -716,4 +708,4 @@ root@RK356X:~# stty -F /dev/ttyS4 -a echoke: 启用回显字符删除时的效果,删除字符时会显示删除过程。 -flusho: 禁用数据的实时刷新,数据不会立即显示到终端。 -extproc: 禁用外部处理功能,外部处理程序无法影响串口通信。 -*/ \ No newline at end of file +*/ diff --git a/Core/Shared_Memory/Shared_Memory_linux.cpp b/Core/Shared_Memory/Shared_Memory_linux.cpp index b963278..c24760b 100644 --- a/Core/Shared_Memory/Shared_Memory_linux.cpp +++ b/Core/Shared_Memory/Shared_Memory_linux.cpp @@ -16,158 +16,165 @@ #include #include #include + namespace Psc { - -bool check_if_shared_memory_exists(std::string_view name) { - int fd = shm_open(name.c_str(), O_RDWR, 0777); - if (fd < 0) { - return false; - } - close(fd); - return true; -} -Shared_Memory::~Shared_Memory() { close(); } -bool Shared_Memory::create(std::string_view name, size_t size) { - m_name = name; - m_size = size; - m_fd = shm_open(name.c_str(), O_CREAT | O_RDWR, 0777); - if (m_fd < 0) - return false; - if (ftruncate(m_fd, size) != 0) - return false; - m_ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0); - if (fchmod(m_fd, 0777) != 0) { - std::cerr << "修改共享内存权限失败: " << get_error_message() << std::endl; - Psc::fail_fast(); - } - return m_ptr != MAP_FAILED; -} -bool Shared_Memory::open(std::string_view name, size_t size) { - m_name = name; - m_size = size; - m_fd = shm_open(name.c_str(), O_RDWR, 0777); - if (m_fd < 0) - return false; - m_ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0); - return m_ptr != MAP_FAILED; -} -void Shared_Memory::close() { - // if (m_ptr) { - // munmap(m_ptr, m_size); - // m_ptr = nullptr; - // } - if (m_fd >= 0) { - ::close(m_fd); - m_fd = -1; - } -} -void Shared_Memory::unlink() { - if (!m_name.empty()) - shm_unlink(m_name.c_str()); -} - -struct MutexBlock { - pthread_mutex_t mutex; - std::atomic initialized; // 0 = 未初始化, 1 = 初始化完成 -}; - -Cross_Process_Mutex::Cross_Process_Mutex() {} - -void Cross_Process_Mutex::init(std::string_view mutexName) { - this->m_mutexName = mutexName; - bool creator = false; - - // 第一次创建尝试 O_EXCL,能判断是否首次创建 - m_fd = shm_open(m_mutexName.c_str(), O_RDWR | O_CREAT | O_EXCL, 0777); - if (m_fd >= 0) { - creator = true; - if (ftruncate(m_fd, sizeof(MutexBlock)) < 0) { - std::cout << "跨进程锁" << mutexName << "创建失败 [" - << get_error_message() << "] " << LOG_POS << std::endl; - Psc::fail_fast(); + bool check_if_shared_memory_exists(std::string_view name) { + std::string shm_name(name); + int fd = shm_open(shm_name.c_str(), O_RDWR, 0777); + if (fd < 0) { + return false; } + close(fd); + return true; + } + + Shared_Memory::~Shared_Memory() { close(); } + + bool Shared_Memory::create(std::string_view name, size_t size) { + m_name = name; + m_size = size; + std::string shm_name(name); + m_fd = shm_open(shm_name.c_str(), O_CREAT | O_RDWR, 0777); + if (m_fd < 0) + return false; + if (ftruncate(m_fd, size) != 0) + return false; + m_ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0); if (fchmod(m_fd, 0777) != 0) { - std::cerr << "修改共享内存权限失败: " << get_error_message() << LOG_POS - << std::endl; + std::cerr << "修改共享内存权限失败: " << get_error_message() << std::endl; Psc::fail_fast(); } - } else if (errno == EEXIST) { - // 已经存在,只打开,不改大小 - m_fd = shm_open(m_mutexName.c_str(), O_RDWR, 0777); - if (m_fd < 0) { - std::cout << "跨进程锁" << mutexName << "打开失败 [" - << get_error_message() << "] " << LOG_POS << std::endl; + return m_ptr != MAP_FAILED; + } + + bool Shared_Memory::open(std::string_view name, size_t size) { + m_name = name; + m_size = size; + std::string shm_name(name); + m_fd = shm_open(shm_name.c_str(), O_RDWR, 0777); + if (m_fd < 0) + return false; + m_ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0); + return m_ptr != MAP_FAILED; + } + + void Shared_Memory::close() { + // if (m_ptr) { + // munmap(m_ptr, m_size); + // m_ptr = nullptr; + // } + if (m_fd >= 0) { + ::close(m_fd); + m_fd = -1; + } + } + + void Shared_Memory::unlink() { + if (!m_name.empty()) + shm_unlink(m_name.c_str()); + } + + struct MutexBlock { + pthread_mutex_t mutex; + std::atomic initialized; // 0 = 未初始化, 1 = 初始化完成 + }; + + Cross_Process_Mutex::Cross_Process_Mutex() { + } + + void Cross_Process_Mutex::init(std::string_view mutexName) { + this->m_mutexName = mutexName; + bool creator = false; + + // 第一次创建尝试 O_EXCL,能判断是否首次创建 + m_fd = shm_open(m_mutexName.c_str(), O_RDWR | O_CREAT | O_EXCL, 0777); + if (m_fd >= 0) { + creator = true; + if (ftruncate(m_fd, sizeof(MutexBlock)) < 0) { + std::cout << "跨进程锁" << mutexName << "创建失败 [" + << get_error_message() << "] " << LOG_POS << std::endl; + Psc::fail_fast(); + } + if (fchmod(m_fd, 0777) != 0) { + std::cerr << "修改共享内存权限失败: " << get_error_message() << LOG_POS + << std::endl; + Psc::fail_fast(); + } + } else if (errno == EEXIST) { + // 已经存在,只打开,不改大小 + m_fd = shm_open(m_mutexName.c_str(), O_RDWR, 0777); + if (m_fd < 0) { + std::cout << "跨进程锁" << mutexName << "打开失败 [" + << get_error_message() << "] " << LOG_POS << std::endl; + Psc::fail_fast(); + } + } else { + std::cout << "跨进程锁" << mutexName << "检测失败 [" << get_error_message() + << "] " << LOG_POS << std::endl; Psc::fail_fast(); } - } else { - std::cout << "跨进程锁" << mutexName << "检测失败 [" << get_error_message() - << "] " << LOG_POS << std::endl; - Psc::fail_fast(); - } - // 映射 - m_addr = mmap(nullptr, sizeof(MutexBlock), PROT_READ | PROT_WRITE, MAP_SHARED, - m_fd, 0); - if (m_addr == MAP_FAILED) { - std::cout << "跨进程锁" << mutexName << "mmap失败 [" << get_error_message() - << "] " << LOG_POS << std::endl; - Psc::fail_fast(); - } + // 映射 + m_addr = mmap(nullptr, sizeof(MutexBlock), PROT_READ | PROT_WRITE, MAP_SHARED, + m_fd, 0); + if (m_addr == MAP_FAILED) { + std::cout << "跨进程锁" << mutexName << "mmap失败 [" << get_error_message() + << "] " << LOG_POS << std::endl; + Psc::fail_fast(); + } - auto block = static_cast(m_addr); + auto block = static_cast(m_addr); - if (creator) { - // 创建者初始化 - memset(&block->mutex, 0, sizeof(pthread_mutex_t)); - block->initialized.store(0, std::memory_order_relaxed); + if (creator) { + // 创建者初始化 + memset(&block->mutex, 0, sizeof(pthread_mutex_t)); + block->initialized.store(0, std::memory_order_relaxed); - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); - if (pthread_mutex_init(&block->mutex, &attr) != 0) { + if (pthread_mutex_init(&block->mutex, &attr) != 0) { + pthread_mutexattr_destroy(&attr); + std::cout << "跨进程锁" << mutexName << "init失败 [" + << get_error_message() << "] " << LOG_POS << std::endl; + Psc::fail_fast(); + } pthread_mutexattr_destroy(&attr); - std::cout << "跨进程锁" << mutexName << "init失败 [" - << get_error_message() << "] " << LOG_POS << std::endl; - Psc::fail_fast(); - } - pthread_mutexattr_destroy(&attr); - block->initialized.store(1, std::memory_order_release); - - } else { - // 后来者等待初始化完成 - auto ib = &block->initialized; - while (ib->load(std::memory_order_acquire) != 1) { - sched_yield(); + block->initialized.store(1, std::memory_order_release); + } else { + // 后来者等待初始化完成 + auto ib = &block->initialized; + while (ib->load(std::memory_order_acquire) != 1) { + sched_yield(); + } } } -} -Cross_Process_Mutex::~Cross_Process_Mutex() { - // if (m_addr) munmap(m_addr, sizeof(MutexBlock)); - // - close(m_fd); -} - -void Cross_Process_Mutex::lock() { - auto block = static_cast(m_addr); - int r = pthread_mutex_lock(&block->mutex); - - if (r == EOWNERDEAD) { - pthread_mutex_consistent(&block->mutex); - } else if (r == ENOTRECOVERABLE) { - throw std::runtime_error("mutex not recoverable"); - } else if (r != 0) { - throw std::runtime_error("pthread_mutex_lock failed"); + Cross_Process_Mutex::~Cross_Process_Mutex() { + // if (m_addr) munmap(m_addr, sizeof(MutexBlock)); + // + close(m_fd); } -} -void Cross_Process_Mutex::unlock() { - auto block = static_cast(m_addr); - pthread_mutex_unlock(&block->mutex); -} + void Cross_Process_Mutex::lock() { + auto block = static_cast(m_addr); + int r = pthread_mutex_lock(&block->mutex); + if (r == EOWNERDEAD) { + pthread_mutex_consistent(&block->mutex); + } else if (r == ENOTRECOVERABLE) { + throw std::runtime_error("mutex not recoverable"); + } else if (r != 0) { + throw std::runtime_error("pthread_mutex_lock failed"); + } + } + + void Cross_Process_Mutex::unlock() { + auto block = static_cast(m_addr); + pthread_mutex_unlock(&block->mutex); + } } // namespace Psc #endif diff --git a/Core/Statistics/Frequency_Limit.h b/Core/Statistics/Frequency_Limit.h index b34598d..eef4186 100644 --- a/Core/Statistics/Frequency_Limit.h +++ b/Core/Statistics/Frequency_Limit.h @@ -2,11 +2,13 @@ #include #include "global.h" -template class Frequency_Limit_T { +template +class Frequency_Limit_T { public: explicit Frequency_Limit_T(double times_per_second = 1.0) - : interval(1.0 / times_per_second), - last(std::chrono::steady_clock::now()) {} + : interval(1.0 / times_per_second), + last(std::chrono::steady_clock::now()) { + } bool test() { using namespace std::chrono; @@ -28,52 +30,55 @@ private: std::chrono::steady_clock::time_point last; Mutex_Type mtx; }; + using Frequency_Limit_ST = Frequency_Limit_T; using Frequency_Limit = Frequency_Limit_T; -template class Frequency_Limit_Multi_T { +template +class Frequency_Limit_Multi_T { public: - explicit Frequency_Limit_Multi_T(double default_times_per_second = 1.0) - : default_interval_(1.0 / default_times_per_second) {} + using Clock = std::chrono::steady_clock; - // 单个类型的状态 struct Type_Info { - double interval{}; // 秒 - std::chrono::steady_clock::time_point last; + double interval{}; + Clock::time_point last{}; + [[no_unique_address]] State_Type state{}; }; - // 设置某个类型的频率 - void set_rate(std::string_view type, double times_per_second) { - std::lock_guard lock(mtx_); - map_[std::string(type)].interval = 1.0 / times_per_second; - // 注意:不重置 last,避免突发放行 + explicit Frequency_Limit_Multi_T(double default_times_per_second = 1.0) + : default_interval_(1.0 / default_times_per_second) { + } + + void set_rate(std::string_view type, double times_per_second) { + std::lock_guard lock(mtx_); + map_[std::string(type)].interval = 1.0 / times_per_second; } - // 测试是否允许执行 bool test(std::string_view type) { - using namespace std::chrono; - - const auto now = steady_clock::now(); - - std::lock_guard lock(mtx_); + return test_ex(type); + } + State_Type *test_ex(std::string_view type) { + const auto now = Clock::now(); + std::lock_guard lock(mtx_); auto &info = map_[std::string(type)]; - - // 第一次使用该 type if (info.interval == 0.0) { info.interval = default_interval_; info.last = now; - return true; + return &info.state; } - - const double dt = duration_cast>(now - info.last).count(); - - if (dt >= info.interval) { - info.last = now; - return true; + const double dt = std::chrono::duration(now - info.last).count(); + if (dt < info.interval) { + return nullptr; } + info.last = now; + return &info.state; + } - return false; + State_Type *get_state(std::string_view type) { + std::lock_guard lock(mtx_); + auto &info = map_[std::string(type)]; + return &info.state; } private: @@ -84,3 +89,75 @@ private: using Frequency_Limit_Multi_ST = Frequency_Limit_Multi_T; using Frequency_Limit_Multi = Frequency_Limit_Multi_T; + + +template +class Value_Growth_Multi_T { +public: + struct Type_Info { + std::size_t required_increase_count{}; + Value_Type last_value{}; + std::size_t increase_count{}; + bool initialized{}; + [[no_unique_address]] State_Type state{}; + }; + + explicit Value_Growth_Multi_T(std::size_t default_required_increase_count = 5) + : default_required_increase_count_(default_required_increase_count) { + } + + void set_count(std::string_view type, std::size_t required_increase_count) { + std::lock_guard lock(mtx_); + auto &info = map_[std::string(type)]; + info.required_increase_count = required_increase_count; + info.initialized = false; + info.increase_count = 0; + } + + bool test(std::string_view type, Value_Type value) { + return test_ex(type, value); + } + + State_Type *test_ex(std::string_view type, Value_Type value) { + std::lock_guard lock(mtx_); + auto &info = map_[std::string(type)]; + if (info.required_increase_count == 0) { + info.required_increase_count = default_required_increase_count_; + } + if (!info.initialized) { + info.last_value = value; + info.initialized = true; + return nullptr; + } + if (value <= info.last_value) { + info.last_value = value; + info.increase_count = 0; + return nullptr; + } + info.last_value = value; + if (info.increase_count < info.required_increase_count) { + ++info.increase_count; + } + if (info.increase_count < info.required_increase_count) { + return nullptr; + } + return &info.state; + } + + State_Type *get_state(std::string_view type) { + std::lock_guard lock(mtx_); + return &map_[std::string(type)].state; + } + + void reset(std::string_view type) { + std::lock_guard lock(mtx_); + auto &info = map_[std::string(type)]; + info.initialized = false; + info.increase_count = 0; + } + +private: + std::size_t default_required_increase_count_; + std::map map_; + Mutex_Type mtx_; +}; diff --git a/Core/Statistics/Statistics.h b/Core/Statistics/Statistics.h index acda25b..bae884d 100644 --- a/Core/Statistics/Statistics.h +++ b/Core/Statistics/Statistics.h @@ -3,191 +3,205 @@ #include "global.h" namespace Psc { -class Base_Statistics { -protected: - size_t start_time; + class Base_Statistics { + protected: + size_t start_time; - [[nodiscard]] static uint64_t get_current_ms() { - using namespace std::chrono; - return duration_cast(steady_clock::now().time_since_epoch()) - .count(); - } - -public: - Base_Statistics() : start_time(get_current_ms()) {} - virtual JSON to_json() const = 0; - virtual ~Base_Statistics() = default; - - unsigned int get_start_time() const { return start_time; } -}; - -// Speed Statistics 类,继承自 Base_Statistics\ -// 单位B/s - -class Speed_Statistics : public Base_Statistics { -public: - double cur_total{}; - double instant_speed{}; - double average_speed{}; - size_t times{}; - - Speed_Statistics &operator+=(const Speed_Statistics &rhs) { - this->cur_total += rhs.cur_total; - this->instant_speed += rhs.instant_speed; - this->average_speed += rhs.average_speed; - this->times += rhs.times; - return *this; - } - - Speed_Statistics() { clear(); } - - void clear() { - cur_total = 0; - instant_speed = -1; - average_speed = -1; - times = 0; - } - - [[nodiscard]] JSON to_json() const override { - JSON ret = JSON::object(); - // Ret_J(cur_total) - Ret_J(instant_speed) Ret_J(average_speed) Ret_J(times) return ret; - } - - [[nodiscard]] std::string to_string() const { - return VAR_STR_4(instant_speed, average_speed, cur_total, times); - } - - void update(unsigned int len) { - cur_total += static_cast(len); - auto true_period_ms = get_current_ms() - start_time; - size_t period = 100; // 100 ms 统计一次 - times++; - if (instant_speed == -1) { - instant_speed = 0; - start_time = get_current_ms(); + [[nodiscard]] static uint64_t get_current_ms() { + using namespace std::chrono; + return duration_cast(steady_clock::now().time_since_epoch()) + .count(); } - if (true_period_ms > period) { + public: + Base_Statistics() : start_time(get_current_ms()) { + } - // 字节/秒 - instant_speed = cur_total / true_period_ms * 1000; + virtual JSON to_json() const = 0; - // 转换成 KB/s - // instant_speed /= 1024.0f; + virtual ~Base_Statistics() = default; - // 平均速度 - double average_times = 10.0f; - if (average_speed >= 0) { - average_speed = average_speed * (average_times - 1.0f) / average_times + - instant_speed * (1.0f) / average_times; - } else { - average_speed = instant_speed; + unsigned int get_start_time() const { return start_time; } + }; + + // Speed Statistics 类,继承自 Base_Statistics\ + // 单位B/s + + class Speed_Statistics : public Base_Statistics { + public: + double cur_total{}; + double instant_speed{}; + double average_speed{}; + size_t times{}; + + Speed_Statistics &operator+=(const Speed_Statistics &rhs) { + this->cur_total += rhs.cur_total; + this->instant_speed += rhs.instant_speed; + this->average_speed += rhs.average_speed; + this->times += rhs.times; + return *this; + } + + Speed_Statistics() { clear(); } + + void clear() { + cur_total = 0; + instant_speed = -1; + average_speed = -1; + times = 0; + } + + [[nodiscard]] JSON to_json() const override { + JSON ret = JSON::object(); + // Ret_J(cur_total) + Ret_J(instant_speed) + Ret_J(average_speed) + Ret_J(times) + return ret; + } + + [[nodiscard]] std::string to_string() const { + return VAR_STR_4(instant_speed, average_speed, cur_total, times); + } + + void update(unsigned int len) { + cur_total += static_cast(len); + auto true_period_ms = get_current_ms() - start_time; + size_t period = 100; // 100 ms 统计一次 + times++; + if (instant_speed == -1) { + instant_speed = 0; + start_time = get_current_ms(); } - start_time = get_current_ms(); - cur_total = 0; + if (true_period_ms > period) { + // 字节/秒 + instant_speed = cur_total / true_period_ms * 1000; + + // 转换成 KB/s + // instant_speed /= 1024.0f; + + // 平均速度 + double average_times = 10.0f; + if (average_speed >= 0) { + average_speed = average_speed * (average_times - 1.0f) / average_times + + instant_speed * (1.0f) / average_times; + } else { + average_speed = instant_speed; + } + + start_time = get_current_ms(); + cur_total = 0; + } } - } -}; + }; -// Value Statistics 类,继承自 Base_Statistics -class Value_Statistics : public Base_Statistics { -public: - double max{}; - double min{}; - double average{}; - double instant{}; - size_t times = 0; - [[nodiscard]] JSON to_json() const override { - JSON ret = JSON::object(); - Ret_J(max) Ret_J(min) Ret_J(average) Ret_J(instant) return ret; - } + // Value Statistics 类,继承自 Base_Statistics + class Value_Statistics : public Base_Statistics { + public: + double max{}; + double min{}; + double average{}; + double instant{}; + size_t times = 0; - [[nodiscard]] std::string to_string() const { - return VAR_STR_5(min, max, average, instant, times); - } - - Value_Statistics() { clear(); } - - void clear() { - max = std::numeric_limits::min(); - min = std::numeric_limits::max(); - average = -1; - instant = 0; - times = 0; - } - - void update(double value) { - max = std::max(max, value); - min = std::min(min, value); - double average_times = 10.0f; // 平均取近十次 - instant = value; - if (average != -1) { - average = average * (average_times - 1.0f) / average_times + - instant * (1.0f) / average_times; - } else { - average = instant; - } - times++; - } -}; - -// Probability Statistics 类,继承自 Base_Statistics -class Probability_Statistics : public Base_Statistics { -public: - unsigned short instant{}; - unsigned short average{}; - unsigned int count{}; // 成功次数 - unsigned int total{}; // 总次数 - unsigned int total_max{}; // 总次数 - [[nodiscard]] JSON to_json() const override { - JSON ret = JSON::object(); - Ret_J(instant) Ret_J(average) Ret_J(count) Ret_J(total) - Ret_J(total_max) return ret; - } - Probability_Statistics() { clear(); } - - void clear() { - instant = 0; - average = 0; - count = 0; - total = 0; - total_max = 10; - } - - // 成功调用 update(1) - // 失败调用 update(-1) - // 可以调用 update(n) n > 0 表示连续成功n次 - // 可以调用 update(n) n < 0 表示连续失败n次 - void update(int count_change) { - if (count_change == 0) { - count_change = -1; - } - if (count_change > 0) { - count += count_change; - total += count_change; - } else if (count_change < 0) { - total += -count_change; + [[nodiscard]] JSON to_json() const override { + JSON ret = JSON::object(); + Ret_J(max) + Ret_J(min) + Ret_J(average) + Ret_J(instant) + return ret; } - if (total < total_max) - return; // 至少累计 100 次才统计一次 - - // instant 概率 (0-1000) - instant = (count * 1000) / total; - - if (average == 0) { - average = instant; - } else { - // 平滑平均,base = 8 - average = ((average << 3) - average + instant) >> 3; + [[nodiscard]] std::string to_string() const { + return VAR_STR_5(min, max, average, instant, times); } - // 清零重新统计一批 - count = 0; - total = 0; - } -}; + Value_Statistics() { clear(); } -} // namespace Psc \ No newline at end of file + void clear() { + max = std::numeric_limits::min(); + min = std::numeric_limits::max(); + average = -1; + instant = 0; + times = 0; + } + + void update(double value) { + max = std::max(max, value); + min = std::min(min, value); + double average_times = 10.0f; // 平均取近十次 + instant = value; + if (average != -1) { + average = average * (average_times - 1.0f) / average_times + + instant * (1.0f) / average_times; + } else { + average = instant; + } + times++; + } + }; + + // Probability Statistics 类,继承自 Base_Statistics + class Probability_Statistics : public Base_Statistics { + public: + unsigned short instant{}; + unsigned short average{}; + unsigned int count{}; // 成功次数 + unsigned int total{}; // 总次数 + unsigned int total_max{}; // 总次数 + [[nodiscard]] JSON to_json() const override { + JSON ret = JSON::object(); + Ret_J(instant) + Ret_J(average) + Ret_J(count) + Ret_J(total) + Ret_J(total_max) + return ret; + } + + Probability_Statistics() { clear(); } + + void clear() { + instant = 0; + average = 0; + count = 0; + total = 0; + total_max = 10; + } + + // 成功调用 update(1) + // 失败调用 update(-1) + // 可以调用 update(n) n > 0 表示连续成功n次 + // 可以调用 update(n) n < 0 表示连续失败n次 + void update(int count_change) { + if (count_change == 0) { + count_change = -1; + } + if (count_change > 0) { + count += count_change; + total += count_change; + } else if (count_change < 0) { + total += -count_change; + } + + if (total < total_max) + return; // 至少累计 100 次才统计一次 + + // instant 概率 (0-1000) + instant = (count * 1000) / total; + + if (average == 0) { + average = instant; + } else { + // 平滑平均,base = 8 + average = ((average << 3) - average + instant) >> 3; + } + + // 清零重新统计一批 + count = 0; + total = 0; + } + }; +} // namespace Psc diff --git a/Core/system/export.cpp b/Core/system/export.cpp index 830a7bd..e0efc0e 100644 --- a/Core/system/export.cpp +++ b/Core/system/export.cpp @@ -39,353 +39,359 @@ #endif namespace Psc { -namespace { -bool local_time_from_time_t(std::time_t value, std::tm &out) noexcept { + namespace { + bool local_time_from_time_t(std::time_t value, std::tm &out) noexcept { #if defined(_WIN32) - // MinGW / Clang / MSVC 对 localtime_s 支持不完全一致,这里统一用带锁的 - // std::localtime。 - static std::mutex mtx; - std::lock_guard lock(mtx); - std::tm *tmp = std::localtime(&value); - if (!tmp) { - return false; - } - out = *tmp; - return true; + // MinGW / Clang / MSVC 对 localtime_s 支持不完全一致,这里统一用带锁的 + // std::localtime。 + static std::mutex mtx; + std::lock_guard lock(mtx); + std::tm *tmp = std::localtime(&value); + if (!tmp) { + return false; + } + out = *tmp; + return true; #elif defined(__unix__) || defined(__APPLE__) - return ::localtime_r(&value, &out) != nullptr; + return ::localtime_r(&value, &out) != nullptr; #else - std::tm *tmp = std::localtime(&value); - if (!tmp) { - return false; - } - out = *tmp; - return true; + std::tm *tmp = std::localtime(&value); + if (!tmp) { + return false; + } + out = *tmp; + return true; #endif -} -} // namespace + } + } // namespace -std::uint64_t get_current_millisecond_timestamp() { - const auto now = std::chrono::system_clock::now(); - const auto duration = std::chrono::duration_cast( + std::uint64_t get_current_millisecond_timestamp() { + const auto now = std::chrono::system_clock::now(); + const auto duration = std::chrono::duration_cast( now.time_since_epoch()); - return static_cast(duration.count()); -} + return static_cast(duration.count()); + } -int redict_main_with_gtest(int argc, char *argv[], - Main_Function_Type main_func) { + int redict_main_with_gtest(int argc, char *argv[], + Main_Function_Type main_func) { #if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC) - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif - set_console_utf8(); + set_console_utf8(); - if (argc == 1) { + if (argc == 1) { #ifdef _USE_GTEST - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); #else - return main_func(argc, argv); + return main_func(argc, argv); #endif - } + } - const int new_argc = argc - 1; - char **new_argv = &argv[1]; + const int new_argc = argc - 1; + char **new_argv = &argv[1]; - int ret = 0; + int ret = 0; #ifdef _USE_GTEST - const std::string type = argv[1]; - if (type != "main") { - ::testing::InitGoogleTest(&argc, argv); - ret = RUN_ALL_TESTS(); - } else { - ret = main_func(new_argc, new_argv); - } -#else - ret = main_func(new_argc, new_argv); -#endif - -#if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC) - if (_CrtDumpMemoryLeaks()) { - std::cout << "[CRT] Detected memory leaks.\n"; - } else { - std::cout << "[CRT] No memory leaks detected.\n"; - } -#endif - - std::cout << "ecap ret: " << ret << std::endl; - return ret; -} - -std::string get_exe_dir() { - const std::string exe_path = get_exe_path(); - if (exe_path.empty()) { - return ""; - } - return std::filesystem::path(exe_path).parent_path().string(); -} - -std::string get_abs_path(std::string_view path) { - if (path.empty()) { - return ""; - } - - if (path.rfind("@", 0) == 0) { - return get_exe_dir() + std::string(path.substr(1)); - } - - if (path.rfind("~", 0) == 0) { - return get_home_dir() + std::string(path.substr(1)); - } - - return std::string(path); -} - -std::string utc_2_local_time(std::time_t us_timestamp, const char *format) { - std::tm local_tm{}; - if (!local_time_from_time_t(us_timestamp, local_tm)) { - return "invalid_time"; - } - - std::ostringstream oss; - oss << std::put_time(&local_tm, format ? format : "%H:%M:%S"); - return oss.str(); -} - -namespace { -constexpr std::uint8_t alphabet_map[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -constexpr std::uint8_t invalid_base64 = 255; - -constexpr std::uint8_t decode_char(unsigned char ch) noexcept { - return (ch >= 'A' && ch <= 'Z') ? static_cast(ch - 'A') - : (ch >= 'a' && ch <= 'z') ? static_cast(ch - 'a' + 26) - : (ch >= '0' && ch <= '9') ? static_cast(ch - '0' + 52) - : (ch == '+') ? 62 - : (ch == '/') ? 63 - : (ch == '=') ? 64 - : invalid_base64; -} - -std::uint32_t base64_encode_raw(const std::uint8_t *text, - std::uint32_t text_len, std::uint8_t *encode) { - std::uint32_t i = 0; - std::uint32_t j = 0; - - for (; i + 3 <= text_len; i += 3) { - encode[j++] = alphabet_map[text[i] >> 2]; - encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)]; - encode[j++] = - alphabet_map[((text[i + 1] << 2) & 0x3c) | (text[i + 2] >> 6)]; - encode[j++] = alphabet_map[text[i + 2] & 0x3f]; - } - - if (i < text_len) { - const std::uint32_t tail = text_len - i; - if (tail == 1) { - encode[j++] = alphabet_map[text[i] >> 2]; - encode[j++] = alphabet_map[(text[i] << 4) & 0x30]; - encode[j++] = '='; - encode[j++] = '='; + const std::string type = argv[1]; + if (type != "main") { + ::testing::InitGoogleTest(&argc, argv); + ret = RUN_ALL_TESTS(); } else { - encode[j++] = alphabet_map[text[i] >> 2]; - encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)]; - encode[j++] = alphabet_map[(text[i + 1] << 2) & 0x3c]; - encode[j++] = '='; + ret = main_func(new_argc, new_argv); } +#else + ret = main_func(new_argc, new_argv); +#endif + +#if defined(_MSC_VER) && defined(_CRTDBG_MAP_ALLOC) + if (_CrtDumpMemoryLeaks()) { + std::cout << "[CRT] Detected memory leaks.\n"; + } else { + std::cout << "[CRT] No memory leaks detected.\n"; + } +#endif + + std::cout << "ecap ret: " << ret << std::endl; + return ret; } - return j; -} - -std::uint32_t base64_decode_raw(const std::uint8_t *code, - std::uint32_t code_len, std::uint8_t *plain) { - if (!code || !plain || (code_len & 0x03U) != 0U) { - return 0; + std::string get_exe_dir() { + const std::string exe_path = get_exe_path(); + if (exe_path.empty()) { + return ""; + } + return std::filesystem::path(exe_path).parent_path().string(); } - std::uint32_t j = 0; - for (std::uint32_t i = 0; i < code_len; i += 4) { - const std::uint8_t q0 = decode_char(code[i]); - const std::uint8_t q1 = decode_char(code[i + 1]); - const std::uint8_t q2 = decode_char(code[i + 2]); - const std::uint8_t q3 = decode_char(code[i + 3]); + std::string get_abs_path(std::string_view path) { + if (path.empty()) { + return ""; + } + + if (path.rfind("@", 0) == 0) { + return get_exe_dir() + std::string(path.substr(1)); + } + + if (path.rfind("~", 0) == 0) { + return get_home_dir() + std::string(path.substr(1)); + } + + return std::string(path); + } + + std::string utc_2_local_time(std::time_t us_timestamp, const char *format) { + std::tm local_tm{}; + if (!local_time_from_time_t(us_timestamp, local_tm)) { + return "invalid_time"; + } + + std::ostringstream oss; + oss << std::put_time(&local_tm, format ? format : "%H:%M:%S"); + return oss.str(); + } + + namespace { + constexpr std::uint8_t alphabet_map[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + constexpr std::uint8_t invalid_base64 = 255; + + constexpr std::uint8_t decode_char(unsigned char ch) noexcept { + return (ch >= 'A' && ch <= 'Z') + ? static_cast(ch - 'A') + : (ch >= 'a' && ch <= 'z') + ? static_cast(ch - 'a' + 26) + : (ch >= '0' && ch <= '9') + ? static_cast(ch - '0' + 52) + : (ch == '+') + ? 62 + : (ch == '/') + ? 63 + : (ch == '=') + ? 64 + : invalid_base64; + } + + std::uint32_t base64_encode_raw(const std::uint8_t *text, + std::uint32_t text_len, std::uint8_t *encode) { + std::uint32_t i = 0; + std::uint32_t j = 0; + + for (; i + 3 <= text_len; i += 3) { + encode[j++] = alphabet_map[text[i] >> 2]; + encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)]; + encode[j++] = + alphabet_map[((text[i + 1] << 2) & 0x3c) | (text[i + 2] >> 6)]; + encode[j++] = alphabet_map[text[i + 2] & 0x3f]; + } + + if (i < text_len) { + const std::uint32_t tail = text_len - i; + if (tail == 1) { + encode[j++] = alphabet_map[text[i] >> 2]; + encode[j++] = alphabet_map[(text[i] << 4) & 0x30]; + encode[j++] = '='; + encode[j++] = '='; + } else { + encode[j++] = alphabet_map[text[i] >> 2]; + encode[j++] = alphabet_map[((text[i] << 4) & 0x30) | (text[i + 1] >> 4)]; + encode[j++] = alphabet_map[(text[i + 1] << 2) & 0x3c]; + encode[j++] = '='; + } + } - if (q0 >= 64 || q1 >= 64) { return j; } - plain[j++] = static_cast((q0 << 2) | (q1 >> 4)); + std::uint32_t base64_decode_raw(const std::uint8_t *code, + std::uint32_t code_len, std::uint8_t *plain) { + if (!code || !plain || (code_len & 0x03U) != 0U) { + return 0; + } + + std::uint32_t j = 0; + for (std::uint32_t i = 0; i < code_len; i += 4) { + const std::uint8_t q0 = decode_char(code[i]); + const std::uint8_t q1 = decode_char(code[i + 1]); + const std::uint8_t q2 = decode_char(code[i + 2]); + const std::uint8_t q3 = decode_char(code[i + 3]); + + if (q0 >= 64 || q1 >= 64) { + return j; + } + + plain[j++] = static_cast((q0 << 2) | (q1 >> 4)); + + if (q2 == 64) { + break; + } + if (q2 > 64) { + return j; + } + + plain[j++] = static_cast((q1 << 4) | (q2 >> 2)); + + if (q3 == 64) { + break; + } + if (q3 > 64) { + return j; + } + + plain[j++] = static_cast((q2 << 6) | q3); + } - if (q2 == 64) { - break; - } - if (q2 > 64) { return j; } + } // namespace - plain[j++] = static_cast((q1 << 4) | (q2 >> 2)); - - if (q3 == 64) { - break; - } - if (q3 > 64) { - return j; - } - - plain[j++] = static_cast((q2 << 6) | q3); + std::string base64_encode(std::string_view data) { + const auto text_len = static_cast(data.size()); + std::vector encode(4 * ((text_len + 2) / 3)); + const std::uint32_t encoded_len = + base64_encode_raw(reinterpret_cast(data.data()), + text_len, encode.data()); + return std::string(encode.begin(), encode.begin() + encoded_len); } - return j; -} -} // namespace + std::string base64_decode(std::string_view data) { + const auto code_len = static_cast(data.size()); + if ((code_len & 0x03U) != 0U) { + return ""; + } -std::string base64_encode(std::string_view data) { - const auto text_len = static_cast(data.size()); - std::vector encode(4 * ((text_len + 2) / 3)); - const std::uint32_t encoded_len = - base64_encode_raw(reinterpret_cast(data.data()), - text_len, encode.data()); - return std::string(encode.begin(), encode.begin() + encoded_len); -} - -std::string base64_decode(std::string_view data) { - const auto code_len = static_cast(data.size()); - if ((code_len & 0x03U) != 0U) { - return ""; + std::vector plain(code_len * 3 / 4); + const std::uint32_t decoded_len = + base64_decode_raw(reinterpret_cast(data.data()), + code_len, plain.data()); + return std::string(plain.begin(), plain.begin() + decoded_len); } - std::vector plain(code_len * 3 / 4); - const std::uint32_t decoded_len = - base64_decode_raw(reinterpret_cast(data.data()), - code_len, plain.data()); - return std::string(plain.begin(), plain.begin() + decoded_len); -} - -std::string signal_to_string(int signal) { - switch (signal) { - case SIGINT: - return "SIGINT (Ctrl+C)"; - case SIGILL: - return "SIGILL (Illegal Instruction)"; - case SIGFPE: - return "SIGFPE (Floating Point Exception)"; - case SIGSEGV: - return "SIGSEGV (Segmentation Fault)"; - case SIGTERM: - return "SIGTERM (Termination Request)"; - case SIGABRT: - return "SIGABRT (Abort Signal)"; + std::string signal_to_string(int signal) { + switch (signal) { + case SIGINT: + return "SIGINT (Ctrl+C)"; + case SIGILL: + return "SIGILL (Illegal Instruction)"; + case SIGFPE: + return "SIGFPE (Floating Point Exception)"; + case SIGSEGV: + return "SIGSEGV (Segmentation Fault)"; + case SIGTERM: + return "SIGTERM (Termination Request)"; + case SIGABRT: + return "SIGABRT (Abort Signal)"; #if defined(_WIN32) && defined(SIGBREAK) - case SIGBREAK: - return "SIGBREAK (Breakpoint)"; + case SIGBREAK: + return "SIGBREAK (Breakpoint)"; #endif #if defined(_MSC_VER) && defined(SIGABRT_COMPAT) - case SIGABRT_COMPAT: - return "SIGABRT_COMPAT (Abort Compatibility Signal)"; + case SIGABRT_COMPAT: + return "SIGABRT_COMPAT (Abort Compatibility Signal)"; #endif - default: - return "Unknown signal:" + std::to_string(signal); - } -} - -std::string get_current_time_as_string() { - const auto now = std::chrono::system_clock::now(); - const std::time_t now_time = std::chrono::system_clock::to_time_t(now); - - std::tm local_tm{}; - if (!local_time_from_time_t(now_time, local_tm)) { - return "invalid_time"; - } - - std::ostringstream ss; - ss << std::put_time(&local_tm, "%Y-%m-%d_%H-%M-%S"); - return ss.str(); -} - -namespace fs = std::filesystem; - -void create_file_if_not_exists(std::string_view file_name) { - std::error_code ec; - const fs::path file_path(file_name); - - if (fs::exists(file_path, ec)) { - return; - } - - const fs::path parent = file_path.parent_path(); - if (!parent.empty()) { - fs::create_directories(parent, ec); - if (ec) { - std::cerr << "Failed to create parent directory: " << parent.string() - << ", error: " << ec.message() << std::endl; - return; + default: + return "Unknown signal:" + std::to_string(signal); } } - std::ofstream ofs(file_path, std::ios::out | std::ios::app); - if (!ofs) { - std::cerr << "Failed to create the file: " << file_name << std::endl; - return; + std::string get_current_time_as_string() { + const auto now = std::chrono::system_clock::now(); + const std::time_t now_time = std::chrono::system_clock::to_time_t(now); + + std::tm local_tm{}; + if (!local_time_from_time_t(now_time, local_tm)) { + return "invalid_time"; + } + + std::ostringstream ss; + ss << std::put_time(&local_tm, "%Y-%m-%d_%H-%M-%S"); + return ss.str(); } - const auto perms = - fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all; - fs::permissions(file_path, perms, ec); -} + namespace fs = std::filesystem; -void create_dir_if_not_exists(std::string_view dir_name) { - std::error_code ec; - const fs::path dir_path(dir_name); + void create_file_if_not_exists(std::string_view file_name) { + std::error_code ec; + const fs::path file_path(file_name); - if (!fs::exists(dir_path, ec)) { - fs::create_directories(dir_path, ec); - if (ec) { - std::cerr << "Failed to create directory: " << dir_name - << ", error: " << ec.message() << std::endl; + if (fs::exists(file_path, ec)) { return; } + + const fs::path parent = file_path.parent_path(); + if (!parent.empty()) { + fs::create_directories(parent, ec); + if (ec) { + std::cerr << "Failed to create parent directory: " << parent.string() + << ", error: " << ec.message() << std::endl; + return; + } + } + + std::ofstream ofs(file_path, std::ios::out | std::ios::app); + if (!ofs) { + std::cerr << "Failed to create the file: " << file_name << std::endl; + return; + } + + const auto perms = + fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all; + fs::permissions(file_path, perms, ec); + } + + void create_dir_if_not_exists(std::string_view dir_name) { + std::error_code ec; + const fs::path dir_path(dir_name); + + if (!fs::exists(dir_path, ec)) { + fs::create_directories(dir_path, ec); + if (ec) { + std::cerr << "Failed to create directory: " << dir_name + << ", error: " << ec.message() << std::endl; + return; + } + } + + const auto perms = + fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all; + fs::permissions(dir_path, perms, ec); + } + + void write_stack_trace_to_file(std::string_view stack_trace, + std::string_view filename) { + create_file_if_not_exists(filename); + std::ofstream out_file(filename.data(), std::ios::out | std::ios::app); + if (out_file.is_open()) { + out_file << stack_trace << std::endl; + } else { + std::cerr << "Unable to open file " << filename << " for writing." + << std::endl; + } } - const auto perms = - fs::perms::owner_all | fs::perms::group_all | fs::perms::others_all; - fs::permissions(dir_path, perms, ec); -} - -void write_stack_trace_to_file(std::string_view stack_trace, - std::string_view filename) { - create_file_if_not_exists(filename); - std::ofstream out_file(filename, std::ios::out | std::ios::app); - if (out_file.is_open()) { - out_file << stack_trace << std::endl; - } else { - std::cerr << "Unable to open file " << filename << " for writing." - << std::endl; - } -} - -void signal_handler(int signal_code) { - std::cout << "signal_handler [" << signal_to_string(signal_code) << "]" - << std::endl; - stop_program = signal_code; -} - -void signal_handler_ignore(int) {} - -void init_signal_config(std::set normal_quit, - std::set ignore_signal) { - if (is_big_endian) { - std::cout << "大端序 This system is Big-endian." << std::endl; - } else { - std::cout << "小端序 This system is Little-endian." << std::endl; + void signal_handler(int signal_code) { + std::cout << "signal_handler [" << signal_to_string(signal_code) << "]" + << std::endl; + stop_program = signal_code; } - for (auto signum : normal_quit) { - std::signal(signum, signal_handler); + void signal_handler_ignore(int) { } - for (auto signum : ignore_signal) { - std::signal(signum, signal_handler_ignore); - } -} + void init_signal_config(std::set normal_quit, + std::set ignore_signal) { + if (is_big_endian) { + std::cout << "大端序 This system is Big-endian." << std::endl; + } else { + std::cout << "小端序 This system is Little-endian." << std::endl; + } + + for (auto signum: normal_quit) { + std::signal(signum, signal_handler); + } + for (auto signum: ignore_signal) { + std::signal(signum, signal_handler_ignore); + } + } } // namespace Psc diff --git a/Core/system/linux.cpp b/Core/system/linux.cpp index c681292..62f0088 100644 --- a/Core/system/linux.cpp +++ b/Core/system/linux.cpp @@ -41,399 +41,403 @@ #endif namespace Psc { -namespace { -std::string trim_copy(std::string_view value) { - auto not_space = [](unsigned char ch) { return !std::isspace(ch); }; - value.erase(value.begin(), - std::find_if(value.begin(), value.end(), not_space)); - value.erase(std::find_if(value.rbegin(), value.rend(), not_space).base(), - value.end()); - return value; -} - -std::string read_first_line(std::string_view file_name) { - std::ifstream in(file_name); - std::string line; - if (std::getline(in, line)) { - return trim_copy(line); - } - return ""; -} - -std::string execute_command_single(const char *cmd) { - if (!cmd || !*cmd) { - return ""; - } - - int pipefd[2]{}; - if (pipe(pipefd) != 0) { - return ""; - } - - pid_t pid = fork(); - if (pid < 0) { - close(pipefd[0]); - close(pipefd[1]); - return ""; - } - - if (pid == 0) { - close(pipefd[0]); - dup2(pipefd[1], STDOUT_FILENO); - close(pipefd[1]); - setsid(); - execl("/bin/sh", "sh", "-c", cmd, static_cast(nullptr)); - _exit(127); - } - - close(pipefd[1]); - - std::string out; - char buf[1024]; - ssize_t n = 0; - while ((n = read(pipefd[0], buf, sizeof(buf))) > 0) { - out.append(buf, static_cast(n)); - } - close(pipefd[0]); - waitpid(pid, nullptr, 0); - return out; -} - -size_t read_proc_status_kb(const char *key) { - std::ifstream status("/proc/self/status"); - std::string line; - while (std::getline(status, line)) { - if (line.rfind(key, 0) == 0) { - size_t value_kb = 0; - if (std::sscanf(line.c_str(), "%*s %zu kB", &value_kb) == 1) { - return value_kb; + namespace { + std::string trim_copy(std::string_view value) { + auto not_space = [](unsigned char ch) { + return !std::isspace(ch); + }; + auto first = std::find_if(value.begin(), value.end(), not_space); + if (first == value.end()) { + return {}; } + auto last = std::find_if(value.rbegin(), value.rend(), not_space).base(); + return std::string(first, last); } - } - return 0; -} -} // namespace -std::string extract_address(std::string_view input) { - const std::regex address_regex(R"(\[([0-9a-fA-Fx]+)\])"); - std::smatch match; - if (std::regex_search(input, match, address_regex)) { - return match[1].str(); - } - return ""; -} + std::string read_first_line(std::string_view file_name) { + std::ifstream in(file_name.data()); + std::string line; + if (std::getline(in, line)) { + return trim_copy(line); + } + return ""; + } -std::string execute_command(std::string_view cmd) { - FILE *fp = popen(cmd.c_str(), "r"); - if (!fp) { - return ""; + std::string execute_command_single(const char *cmd) { + if (!cmd || !*cmd) { + return ""; + } + + int pipefd[2]{}; + if (pipe(pipefd) != 0) { + return ""; + } + + pid_t pid = fork(); + if (pid < 0) { + close(pipefd[0]); + close(pipefd[1]); + return ""; + } + + if (pid == 0) { + close(pipefd[0]); + dup2(pipefd[1], STDOUT_FILENO); + close(pipefd[1]); + setsid(); + execl("/bin/sh", "sh", "-c", cmd, static_cast(nullptr)); + _exit(127); + } + + close(pipefd[1]); + + std::string out; + char buf[1024]; + ssize_t n = 0; + while ((n = read(pipefd[0], buf, sizeof(buf))) > 0) { + out.append(buf, static_cast(n)); + } + close(pipefd[0]); + waitpid(pid, nullptr, 0); + return out; + } + + size_t read_proc_status_kb(const char *key) { + std::ifstream status("/proc/self/status"); + std::string line; + while (std::getline(status, line)) { + if (line.rfind(key, 0) == 0) { + size_t value_kb = 0; + if (std::sscanf(line.c_str(), "%*s %zu kB", &value_kb) == 1) { + return value_kb; + } + } + } + return 0; + } + } // namespace + + std::string extract_address(std::string_view input) { + static const std::regex address_regex(R"(\[([0-9a-fA-Fx]+)\])"); + std::match_results match; + if (std::regex_search(input.begin(), input.end(), match, address_regex)) { + return match[1].str(); + } + return {}; } - std::string result; - char buffer[1024]; - while (fgets(buffer, sizeof(buffer), fp) != nullptr) { - result.append(buffer); + std::string execute_command(std::string_view cmd) { + FILE *fp = popen(cmd.data(), "r"); + if (!fp) { + return ""; + } + + std::string result; + char buffer[1024]; + while (fgets(buffer, sizeof(buffer), fp) != nullptr) { + result.append(buffer); + } + pclose(fp); + return result; } - pclose(fp); - return result; -} -std::string get_tool(std::string_view name) { - std::string ret = name; - const std::string exe_dir = get_exe_dir(); - const std::string ret_1 = exe_dir + "/" + name; + std::string get_tool(std::string_view _name) { + std::string name(_name); + std::string ret = name; + const std::string exe_dir = get_exe_dir(); + const std::string ret_1 = exe_dir + "/" + name; + std::filesystem::path path = std::filesystem::u8path(exe_dir); + const auto parent = path.parent_path(); + const std::string parent_path = path_to_utf8(parent); + const std::string ret_2 = parent_path + "/lib/" + name; - std::filesystem::path path = std::filesystem::u8path(exe_dir); - const auto parent = path.parent_path(); - const std::string parent_path = path_to_utf8(parent); - const std::string ret_2 = parent_path + "/lib/" + name; - - if (std::filesystem::exists(ret_1)) { - ret = ret_1; - } else if (std::filesystem::exists(ret_2)) { - ret = ret_2; + if (std::filesystem::exists(ret_1)) { + ret = ret_1; + } else if (std::filesystem::exists(ret_2)) { + ret = ret_2; + } + return ret; } - return ret; -} -std::string get_stack_trace() { - std::ostringstream oss; - constexpr int max_frames = 64; - void *stack[max_frames]{}; + std::string get_stack_trace() { + std::ostringstream oss; + constexpr int max_frames = 64; + void *stack[max_frames]{}; - const int stack_size = backtrace(stack, max_frames); - char **symbols = backtrace_symbols(stack, stack_size); - if (!symbols) { - oss << "Error retrieving stack symbols\n"; + const int stack_size = backtrace(stack, max_frames); + char **symbols = backtrace_symbols(stack, stack_size); + if (!symbols) { + oss << "Error retrieving stack symbols\n"; + return oss.str(); + } + + const std::string addr2line = get_tool("addr2line"); + const std::string cppfilt = get_tool("c++filt"); + const std::string exe = get_exe_path(); + + oss << "addr2line: " << addr2line << '\n'; + oss << "cppfilt: " << cppfilt << '\n'; + oss << "Stack trace: " << exe << '\n'; + + for (int i = 0; i < stack_size; ++i) { + const auto addr = reinterpret_cast(stack[i]); + oss << "【" << std::dec << std::left << std::setw(2) << i << "】"; + + char cmd[4096]; + std::snprintf(cmd, sizeof(cmd), R"(%s -e "%s" -f -i 0x%lx | %s)", + addr2line.c_str(), exe.c_str(), + static_cast(addr), cppfilt.c_str()); + + std::string str = execute_command_single(cmd); + if (str.empty() && symbols[i]) { + str = symbols[i]; + } + std::replace(str.begin(), str.end(), '\n', ' '); + oss << " " << str << "【0x" << std::hex << addr << std::dec << "】" + << '\n'; + } + + free(symbols); return oss.str(); } - const std::string addr2line = get_tool("addr2line"); - const std::string cppfilt = get_tool("c++filt"); - const std::string exe = get_exe_path(); - - oss << "addr2line: " << addr2line << '\n'; - oss << "cppfilt: " << cppfilt << '\n'; - oss << "Stack trace: " << exe << '\n'; - - for (int i = 0; i < stack_size; ++i) { - const auto addr = reinterpret_cast(stack[i]); - oss << "【" << std::dec << std::left << std::setw(2) << i << "】"; - - char cmd[4096]; - std::snprintf(cmd, sizeof(cmd), R"(%s -e "%s" -f -i 0x%lx | %s)", - addr2line.c_str(), exe.c_str(), - static_cast(addr), cppfilt.c_str()); - - std::string str = execute_command_single(cmd); - if (str.empty() && symbols[i]) { - str = symbols[i]; - } - std::replace(str.begin(), str.end(), '\n', ' '); - oss << " " << str << "【0x" << std::hex << addr << std::dec << "】" - << '\n'; - } - - free(symbols); - return oss.str(); -} - -std::string get_computer_serial_number() { - static const char *candidates[] = { + std::string get_computer_serial_number() { + static const char *candidates[] = { "/sys/class/dmi/id/product_serial", "/sys/class/dmi/id/board_serial", - "/sys/class/dmi/id/product_uuid", "/sys/class/dmi/id/chassis_serial"}; + "/sys/class/dmi/id/product_uuid", "/sys/class/dmi/id/chassis_serial" + }; - for (const char *file : candidates) { - std::string value = read_first_line(file); - if (!value.empty() && value != "None" && value != "Not Specified" && - value != "To Be Filled By O.E.M.") { - return value; - } - } - - return "Unknown"; -} - -void set_current_thread_name(std::string_view name) { - std::string trimmed = - name.substr(0, 15); // Linux 线程名最多 16 字节,含结尾 \0。 - (void)pthread_setname_np(pthread_self(), trimmed.c_str()); -} - -void set_console_utf8() { - if (setlocale(LC_ALL, "") != nullptr) { - return; - } - if (setlocale(LC_ALL, "C.UTF-8") != nullptr) { - return; - } - (void)setlocale(LC_ALL, "en_US.UTF-8"); -} - -PSC_DEFINE_TRIPLE_API_FROM_BOOL(void *, load_library, PSC_LOAD_LIBRARY_PARAMS) - -bool load_library_ec(std::string_view library_path, void *&out, - std::error_code &ec) noexcept { - out = nullptr; - ec.clear(); - (void)dlerror(); - - void *library = dlopen(library_path.c_str(), RTLD_LAZY); - if (!library) { - const char *err = dlerror(); - std::cerr << library_path - << " Failed to load library: " << (err ? err : "unknown") << '\n'; - ec = std::make_error_code(std::errc::no_such_file_or_directory); - return false; - } - - out = library; - return true; -} - -void *load_library_fail_fast(std::string_view library_path) { - auto ret = try_load_library(library_path); - if (!ret) { - Psc::fail_fast(); - } - return ret.value(); -} - -void free_library(void *library) { - if (library) { - dlclose(library); - } -} - -PSC_DEFINE_TRIPLE_API_FROM_BOOL(void *, load_function, PSC_LOAD_FUNCTION_PARAMS) - -bool load_function_ec(void *library, std::string_view function_name, - void *&out, std::error_code &ec) noexcept { - out = nullptr; - ec.clear(); - - if (!library) { - std::cerr << function_name << " function not loaded (library == nullptr)\n"; - ec = std::make_error_code(std::errc::invalid_argument); - return false; - } - - (void)dlerror(); - void *func = dlsym(library, function_name.c_str()); - const char *err = dlerror(); - if (err != nullptr) { - std::cerr << "dlsym failed for " << function_name << " : " << err << '\n'; - ec = std::make_error_code(std::errc::function_not_supported); - return false; - } - - out = func; - return true; -} - -void *load_function_fail_fast(void *library, std::string_view function_name) { - auto ret = try_load_function(library, function_name); - if (!ret) { - Psc::fail_fast(); - } - return ret.value(); -} - -std::string get_error_message() { - const int ec = errno; - return "错误码:" + std::to_string(ec) + ": " + std::strerror(ec); -} - -std::string get_error_message(ERROR_CODE_TYPE ec) { - return "错误码:" + std::to_string(ec) + ": " + - std::strerror(static_cast(ec)); -} - -ERROR_CODE_TYPE get_error_code() { return errno; } - -void set_error_code(ERROR_CODE_TYPE code) { errno = static_cast(code); } - -std::string get_exe_path() { - std::vector buffer(PATH_MAX + 1, '\0'); - const ssize_t len = - readlink("/proc/self/exe", buffer.data(), buffer.size() - 1); - if (len == -1) { - return ""; - } - buffer[static_cast(len)] = '\0'; - return std::string(buffer.data()); -} - -std::string get_home_dir() { - if (const char *home = std::getenv("HOME")) { - return home; - } - - struct passwd *pw = getpwuid(getuid()); - if (pw && pw->pw_dir) { - return std::string(pw->pw_dir); - } - - return ""; -} - -bool set_thread_priority(void *thread_ptr, int priority, bool realtime) { - if (!thread_ptr) { - return false; - } - - pthread_t pthread = *static_cast(thread_ptr); - int policy = realtime ? SCHED_FIFO : SCHED_OTHER; - - sched_param param{}; - if (realtime) { - const int min_prio = sched_get_priority_min(policy); - const int max_prio = sched_get_priority_max(policy); - if (min_prio >= 0 && max_prio >= min_prio) { - priority = std::max(min_prio, std::min(max_prio, priority)); - } - param.sched_priority = priority; - } else { - param.sched_priority = 0; - } - - const int ret = pthread_setschedparam(pthread, policy, ¶m); - if (ret != 0) { - errno = ret; - return false; - } - return true; -} - -bool set_thread_priority(std::thread *thread, int priority, bool realtime) { - if (!thread) { - return false; - } - - pthread_t pthread = thread->native_handle(); - return set_thread_priority(static_cast(&pthread), priority, realtime); -} - -bool set_thread_affinity(void *thread_ptr, unsigned cpu_index) { - if (!thread_ptr) { - return false; - } - - if (cpu_index >= CPU_SETSIZE) { - errno = EINVAL; - return false; - } - - pthread_t pthread = *static_cast(thread_ptr); - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(cpu_index, &cpuset); - - const int ret = pthread_setaffinity_np(pthread, sizeof(cpu_set_t), &cpuset); - if (ret != 0) { - errno = ret; - std::cerr << "Error setting thread affinity: " << std::strerror(ret) - << std::endl; - return false; - } - - return true; -} - -bool set_thread_affinity(std::thread *thread, unsigned cpu_index) { - if (!thread) { - return false; - } - - pthread_t pthread = thread->native_handle(); - return set_thread_affinity(static_cast(&pthread), cpu_index); -} - -std::string get_memory_info() { - auto ret = JSON::object(); - ret.append({"VmRSS (MB)", read_proc_status_kb("VmRSS:") / 1024}); - ret.append({"VmSize (MB)", read_proc_status_kb("VmSize:") / 1024}); - ret.append({"VmPeak (MB)", read_proc_status_kb("VmPeak:") / 1024}); - ret.append({"VmHWM (MB)", read_proc_status_kb("VmHWM:") / 1024}); - return ret.to_json_string(); -} - -size_t get_system_memory() { - std::ifstream meminfo("/proc/meminfo"); - std::string line; - while (std::getline(meminfo, line)) { - if (line.rfind("MemTotal:", 0) == 0) { - size_t mem_total_kb = 0; - if (std::sscanf(line.c_str(), "MemTotal: %zu kB", &mem_total_kb) == 1) { - return mem_total_kb * 1024; + for (const char *file: candidates) { + std::string value = read_first_line(file); + if (!value.empty() && value != "None" && value != "Not Specified" && + value != "To Be Filled By O.E.M.") { + return value; } } + + return "Unknown"; } - return 0; -} -size_t get_process_memory() { return read_proc_status_kb("VmRSS:") * 1024; } + void set_current_thread_name(std::string_view name) { + auto trimmed = + name.substr(0, 15); // Linux 线程名最多 16 字节,含结尾 \0。 + (void) pthread_setname_np(pthread_self(), trimmed.data()); + } + void set_console_utf8() { + if (setlocale(LC_ALL, "") != nullptr) { + return; + } + if (setlocale(LC_ALL, "C.UTF-8") != nullptr) { + return; + } + (void) setlocale(LC_ALL, "en_US.UTF-8"); + } + + PSC_DEFINE_TRIPLE_API_FROM_BOOL(void *, load_library, PSC_LOAD_LIBRARY_PARAMS) + + bool load_library_ec(std::string_view library_path, void *&out, + std::error_code &ec) noexcept { + out = nullptr; + ec.clear(); + (void) dlerror(); + void *library = dlopen(library_path.data(), RTLD_LAZY); + if (!library) { + const char *err = dlerror(); + std::cerr << library_path + << " Failed to load library: " << (err ? err : "unknown") << '\n'; + ec = std::make_error_code(std::errc::no_such_file_or_directory); + return false; + } + + out = library; + return true; + } + + void *load_library_fail_fast(std::string_view library_path) { + auto ret = try_load_library(library_path); + if (!ret) { + Psc::fail_fast(); + } + return ret.value(); + } + + void free_library(void *library) { + if (library) { + dlclose(library); + } + } + + PSC_DEFINE_TRIPLE_API_FROM_BOOL(void *, load_function, PSC_LOAD_FUNCTION_PARAMS) + + bool load_function_ec(void *library, std::string_view function_name, + void *&out, std::error_code &ec) noexcept { + out = nullptr; + ec.clear(); + + if (!library) { + std::cerr << function_name << " function not loaded (library == nullptr)\n"; + ec = std::make_error_code(std::errc::invalid_argument); + return false; + } + + (void) dlerror(); + void *func = dlsym(library, function_name.data()); + const char *err = dlerror(); + if (err != nullptr) { + std::cerr << "dlsym failed for " << function_name << " : " << err << '\n'; + ec = std::make_error_code(std::errc::function_not_supported); + return false; + } + + out = func; + return true; + } + + void *load_function_fail_fast(void *library, std::string_view function_name) { + auto ret = try_load_function(library, function_name); + if (!ret) { + Psc::fail_fast(); + } + return ret.value(); + } + + std::string get_error_message() { + const int ec = errno; + return "错误码:" + std::to_string(ec) + ": " + std::strerror(ec); + } + + std::string get_error_message(ERROR_CODE_TYPE ec) { + return "错误码:" + std::to_string(ec) + ": " + + std::strerror(static_cast(ec)); + } + + ERROR_CODE_TYPE get_error_code() { return errno; } + + void set_error_code(ERROR_CODE_TYPE code) { + errno = static_cast(code); + } + + std::string get_exe_path() { + std::vector buffer(PATH_MAX + 1, '\0'); + const ssize_t len = + readlink("/proc/self/exe", buffer.data(), buffer.size() - 1); + if (len == -1) { + return ""; + } + buffer[static_cast(len)] = '\0'; + return std::string(buffer.data()); + } + + std::string get_home_dir() { + if (const char *home = std::getenv("HOME")) { + return home; + } + + struct passwd *pw = getpwuid(getuid()); + if (pw && pw->pw_dir) { + return std::string(pw->pw_dir); + } + + return ""; + } + + bool set_thread_priority(void *thread_ptr, int priority, bool realtime) { + if (!thread_ptr) { + return false; + } + + pthread_t pthread = *static_cast(thread_ptr); + int policy = realtime ? SCHED_FIFO : SCHED_OTHER; + + sched_param param{}; + if (realtime) { + const int min_prio = sched_get_priority_min(policy); + const int max_prio = sched_get_priority_max(policy); + if (min_prio >= 0 && max_prio >= min_prio) { + priority = std::max(min_prio, std::min(max_prio, priority)); + } + param.sched_priority = priority; + } else { + param.sched_priority = 0; + } + + const int ret = pthread_setschedparam(pthread, policy, ¶m); + if (ret != 0) { + errno = ret; + return false; + } + return true; + } + + bool set_thread_priority(std::thread *thread, int priority, bool realtime) { + if (!thread) { + return false; + } + + pthread_t pthread = thread->native_handle(); + return set_thread_priority(static_cast(&pthread), priority, realtime); + } + + bool set_thread_affinity(void *thread_ptr, unsigned cpu_index) { + if (!thread_ptr) { + return false; + } + + if (cpu_index >= CPU_SETSIZE) { + errno = EINVAL; + return false; + } + + pthread_t pthread = *static_cast(thread_ptr); + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(cpu_index, &cpuset); + + const int ret = pthread_setaffinity_np(pthread, sizeof(cpu_set_t), &cpuset); + if (ret != 0) { + errno = ret; + std::cerr << "Error setting thread affinity: " << std::strerror(ret) + << std::endl; + return false; + } + + return true; + } + + bool set_thread_affinity(std::thread *thread, unsigned cpu_index) { + if (!thread) { + return false; + } + + pthread_t pthread = thread->native_handle(); + return set_thread_affinity(static_cast(&pthread), cpu_index); + } + + std::string get_memory_info() { + auto ret = JSON::object(); + ret.append({"VmRSS (MB)", read_proc_status_kb("VmRSS:") / 1024}); + ret.append({"VmSize (MB)", read_proc_status_kb("VmSize:") / 1024}); + ret.append({"VmPeak (MB)", read_proc_status_kb("VmPeak:") / 1024}); + ret.append({"VmHWM (MB)", read_proc_status_kb("VmHWM:") / 1024}); + return ret.to_json_string(); + } + + size_t get_system_memory() { + std::ifstream meminfo("/proc/meminfo"); + std::string line; + while (std::getline(meminfo, line)) { + if (line.rfind("MemTotal:", 0) == 0) { + size_t mem_total_kb = 0; + if (std::sscanf(line.c_str(), "MemTotal: %zu kB", &mem_total_kb) == 1) { + return mem_total_kb * 1024; + } + } + } + return 0; + } + + size_t get_process_memory() { return read_proc_status_kb("VmRSS:") * 1024; } } // namespace Psc #endif // defined(__linux__) diff --git a/psc_global_include/Bit.h b/psc_global_include/Bit.h index 100eab0..edef2d4 100644 --- a/psc_global_include/Bit.h +++ b/psc_global_include/Bit.h @@ -14,71 +14,74 @@ #undef min #undef max namespace Psc { - - - template + template typename std::enable_if, T>::type - create_string(size_t size, std::pmr::memory_resource* resource = nullptr) { - std::pmr::memory_resource* r = resource ? resource : std::pmr::get_default_resource(); - std::pmr::string ret(r); - ret.resize(size); - return ret; // 返回 std::pmr::string + create_string(size_t size, std::pmr::memory_resource *resource = nullptr) { + std::pmr::memory_resource *r = resource ? resource : std::pmr::get_default_resource(); + std::pmr::string ret(r); + ret.resize(size); + return ret; // 返回 std::pmr::string } - template + template std::enable_if_t, T> - create_string(size_t size, std::pmr::memory_resource* resource = nullptr) { + create_string(size_t size, std::pmr::memory_resource *resource = nullptr) { std::string ret; ret.resize(size); - return ret; // 返回 std::string + return ret; // 返回 std::string } - template + template struct cacl_type { using type = T; - static size_t size(T& a) { + + static size_t size(T &a) { return a.size(); } - static void* data(T& a) { + + static void *data(T &a) { return static_cast(a.data()); } - static size_t size(const T& a) { + + static size_t size(const T &a) { return a.size(); } - static void* data(const T& a) { - return (void*)a.data(); + + static void *data(const T &a) { + return (void *) a.data(); } }; - template <> + + template<> struct cacl_type { using type = std::string; - static size_t size(std::string_view a) {return a.size();} - static void* data(std::string_view a) {return (void*)a.data();} - }; - template <> - struct cacl_type { - using type = std::string; - static size_t size(const char* a) {return std::strlen(a);} - static void* data(const char* a) {return (void*)a;} + static size_t size(std::string_view a) { return a.size(); } + static void *data(std::string_view a) { return (void *) a.data(); } }; - template + template<> + struct cacl_type { + using type = std::string; + static size_t size(const char *a) { return std::strlen(a); } + static void *data(const char *a) { return (void *) a; } + }; + + template struct cacl_type { using type = std::string; static size_t size(const char (&a)[N]) { return N - 1; } - static void* data(const char (&a)[N]) { return (void*)a; } + static void *data(const char (&a)[N]) { return (void *) a; } }; - template + template struct cacl_type { using type = std::string; static size_t size(const char (&a)[N]) { return N - 1; } - static void* data(const char (&a)[N]) { return (void*)a; } + static void *data(const char (&a)[N]) { return (void *) a; } }; } - #define Base template , \ typename STR_Type = std::basic_string, allocator_type>, \ typename ret_type = typename cacl_type::type \ @@ -92,34 +95,40 @@ typename ret_type = typename cacl_type::type \ namespace Psc { // 检查当前机器是否是大端字节序 extern const bool is_big_endian; - void big_endian_2_platform(void* dest, const char* d, size_t size); - void little_endian_2_platform(void* dest, const char* d, size_t size); + + void big_endian_2_platform(void *dest, const char *d, size_t size); + + void little_endian_2_platform(void *dest, const char *d, size_t size); + void big_endian_2_platform(const std::string &big_endian, char *dest); - void big_endian_2_platform(const std::string& big_endian, char* dest); - void little_endian_2_platform(const std::string& little_endian, char* dest); + void little_endian_2_platform(const std::string &little_endian, char *dest); - std::string big_endian_2_platform(const std::string& big_endian); - std::string little_endian_2_platform(const std::string& little_endian); - std::string platform_2_big_endian(void* memory, int size); - std::string platform_2_little_endian(void* memory, int size); + std::string big_endian_2_platform(const std::string &big_endian); + std::string little_endian_2_platform(const std::string &little_endian); + std::string platform_2_big_endian(void *memory, int size); + + std::string platform_2_little_endian(void *memory, int size); inline size_t get_xor_hex_size(size_t n1, size_t n2) { return n1 > n2 ? n1 : n2; } - void xor_hex_Ex(void* dest, const char* s1, size_t n1, const char* s2, size_t n2, bool big_dian, bool is_upper); + + void xor_hex_Ex(void *dest, const char *s1, size_t n1, const char *s2, size_t n2, bool big_dian, bool is_upper); + Base - ret_type xorHex(const STR_Type& s1, const STR_Type& s2, std::pmr::memory_resource* resource = nullptr) { + ret_type xorHex(const STR_Type &s1, const STR_Type &s2, std::pmr::memory_resource *resource = nullptr) { auto n1 = cacl_type::size(s1); auto n2 = cacl_type::size(s2); auto size = get_xor_hex_size(n1, n2); auto ret = create_string(size, resource); - xor_hex_Ex((void*)ret.data(), (const char*)cacl_type::data(s1), n1, (const char*)cacl_type::data(s2), n2, true, true); + xor_hex_Ex((void *) ret.data(), (const char *) cacl_type::data(s1), n1, + (const char *) cacl_type::data(s2), n2, true, true); return ret; } @@ -127,122 +136,142 @@ namespace Psc { inline size_t get_xor_bin_size(size_t n1, size_t n2) { return n1 > n2 ? n1 : n2; } - void xor_bin_Ex(void* dest, const char* s1, size_t n1, const char* s2, size_t n2, bool big_dian); + + void xor_bin_Ex(void *dest, const char *s1, size_t n1, const char *s2, size_t n2, bool big_dian); + Base - ret_type xorBin(const STR_Type& s1, const STR_Type& s2, std::pmr::memory_resource* resource = nullptr) { + ret_type xorBin(const STR_Type &s1, const STR_Type &s2, std::pmr::memory_resource *resource = nullptr) { auto n1 = cacl_type::size(s1); auto n2 = cacl_type::size(s2); auto size = get_xor_bin_size(n1, n2); auto ret = create_string(size, resource); - xor_bin_Ex((void*)ret.data(), (const char*)cacl_type::data(s1), n1, (const char*)cacl_type::data(s2), n2, true); + xor_bin_Ex((void *) ret.data(), (const char *) cacl_type::data(s1), n1, + (const char *) cacl_type::data(s2), n2, true); return ret; } + inline size_t get_bin2hex_size(size_t num) { return (num + 3) / 4; } + void bin2hex_Ex(void *dest, const char *data, size_t size, bool big_dian, bool isupper); - inline size_t get_bin2hex_size(size_t num) {return (num + 3) / 4;} - void bin2hex_Ex(void* dest, const char* data, size_t size, bool big_dian, bool isupper); Base - ret_type bin2hex(const STR_Type& mem, std::pmr::memory_resource* resource = nullptr) { + ret_type bin2hex(const STR_Type &mem, std::pmr::memory_resource *resource = nullptr) { auto size = cacl_type::size(mem); auto ret = create_string(get_bin2hex_size(size), resource); - bin2hex_Ex((void*)ret.data(), (const char*)cacl_type::data(mem), size, true, true); + bin2hex_Ex((void *) ret.data(), (const char *) cacl_type::data(mem), size, true, true); return ret; } + inline size_t get_hex2bin_size(size_t num) { return num * 4; } + + void hex2bin_Ex(void *dest, const char *data, size_t size, bool big_dian); - inline size_t get_hex2bin_size(size_t num) {return num * 4;} - void hex2bin_Ex(void* dest, const char* data, size_t size, bool big_dian); Base - ret_type hex2bin(const STR_Type& mem, std::pmr::memory_resource* resource = nullptr) { + ret_type hex2bin(const STR_Type &mem, std::pmr::memory_resource *resource = nullptr) { auto size = cacl_type::size(mem); auto ret = create_string(get_hex2bin_size(size), resource); - hex2bin_Ex((void*)ret.data(), (const char*)cacl_type::data(mem), size, true); + hex2bin_Ex((void *) ret.data(), (const char *) cacl_type::data(mem), size, true); return ret; } - inline size_t get_bin2mem_size(size_t num) {return num/8;}; - void bin2mem(void* dest, const char* data, size_t size); + inline size_t get_bin2mem_size(size_t num) { return num / 8; }; + + void bin2mem(void *dest, const char *data, size_t size); + Base - ret_type bin2mem(const STR_Type& mem, std::pmr::memory_resource* resource = nullptr) { + ret_type bin2mem(const STR_Type &mem, std::pmr::memory_resource *resource = nullptr) { auto size = cacl_type::size(mem); auto ret = create_string(get_bin2mem_size(size), resource); - bin2mem((void*)ret.data(), (const char*)cacl_type::data(mem), size); + bin2mem((void *) ret.data(), (const char *) cacl_type::data(mem), size); return ret; } - inline size_t get_hex2mem_size(size_t hex_hum) {return hex_hum/2;}; - void hex2mem_Ex(void* dest, const char* data, size_t size); + inline size_t get_hex2mem_size(size_t hex_hum) { return hex_hum / 2; }; + + void hex2mem_Ex(void *dest, const char *data, size_t size); + Base - STR_Type hex2mem(const STR_Type& mem, std::pmr::memory_resource* resource = nullptr) { + ret_type hex2mem(const STR_Type &mem, std::pmr::memory_resource *resource = nullptr) { auto size = cacl_type::size(mem); - auto ret = create_string(get_hex2mem_size(size), resource); - hex2mem_Ex((void*)ret.data(), (const char*)cacl_type::data(mem), size); + auto ret = create_string(get_hex2mem_size(size), resource); + hex2mem_Ex((void *) ret.data(), (const char *) cacl_type::data(mem), size); return ret; } - inline size_t get_mem2hex_size(size_t num, size_t middle_len) { return num * 2 + (num - 1) * middle_len;} - void mem2hex_Ex2(void* dest, void* mem, size_t num, bool uppercase = true, const char* middle = "", size_t middle_len = std::numeric_limits::max()); + inline size_t get_mem2hex_size(size_t num, size_t middle_len) { return num * 2 + (num - 1) * middle_len; } + + void mem2hex_Ex2(void *dest, void *mem, size_t num, bool uppercase = true, const char *middle = "", + size_t middle_len = std::numeric_limits::max()); + Base - STR_Type mem2hex(const STR_Type& mem, bool uppercase = true, const std::string& middle = "", std::pmr::memory_resource* resource = nullptr) { + ret_type mem2hex(const STR_Type &mem, bool uppercase = true, const std::string &middle = "", + std::pmr::memory_resource *resource = nullptr) { auto size = cacl_type::size(mem); - auto ret = create_string(get_mem2hex_size(size, middle.size()), resource); + auto ret = create_string(get_mem2hex_size(size, middle.size()), resource); mem2hex_Ex2(ret.data(), cacl_type::data(mem), size, uppercase, middle.data(), middle.size()); return ret; } - - - template - void get_bin(std::string& msg, int& n) { + template + void get_bin(std::string &msg, int &n) { n = std::bitset(msg.substr(start, len)).to_ullong(); } - template - void get_bin(std::string& msg, Enum& n) { + + template + void get_bin(std::string &msg, Enum &n) { n = static_cast(std::bitset(msg.substr(start, len)).to_ullong()); } - template - Enum get_bin(std::string& msg) { + + template + Enum get_bin(std::string &msg) { return static_cast(std::bitset(msg.substr(start, len)).to_ullong()); } - template - int get_bin(const std::string& msg) { + + template + int get_bin(const std::string &msg) { return std::bitset(msg.substr(start, len)).to_ullong(); } - template - void set_bin(std::string& msg, unsigned long long n) { + + template + void set_bin(std::string &msg, unsigned long long n) { msg.replace(start, len, std::bitset(n).to_string()); } - template - void set_bin(std::string& msg, const std::string& value) { + + template + void set_bin(std::string &msg, const std::string &value) { if (value.size() != len) { std::cout << "len == " << len << " bits.size() == " << value.size() << std::endl; throw std::out_of_range("Start position and length exceed bit string size"); } msg.replace(start, len, value); } - template + + template std::string to_bin(unsigned long long n) { return std::bitset(n).to_string(); } - template + + template std::string to_bin(double n) { return std::bitset(static_cast(n)).to_string(); } - template + + template T bin2(std::string_view binstr) { auto text = std::string(binstr); return std::stoull(text, nullptr, 2); } - template + + template unsigned long long hex2(std::string_view hexstr) { auto text = std::string(hexstr); return std::stoull(text, nullptr, 16); } - template <> + + template<> inline int bin2(std::string_view binstr) { int bit_limit = sizeof(int) * 8; if (binstr.size() > bit_limit) { @@ -254,9 +283,9 @@ namespace Psc { // 设置dest的从off位起n位,写入value的低n位 - template - void set_bits(void* dest, size_t off, size_t n, T value) { - auto d = static_cast(dest); + template + void set_bits(void *dest, size_t off, size_t n, T value) { + auto d = static_cast(dest); size_t bit_pos = off; size_t val_pos = 0; while (n > 0) { @@ -276,15 +305,18 @@ namespace Psc { } - // 大批量位处理性能不好 但是通常也没有这样的场景 - void bit_move(void* d, size_t doff, const void* s, size_t soff, size_t n); - void bit_xor(void* d, size_t doff, const void* s, size_t soff, size_t n); - void bit_or(void* d, size_t doff, const void* s, size_t soff, size_t n); - void bit_and(void* d, size_t doff, const void* s, size_t soff, size_t n); - template - T get_bits(void* src, size_t off, size_t n) { - auto s = static_cast(src); + void bit_move(void *d, size_t doff, const void *s, size_t soff, size_t n); + + void bit_xor(void *d, size_t doff, const void *s, size_t soff, size_t n); + + void bit_or(void *d, size_t doff, const void *s, size_t soff, size_t n); + + void bit_and(void *d, size_t doff, const void *s, size_t soff, size_t n); + + template + T get_bits(void *src, size_t off, size_t n) { + auto s = static_cast(src); size_t bit_pos = off; size_t val_pos = 0; T result = 0; @@ -304,4 +336,3 @@ namespace Psc { } #undef Base -