连接器客户端

This commit is contained in:
tianmo
2026-09-05 23:35:45 +08:00
parent d10807d877
commit 41151b7f8b
28 changed files with 2195 additions and 12 deletions
+318
View File
@@ -0,0 +1,318 @@
#include "json.hpp"
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <limits>
#include <sstream>
#include <stdexcept>
namespace connector {
namespace {
void append_utf8(std::string& out, unsigned codepoint) {
if (codepoint <= 0x7f) {
out.push_back(static_cast<char>(codepoint));
} else if (codepoint <= 0x7ff) {
out.push_back(static_cast<char>(0xc0 | (codepoint >> 6)));
out.push_back(static_cast<char>(0x80 | (codepoint & 0x3f)));
} else if (codepoint <= 0xffff) {
out.push_back(static_cast<char>(0xe0 | (codepoint >> 12)));
out.push_back(static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
out.push_back(static_cast<char>(0x80 | (codepoint & 0x3f)));
} else {
out.push_back(static_cast<char>(0xf0 | (codepoint >> 18)));
out.push_back(static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)));
out.push_back(static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
out.push_back(static_cast<char>(0x80 | (codepoint & 0x3f)));
}
}
class Parser {
public:
explicit Parser(const std::string& text) : text_(text) {}
Json parse() {
skip_space();
Json result = value();
skip_space();
if (position_ != text_.size()) fail("JSON 末尾存在多余内容");
return result;
}
private:
const std::string& text_;
std::size_t position_ = 0;
[[noreturn]] void fail(const std::string& message) const {
throw std::runtime_error(message + "(位置 " + std::to_string(position_) + "");
}
void skip_space() {
while (position_ < text_.size()) {
const char c = text_[position_];
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') break;
++position_;
}
}
bool consume(char expected) {
skip_space();
if (position_ >= text_.size() || text_[position_] != expected) return false;
++position_;
return true;
}
void literal(const char* expected) {
while (*expected) {
if (position_ >= text_.size() || text_[position_++] != *expected++) fail("JSON 字面量无效");
}
}
unsigned hex4() {
unsigned value = 0;
for (int index = 0; index < 4; ++index) {
if (position_ >= text_.size()) fail("JSON Unicode 转义不完整");
const char c = text_[position_++];
value <<= 4;
if (c >= '0' && c <= '9') value |= static_cast<unsigned>(c - '0');
else if (c >= 'a' && c <= 'f') value |= static_cast<unsigned>(c - 'a' + 10);
else if (c >= 'A' && c <= 'F') value |= static_cast<unsigned>(c - 'A' + 10);
else fail("JSON Unicode 转义无效");
}
return value;
}
std::string string_value() {
if (!consume('"')) fail("JSON 字符串缺少引号");
std::string out;
while (position_ < text_.size()) {
const char c = text_[position_++];
if (c == '"') return out;
if (static_cast<unsigned char>(c) < 0x20) fail("JSON 字符串包含控制字符");
if (c != '\\') {
out.push_back(c);
continue;
}
if (position_ >= text_.size()) fail("JSON 转义不完整");
switch (text_[position_++]) {
case '"': out.push_back('"'); break;
case '\\': out.push_back('\\'); break;
case '/': out.push_back('/'); break;
case 'b': out.push_back('\b'); break;
case 'f': out.push_back('\f'); break;
case 'n': out.push_back('\n'); break;
case 'r': out.push_back('\r'); break;
case 't': out.push_back('\t'); break;
case 'u': {
unsigned codepoint = hex4();
if (codepoint >= 0xd800 && codepoint <= 0xdbff) {
if (position_ + 2 > text_.size() || text_[position_++] != '\\' || text_[position_++] != 'u') {
fail("JSON Unicode 代理对无效");
}
const unsigned low = hex4();
if (low < 0xdc00 || low > 0xdfff) fail("JSON Unicode 代理对无效");
codepoint = 0x10000 + ((codepoint - 0xd800) << 10) + (low - 0xdc00);
}
append_utf8(out, codepoint);
break;
}
default: fail("JSON 转义字符无效");
}
}
fail("JSON 字符串未结束");
}
Json number_value() {
const std::size_t start = position_;
if (position_ < text_.size() && text_[position_] == '-') ++position_;
if (position_ >= text_.size()) fail("JSON 数字无效");
if (text_[position_] == '0') {
++position_;
} else {
if (text_[position_] < '1' || text_[position_] > '9') fail("JSON 数字无效");
while (position_ < text_.size() && text_[position_] >= '0' && text_[position_] <= '9') ++position_;
}
if (position_ < text_.size() && text_[position_] == '.') {
++position_;
if (position_ >= text_.size() || text_[position_] < '0' || text_[position_] > '9') fail("JSON 小数无效");
while (position_ < text_.size() && text_[position_] >= '0' && text_[position_] <= '9') ++position_;
}
if (position_ < text_.size() && (text_[position_] == 'e' || text_[position_] == 'E')) {
++position_;
if (position_ < text_.size() && (text_[position_] == '+' || text_[position_] == '-')) ++position_;
if (position_ >= text_.size() || text_[position_] < '0' || text_[position_] > '9') fail("JSON 指数无效");
while (position_ < text_.size() && text_[position_] >= '0' && text_[position_] <= '9') ++position_;
}
const std::string token = text_.substr(start, position_ - start);
char* end = nullptr;
const double parsed = std::strtod(token.c_str(), &end);
if (!end || *end || !std::isfinite(parsed)) fail("JSON 数字超出范围");
return Json(parsed);
}
Json array_value() {
consume('[');
Json::Array values;
if (consume(']')) return Json(std::move(values));
while (true) {
values.push_back(value());
if (consume(']')) break;
if (!consume(',')) fail("JSON 数组缺少逗号");
}
return Json(std::move(values));
}
Json object_value() {
consume('{');
Json::Object values;
if (consume('}')) return Json(std::move(values));
while (true) {
skip_space();
if (position_ >= text_.size() || text_[position_] != '"') fail("JSON 对象键名无效");
const std::string key = string_value();
if (!consume(':')) fail("JSON 对象缺少冒号");
values[key] = value();
if (consume('}')) break;
if (!consume(',')) fail("JSON 对象缺少逗号");
}
return Json(std::move(values));
}
Json value() {
skip_space();
if (position_ >= text_.size()) fail("JSON 内容不完整");
switch (text_[position_]) {
case 'n': literal("null"); return Json();
case 't': literal("true"); return Json(true);
case 'f': literal("false"); return Json(false);
case '"': return Json(string_value());
case '[': return array_value();
case '{': return object_value();
default: return number_value();
}
}
};
std::string indent_text(int indent, int depth) {
return indent > 0 ? std::string(static_cast<std::size_t>(indent * depth), ' ') : std::string();
}
void dump_string(std::ostringstream& out, const std::string& value) {
out << '"';
for (const unsigned char c : value) {
switch (c) {
case '"': out << "\\\""; break;
case '\\': out << "\\\\"; break;
case '\b': out << "\\b"; break;
case '\f': out << "\\f"; break;
case '\n': out << "\\n"; break;
case '\r': out << "\\r"; break;
case '\t': out << "\\t"; break;
default:
if (c < 0x20) {
out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(c)
<< std::dec << std::setfill(' ');
} else {
out << static_cast<char>(c);
}
}
}
out << '"';
}
void dump_value(std::ostringstream& out, const Json& value, int indent, int depth) {
if (value.is_null()) {
out << "null";
} else if (value.is_bool()) {
out << (value.boolean() ? "true" : "false");
} else if (value.is_number()) {
out << std::setprecision(std::numeric_limits<double>::max_digits10) << value.number();
} else if (value.is_string()) {
dump_string(out, value.string());
} else if (value.is_array()) {
out << '[';
const auto& values = value.array();
for (std::size_t index = 0; index < values.size(); ++index) {
if (index) out << ',';
if (indent > 0) out << '\n' << indent_text(indent, depth + 1);
dump_value(out, values[index], indent, depth + 1);
}
if (indent > 0 && !values.empty()) out << '\n' << indent_text(indent, depth);
out << ']';
} else {
out << '{';
const auto& values = value.object();
std::size_t index = 0;
for (const auto& [key, child] : values) {
if (index++) out << ',';
if (indent > 0) out << '\n' << indent_text(indent, depth + 1);
dump_string(out, key);
out << (indent > 0 ? ": " : ":");
dump_value(out, child, indent, depth + 1);
}
if (indent > 0 && !values.empty()) out << '\n' << indent_text(indent, depth);
out << '}';
}
}
} // namespace
Json::Json() : value_(nullptr) {}
Json::Json(std::nullptr_t) : value_(nullptr) {}
Json::Json(bool value) : value_(value) {}
Json::Json(double value) : value_(value) {}
Json::Json(const char* value) : value_(std::string(value ? value : "")) {}
Json::Json(std::string value) : value_(std::move(value)) {}
Json::Json(Array value) : value_(std::move(value)) {}
Json::Json(Object value) : value_(std::move(value)) {}
Json Json::parse(const std::string& text) { return Parser(text).parse(); }
std::string Json::dump(int indent) const {
std::ostringstream out;
dump_value(out, *this, indent, 0);
return out.str();
}
bool Json::is_null() const { return std::holds_alternative<std::nullptr_t>(value_); }
bool Json::is_bool() const { return std::holds_alternative<bool>(value_); }
bool Json::is_number() const { return std::holds_alternative<double>(value_); }
bool Json::is_string() const { return std::holds_alternative<std::string>(value_); }
bool Json::is_array() const { return std::holds_alternative<Array>(value_); }
bool Json::is_object() const { return std::holds_alternative<Object>(value_); }
bool Json::boolean(bool fallback) const { return is_bool() ? std::get<bool>(value_) : fallback; }
double Json::number(double fallback) const { return is_number() ? std::get<double>(value_) : fallback; }
const std::string& Json::string() const {
static const std::string empty;
return is_string() ? std::get<std::string>(value_) : empty;
}
const Json::Array& Json::array() const {
static const Array empty;
return is_array() ? std::get<Array>(value_) : empty;
}
Json::Array& Json::array() {
if (!is_array()) value_ = Array{};
return std::get<Array>(value_);
}
const Json::Object& Json::object() const {
static const Object empty;
return is_object() ? std::get<Object>(value_) : empty;
}
Json::Object& Json::object() {
if (!is_object()) value_ = Object{};
return std::get<Object>(value_);
}
const Json* Json::find(const std::string& key) const {
if (!is_object()) return nullptr;
const auto found = object().find(key);
return found == object().end() ? nullptr : &found->second;
}
Json* Json::find(const std::string& key) {
if (!is_object()) return nullptr;
const auto found = object().find(key);
return found == object().end() ? nullptr : &found->second;
}
Json& Json::operator[](const std::string& key) { return object()[key]; }
} // namespace connector