[{"content":"What is Overplus Overplus is a lightweight, high-performance proxy server written in C++17, supporting SOCKS5, HTTPS, and Trojan protocols.\nHighlights:\nBlazing fast: 146 concurrent connections use only 25MB memory, CPU \u0026lt; 1% Zero dependencies: Statically compiled, no runtime libraries needed Windows GUI client: Works out of the box, one-click connect One-click install: Single command to set up the server Prerequisites You need:\nA VPS outside your region (Ubuntu/Debian recommended, 2 cores / 2GB RAM is enough) A domain name (optional, for certificate issuance) SSH access to your VPS Step 1: Server Installation Option A: One-Click Install (Recommended) SSH into your VPS and run:\ncurl -O https://raw.githubusercontent.com/xyanrch1024/overplus/master/install.sh \u0026amp;\u0026amp; chmod +x install.sh \u0026amp;\u0026amp; sudo ./install.sh The script will guide you through:\nSet a password: Your proxy connection password Choose a port: Default is 443 (recommended — disguises traffic as normal HTTPS) After installation, the script automatically:\nGenerates a TLS certificate (valid for 10 years) Downloads the Overplus binary Configures and starts a systemd service Verify the service is running:\nsystemctl status overplus If you see active (running), you\u0026rsquo;re all set.\nOption B: Manual Install If you prefer manual setup:\n# 1. Download wget https://github.com/xyanrch1024/overplus/releases/latest/download/overplus-linux-x86_64.zip unzip overplus-linux-x86_64.zip -d /tmp/overplus # 2. Install binary cp /tmp/overplus/overplus /usr/bin/overplus chmod +x /usr/bin/overplus # 3. Generate TLS certificate (self-signed example) mkdir -p /etc/overplus openssl req -x509 -nodes -newkey ec:\u0026lt;(openssl ecparam -name prime256v1) \\ -keyout /etc/overplus/server.key \\ -out /etc/overplus/server.crt \\ -subj \u0026#34;/CN=your_server_ip\u0026#34; -days 3650 # 4. Create config file cat \u0026gt; /etc/overplus/server.json \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; { \u0026#34;run_type\u0026#34;: \u0026#34;server\u0026#34;, \u0026#34;local_addr\u0026#34;: \u0026#34;0.0.0.0\u0026#34;, \u0026#34;local_port\u0026#34;: \u0026#34;443\u0026#34;, \u0026#34;allowed_passwords\u0026#34;: [\u0026#34;your_password_here\u0026#34;], \u0026#34;log_level\u0026#34;: \u0026#34;NOTICE\u0026#34;, \u0026#34;log_dir\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;ssl\u0026#34;: { \u0026#34;cert\u0026#34;: \u0026#34;/etc/overplus/server.crt\u0026#34;, \u0026#34;key\u0026#34;: \u0026#34;/etc/overplus/server.key\u0026#34; }, \u0026#34;websocketEnabled\u0026#34;: false, \u0026#34;dns_cache_ttl\u0026#34;: 600, \u0026#34;dns_cleanup_interval\u0026#34;: 600 } EOF # 5. Create systemd service cat \u0026gt; /etc/systemd/system/overplus.service \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; [Unit] Description=overplus proxy After=network.target [Service] User=root ExecStart=/usr/bin/overplus -c /etc/overplus/server.json Restart=on-failure RestartSec=10s LimitNOFILE=infinity [Install] WantedBy=multi-user.target EOF # 6. Start service systemctl daemon-reload systemctl start overplus systemctl enable overplus Enable BBR (Highly Recommended) BBR significantly improves TCP throughput:\necho \u0026#34;net.core.default_qdisc=fq\u0026#34; \u0026gt;\u0026gt; /etc/sysctl.conf echo \u0026#34;net.ipv4.tcp_congestion_control=bbr\u0026#34; \u0026gt;\u0026gt; /etc/sysctl.conf sysctl -p Verify it\u0026rsquo;s active:\nsysctl net.ipv4.tcp_congestion_control # Output: net.ipv4.tcp_congestion_control = bbr Step 2: Windows Client Download Go to the Release page and download overplus-client-windows-x64.zip. Extract to any directory.\nSetup After extraction, you\u0026rsquo;ll see:\noverplus_client.exe # Main program Qt5Core.dll # Qt runtime Qt5Gui.dll Qt5Widgets.dll libcrypto-3-x64.dll # OpenSSL libssl-3-x64.dll plugins/ # Qt plugins Double-click overplus_client.exe to launch the GUI client:\nEnter your VPS IP address in the Host Name field Enter the port (default 443) Enter your password Click SAVE to store settings Click CONNECT to connect When the status bar shows CONNECTED and the tray icon turns green, you\u0026rsquo;re connected.\nSystem Proxy Once connected, Overplus automatically sets the Windows system SOCKS proxy. It restores the original settings when you close the program. You can also configure manually:\nOpen Windows Settings → Network \u0026amp; Internet → Proxy Manual proxy setup: 127.0.0.1, port 1080 Alternative Clients Overplus is compatible with the Trojan protocol. You can use any Trojan-compatible client:\nClient Platform Download Clash Windows/Mac/Linux GitHub Nekoray Windows/Mac/Linux GitHub Shadowrocket iOS App Store Quantumult X iOS App Store Example configuration (Clash):\nproxies: - name: \u0026#34;Overplus\u0026#34; type: trojan server: your_server_ip port: 443 password: your_password sni: your_server_ip skip-cert-verify: true Step 3: Verify Check Server Logs # View Overplus logs journalctl -u overplus -f # Or check log files directly cat /var/log/overplus.*.log Normal connection output looks like:\naccept incoming connection :1.2.3.4 connected to example.com:443 session destroyed Test Your Connection Visit https://ipinfo.io to confirm your IP has changed to your VPS IP.\nTroubleshooting Can\u0026rsquo;t Connect? Check firewall: Make sure port 443 is open on your VPS\n# Ubuntu/Debian ufw allow 443/tcp # Or disable firewall ufw disable Check service status:\nsystemctl status overplus journalctl -u overplus -n 20 Check port conflict: Port 443 must not be used by other services (nginx, apache, etc.)\nSlow Speed? Make sure BBR is enabled Try a different VPS datacenter/region Check your VPS bandwidth and traffic quota How to Change Password? Edit the server config:\nnano /etc/overplus/server.json # Modify the allowed_passwords field systemctl restart overplus Benchmarks Tested on 2-core Xeon Skylake / 2GB RAM / Ubuntu 24.04:\nMetric Value Concurrent connections 146 Memory usage 25 MB Memory per connection ~170 KB CPU usage \u0026lt; 1% TLS reconnect \u0026lt; 1ms Links GitHub: https://github.com/xyanrch1024/overplus Releases: https://github.com/xyanrch1024/overplus/releases Telegram: https://t.me/+JfKOqh2wH25kMWFl ","permalink":"https://xyanrch1024.github.io/posts/overplus-getting-started/","summary":"Step-by-step guide to set up your own proxy server with Overplus. One-click install script + Windows GUI client, up and running in 5 minutes.","title":"Overplus Getting Started: Set Up Your Own Proxy in 5 Minutes"},{"content":"Overplus is a C++17 proxy server built on Boost.Asio, supporting SOCKS5, HTTPS, Trojan, and a custom V-Protocol. This post documents the full-stack performance optimization of its server component.\nSource: https://github.com/xyanrch1024/overplus.git\nBaseline Metric Value Active connections 27 Memory usage 5.6MB DNS resolution Every connection TLS handshake Full handshake every time Disconnect logs ERROR level, flooding Server: 2-core Intel Xeon Skylake, 2GB RAM, Ubuntu 24.04.\nOptimization 1: Global TCP DNS Cache The Problem Every TCP connection triggers a DNS resolution. For frequently visited domains (e.g., www.google.com), this is pure waste.\nWrong Approach: Per-Session Cache The first attempt stored DNS cache as a Session member variable:\n// Session.h — WRONG struct TcpDnsCacheEntry { tcp::endpoint endpoint; time_t expire_time; }; std::unordered_map\u0026lt;std::string, TcpDnsCacheEntry\u0026gt; tcp_dns_cache_; Looks reasonable, but Session is destroyed when the TCP connection ends — the cache is destroyed with it. Each Session only queries DNS once, writes the cache, then dies. Zero reuse.\nCorrect Approach: Global DnsCacheManager Singleton Extract the cache into a standalone singleton shared by all Sessions:\n// Shared/DnsCache.h class DnsCacheManager : private boost::noncopyable { public: struct TcpEntry { boost::asio::ip::tcp::endpoint endpoint; time_t expire_time; }; static DnsCacheManager\u0026amp; instance(); void set_default_ttl(time_t ttl); bool get_tcp(const std::string\u0026amp; key, boost::asio::ip::tcp::endpoint\u0026amp; ep); void put_tcp(const std::string\u0026amp; key, const boost::asio::ip::tcp::endpoint\u0026amp; ep); void cleanup_expired(); private: DnsCacheManager() = default; time_t default_ttl_ = 600; std::mutex mtx_; std::unordered_map\u0026lt;std::string, TcpEntry\u0026gt; tcp_cache_; }; Usage:\n// Session.cpp — do_resolve() std::string dns_key = remote_host + \u0026#34;:\u0026#34; + remote_port; tcp::endpoint cached_ep; if (DnsCacheManager::instance().get_tcp(dns_key, cached_ep)) { do_connect(cached_ep); // cache hit, skip DNS return; } // miss — resolve async, then cache resolver_.async_resolve(remote_host, remote_port, ...); Key design decisions:\nstd::mutex protects concurrent access (multi-threaded io_context) Lazy expiration: TTL checked on lookup, expired entries replaced on next query Active cleanup: steady_timer scans every 600 seconds, removing expired entries Configurable: TTL and cleanup interval read from server.json, no hardcoding Cleanup Timer // Service.cpp void Service::start_dns_cleanup_timer() { auto interval = ConfigManage::instance().server_cfg.dns_cleanup_interval; dns_cleanup_timer_.expires_after(std::chrono::seconds(interval)); dns_cleanup_timer_.async_wait([this](const boost::system::error_code\u0026amp; ec) { if (ec) return; DnsCacheManager::instance().cleanup_expired(); start_dns_cleanup_timer(); }); } Configuration { \u0026#34;dns_cache_ttl\u0026#34;: 600, \u0026#34;dns_cleanup_interval\u0026#34;: 600 } Defaults to 600 seconds (10 minutes) if not configured. Backward compatible.\nOptimization 2: SSL Session Reuse The Problem Every client connection requires a full TLS handshake: certificate exchange + key negotiation, taking 2-5ms. For clients that reconnect frequently (browsers, mobile apps), this is avoidable overhead.\nSolution Enable server-side session caching in the SSL context:\n// Service.cpp ctx.set_options( boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); SSL_CTX_set_session_cache_mode(ctx.native_handle(), SSL_SESS_CACHE_SERVER); How It Works Client connects for the first time → full TLS handshake → server caches session (session ID + key parameters) Client reconnects → sends session ID in ClientHello Server recognizes the ID → skips full handshake, restores encrypted channel (\u0026lt;1ms) OpenSSL defaults to caching 20,480 sessions — more than enough for our concurrency level No client-side changes required — TLS session reuse is a protocol standard. Browsers, curl, and overplus_client all support it by default.\nOptimization 3: Buffer Size Tuning The Problem Each Session allocates in_buf and out_buf for read/write. At 32KB, high-throughput scenarios trigger more frequent system calls.\nSolution // Session.h static constexpr size_t MAX_BUFF_SIZE = 64 * 1024; // 32KB → 64KB Larger buffers mean each async_read_some / async_write processes more data. TLS records max at 16KB, so a 64KB buffer can hold multiple TLS records per read, reducing encryption/decryption context switches.\nMemory cost: +64KB per Session. 146 connections ≈ 18MB — acceptable for a 2GB server.\nOptimization 4: Hot-Path Log Demotion The Problem Disconnect error logs (Connection reset by peer, stream truncated) are normal, expected behavior in production. They should not be ERROR level. Under high concurrency, these logs flood the output — and each write involves std::ostringstream allocation, string formatting, and file I/O.\nSolution Demote disconnect-related logs from ERROR_LOG to DEBUG_LOG:\nFile Line Change Server/Session.cpp:349 read from client ERROR → DEBUG Server/Session.cpp:367 read from downstream ERROR → DEBUG Server/Session.cpp:387 write to downstream ERROR → DEBUG Server/TlsSession.cpp:37 write to client (TCP) ERROR → DEBUG Server/TlsSession.cpp:55 write to client (UDP) NOTICE → DEBUG DEBUG_LOG macro evaluates to nothing when log level is above DEBUG — zero overhead:\n#define DEBUG_LOG \\ if (logger::get_log_level() \u0026lt;= L_DEBUG) \\ logger(__FILE__, __func__, __LINE__, L_DEBUG).stream() Results Metric Before After Change Active connections 27 146 +440% Memory usage 5.6MB 24.9MB +19.3MB (146×128KB buffers) CPU Normal Near zero — DNS resolution Every time Cached, direct connect DNS round-trip eliminated TLS handshake Full every time Session restore on reconnect 2-5ms saved ERROR logs Flooding with disconnects Zero Silent logs 25MB memory running 146 concurrent connections, CPU near zero.\nTakeaways Eliminate redundant work — DNS cache avoids repeated resolution, SSL session reuse avoids repeated handshakes Global vs local cache — per-session caches are useless in connection-per-session models; must be globally shared Thread safety — shared caches under multi-threaded io_context need std::mutex Configuration over hardcoding — TTL and cleanup intervals from JSON, tunable without recompilation Log discipline — disconnects are normal behavior; ERROR-level logs for them pollute output and hurt performance The biggest lesson: the first DNS cache implementation was wrong — stored per-Session, destroyed with it. Understand object lifetimes before choosing cache placement. That single insight drove the architecture from a useless local map to a proper global singleton.\n","permalink":"https://xyanrch1024.github.io/posts/overplus-optimization/","summary":"Full-stack performance optimization for Overplus (a C++17 proxy server): global TCP DNS cache, SSL session reuse, buffer tuning, and log level demotion — 146 concurrent connections at just 25MB memory.","title":"C++ Proxy Server Performance Optimization: From DNS Caching to TLS Session Reuse"},{"content":"When nothing goes right, the instinct is to avoid — but avoidance changes nothing.\nWhen you hit a wall, look within. Turn inward — that\u0026rsquo;s where the way out begins.\n","permalink":"https://xyanrch1024.github.io/posts/fan-qiu-zhu-ji/","summary":"\u003cp\u003eWhen nothing goes right, the instinct is to avoid — but avoidance changes nothing.\u003c/p\u003e\n\u003cp\u003eWhen you hit a wall, look within. Turn inward — that\u0026rsquo;s where the way out begins.\u003c/p\u003e","title":"When Nothing Goes Right, Look Within"},{"content":"十年尘埃十年雪\n百年树木仍长青\n曾记年少青云志\n不负韶光不负卿\n","permalink":"https://xyanrch1024.github.io/posts/random-thoughts/","summary":"十年尘埃十年雪，百年树木仍长青。曾记年少青云志，不负韶光不负卿。","title":"随想"},{"content":"The REPL (Read-Eval-Print Loop) is the gateway to any interactive language. It\u0026rsquo;s where beginners experiment, where developers debug snippets, and where the language feels alive. This post walks through the design of the REPL for kai, a Lua-like scripting language running on a stack-based VM.\nSource code: https://github.com/xyanrch1024/VM\nDesign Goals Multi-line input — functions, if/while/repeat blocks spanning multiple lines Expression auto-printing — bare expressions like 1 + 2 automatically print their result State persistence — variables declared in one line persist across subsequent lines Graceful error recovery — errors don\u0026rsquo;t crash the REPL; the user keeps typing Architecture: Data Flow stdin → line buffer → isCompleteInput()? ├── No → \u0026#34;\u0026gt;\u0026gt; \u0026#34; prompt → read more └── Yes → parseQuiet() test → try print() wrap → runSource(accumulated) The core functions live in main.cpp:\nFunction Role isCompleteInput(src) Bracket + keyword balance heuristic runSource(src, vm) Parse → compile → interpret Parser::parseQuiet() Parse without stderr output (silent test) Multi-line Input Detection isCompleteInput() This function scans text to determine syntactic completeness by checking three dimensions:\nBracket Balance Character-by-character scan tracking ( ), [ ], { } depths. Skips inside string literals (\u0026quot;...\u0026quot;) and line comments (--).\nKeyword Balance Converts to lowercase and scans for standalone keywords:\nOpening (increment) Closing (decrement) function, if, while, for, repeat, do end, until A keyword is \u0026ldquo;standalone\u0026rdquo; only when surrounded by non-alpha characters — so endless doesn\u0026rsquo;t falsely close a block.\nCompleteness Condition bool complete = (parenDepth \u0026lt;= 0) \u0026amp;\u0026amp; (bracketDepth \u0026lt;= 0) \u0026amp;\u0026amp; (braceDepth \u0026lt;= 0) \u0026amp;\u0026amp; (blockDepth \u0026lt;= 0); All depths must be ≤ 0. Negative values are allowed (a stray ) with no ( won\u0026rsquo;t fool the heuristic — though the real parser will catch it later).\nPrompt Display \u0026gt; 1 + 2 # Primary prompt for first line \u0026gt;\u0026gt; if x then # Secondary prompt for continuation lines \u0026gt;\u0026gt; print(x) \u0026gt;\u0026gt; end Expression Auto-printing The Problem In kai, pure expressions like 1 + 2 are valid parse trees but the compiler rejects them as statements with \u0026ldquo;expression has no effect\u0026rdquo;. In a REPL, users expect to type 1 + 2 and see 3 printed.\nDetection Algorithm When input is complete, the REPL runs a two-step test:\nTry parsing as-is (with parseQuiet()):\nIf it produces valid statements → use the original input Handles: local x = 10, print(\u0026quot;hi\u0026quot;), x = 5, if ... end If step 1 fails, try wrapping in print(...):\nIf print(original) parses cleanly → use the wrapped version Handles: 1+2, \u0026quot;hello\u0026quot;, x, f(3) — anything that\u0026rsquo;s an expression Fallback: if neither works, use the original input (the real error message will show).\nQuiet Parsing To avoid double-printing confusing errors during the test parse, we redirect stderr to /dev/null:\nstd::vector\u0026lt;Stmt*\u0026gt; Parser::parseQuiet() { FILE* oldStderr = stderr; FILE* devnull = fopen(\u0026#34;/dev/null\u0026#34;, \u0026#34;w\u0026#34;); if (devnull) stderr = devnull; auto result = parse(); if (devnull) { stderr = oldStderr; fclose(devnull); } return result; } Error messages are only shown to the user during the real compilation in runSource().\nState Persistence Strategy The REPL accumulates all entered source in a std::string accumulated. Each time the user submits a complete input, the entire accumulated source is recompiled and re-executed from scratch.\nLine 1: local x = 10 → accumulated = \u0026#34;local x = 10\u0026#34; Line 2: print(x) → accumulated = \u0026#34;local x = 10\\nprint(x)\u0026#34; → recompile \u0026amp; run everything This approach mirrors early BASIC REPLs and has a clear trade-off:\nPro Con Simple implementation All side effects re-executed on every new line No compiler changes needed print() output repeats as lines grow Locals naturally persist Performance degrades with session length For a learning/debugging environment, this simplicity is worth the cost. A future improvement could adopt incremental compilation with a persistent symbol table.\nREPL Loop Pseudocode accumulated = \u0026#34;\u0026#34; // persistent source string buffer = \u0026#34;\u0026#34; // current input being built inMultiLine = false loop: prompt = inMultiLine ? \u0026#34;\u0026gt;\u0026gt; \u0026#34; : \u0026#34;\u0026gt; \u0026#34; print(prompt) line = readLine(stdin) if EOF: if inMultiLine \u0026amp;\u0026amp; buffer not empty: runSource(buffer, vm) break if not inMultiLine \u0026amp;\u0026amp; line in {\u0026#34;exit\u0026#34;, \u0026#34;quit\u0026#34;}: break buffer += \u0026#34;\\n\u0026#34; + line if buffer is all whitespace: continue if isCompleteInput(buffer): toRun = \u0026#34;\u0026#34; // Try as-is parser = Parser(buffer) stmts = parser.parseQuiet() if stmts not empty and no error: toRun = buffer // Try wrapped in print() if toRun empty: parser = Parser(\u0026#34;print(\u0026#34; + buffer + \u0026#34;)\u0026#34;) stmts = parser.parseQuiet() if stmts not empty and no error: toRun = \u0026#34;print(\u0026#34; + buffer + \u0026#34;)\u0026#34; // Fallback if toRun empty: toRun = buffer accumulated += \u0026#34;\\n\u0026#34; + toRun runSource(accumulated, vm) buffer = \u0026#34;\u0026#34; inMultiLine = false else: inMultiLine = true Limitations \u0026amp; Future Work Current Limitations Limitation Cause Impact Side-effect re-execution State persistence recompiles all input each time print() output repeated on every new line Heuristic completeness check Text-based, not grammar-based False positives/negatives possible with unusual syntax No history No readline/libedit integration No up-arrow recall No tab completion No symbol table exposed to REPL Manual typing only Potential Improvements 1. Incremental compilation with persistent scope Keep the Compiler\u0026rsquo;s symbol table alive across lines. Each line compiles into a block appended to the same function. Requires significant changes to CompileState lifecycle.\n2. Readline support Link against libreadline or libedit for line editing, history, and tab completion. Replace std::getline() with readline().\n3. Grammar-aware completeness check Instead of the heuristic isCompleteInput(), use the actual parser with a special \u0026ldquo;expect more\u0026rdquo; error mode. If the parser fails with unexpected EOF, prompt for more input. Would require modifications to Lexer::next() and Parser::consume().\n4. Persistent REPL namespace Replace the accumulated-source approach with a real symbol table that persists in the VM. Each line becomes a separate function sharing the same top-level local scope, using a hidden table to store REPL-declared variables.\nSummary The kai REPL is a pragmatic, minimal design that balances implementation effort against user experience:\nMulti-line input via a heuristic balance checker Expression auto-printing via silent parse-and-wrap State persistence via full-source recompilation (simple but wasteful) Error recovery naturally handled by the loop structure It\u0026rsquo;s not a production-grade REPL — no readline, no tab completion, no incremental compilation — but it gets the job done for language experimentation. The full codebase, including the lexer, parser, compiler, and VM, is open source.\nFull source: https://github.com/xyanrch1024/VM\n","permalink":"https://xyanrch1024.github.io/posts/repl-design/","summary":"Designing an interactive Read-Eval-Print Loop for the kai scripting language — multi-line input, expression auto-printing, state persistence, and graceful error recovery.","title":"Building a REPL for a Scripting Language"},{"content":"The frontend is the first pass of any language implementation: turning source code into executable bytecode. This post summarizes the frontend of kai, a Lua-like scripting language for my stack-based VM.\nSource code: https://github.com/xyanrch1024/VM\nPipeline Source → Lexer → Token stream → Parser → AST → Compiler → Bytecode → VM Module Input Output Lines Lexer source string Token stream ~200 Parser Token stream AST ~600 Compiler AST Function (bytecode) ~400 Total ~1200 Plus AST definitions (~200 lines) and built-in functions (~100 lines) brings the full frontend to ~1500 lines of C++17.\nLanguage Design: Lua Simplified kai is a streamlined Lua dialect that keeps the core philosophy — simplicity, flexibility, embeddability.\nFeature Lua kai Returns Multiple Single Variables Global by default local only Metatable/Coroutine/goto Supported Not yet Generic for Supported Numeric for only EBNF Grammar (excerpt) program = { stat } stat = \u0026#39;local\u0026#39; name \u0026#39;=\u0026#39; expr | name \u0026#39;=\u0026#39; expr | \u0026#39;if\u0026#39; expr \u0026#39;then\u0026#39; block \u0026#39;end\u0026#39; | \u0026#39;while\u0026#39; expr \u0026#39;do\u0026#39; block \u0026#39;end\u0026#39; | \u0026#39;function\u0026#39; name \u0026#39;(\u0026#39; [ namelist ] \u0026#39;)\u0026#39; block \u0026#39;end\u0026#39; | \u0026#39;return\u0026#39; [ expr ] | functioncall expr = nil | true | false | NUMBER | STRING | functiondef | tableconstructor | prefixexpr | expr binop expr | unop expr Operator Precedence 1. () . [] -- scope/index 2. # - not -- unary 3. ^ -- exponent (right-assoc) 4. * / % -- multiplicative 5. + - -- additive 6. .. -- concatenation 7. \u0026lt; \u0026gt; \u0026lt;= \u0026gt;= == ~= -- comparison 8. and 9. or Lexer Hand-written finite state machine, scanning character by character.\nToken Lexer::nextToken() { skipWhitespace(); switch (*cur) { case \u0026#39;+\u0026#39;: cur++; return {TK_PLUS, line}; case \u0026#39;-\u0026#39;: if (peek() == \u0026#39;-\u0026#39;) { skipComment(); return nextToken(); } cur++; return {TK_MINUS, line}; case \u0026#39;\u0026#34;\u0026#39;: return readString(); default: if (isdigit(*cur)) return readNumber(); if (isalpha(*cur) || *cur == \u0026#39;_\u0026#39;) return readNameOrKeyword(); error(\u0026#34;unexpected symbol \u0026#39;%c\u0026#39;\u0026#34;, *cur); } } Keywords are resolved via a hash map lookup after reading an identifier.\nEdge cases handled: .. (concat), ... (varargs), == vs =, comment skipping.\nParser Recursive descent parser — every grammar rule maps to a function.\nExpr* Parser::parseExpr(int precedence) { Expr* expr = parsePrefix(); while (precedence \u0026lt; getPrecedence(current().type)) { TokenType op = current().type; advance(); Expr* rhs = parseExpr(getRightPrecedence(op)); expr = new BinaryExpr(expr, op, rhs); } return expr; } Left/right recursion is handled by a precedence table, avoiding deeply nested function calls.\nAST Definition Tagged unions in C++ — no virtual dispatch overhead.\nstruct Expr { ExprType type; int line; union { double numVal; // NUMBER const char* strVal; // STRING / NAME struct { TokenType op; Expr* rhs; } unary; // UNARY struct { TokenType op; Expr* l, *r; } bin; // BINARY struct { Expr* callee; vector\u0026lt;Expr*\u0026gt; args; } call; // CALL struct { Expr* obj; Expr* key; } index; // INDEX struct { vector\u0026lt;const char*\u0026gt; p; Stmt* b; } func; // FUNCDEF struct { vector\u0026lt;TableField\u0026gt; f; } table; // TABLE }; }; Same pattern for Stmt. This tagged union approach is more compact than polymorphic classes and dispatches with a simple switch.\nCompiler (AST → Bytecode) Two recursive functions: compileExpr and compileStmt.\nExpression Compilation Table Source Bytecode 42 OP_CONSTANT \u0026lt;int:42\u0026gt; x OP_LOAD \u0026lt;slot\u0026gt; a + b a b OP_ADD a and b a OP_JZ →end OP_POP b (short-circuit) f(x) x \u0026lt;funcIdx\u0026gt; OP_CALL 1 t[k] t k OP_GET_INDEX Short-Circuit Logic and and or compile with jump-based short-circuit evaluation:\na and b: \u0026lt;a\u0026gt; OP_JZ → end ; if a is false, skip b OP_POP \u0026lt;b\u0026gt; → end: a or b: \u0026lt;a\u0026gt; OP_JNZ → end ; if a is true, skip b OP_POP \u0026lt;b\u0026gt; → end: Scope Management The compiler maintains a scope stack:\nstruct Local { const char* name; int depth; int slot; }; vector\u0026lt;Scope\u0026gt; scopes; void pushScope() { scopes.push_back({scopeDepth++}); } void popScope() { for (auto\u0026amp; local : scopes.back().locals) emitByte(OP_POP); // clean up locals scopes.pop_back(); } Variable lookup (resolveLocal(name)) traverses the scope chain from innermost outward, returning the slot number.\nControl Flow Compilation if statement: condition → OP_JZ → branch body → OP_JMP → patch jumps.\nwhile loop: record loop start → condition → OP_JZ → body → OP_LOOP → patch.\nfor loop: desugared to while with internal limit/step variables.\nFunction Compilation function fact(n) if n \u0026lt;= 1 then return 1 end return n * fact(n - 1) end Compilation process:\nCreate new Function object Parameter n → slot 0 Compile body with recursive self-call Bytecode stored in independent Chunk Parent emits OP_CLOSURE \u0026lt;funcIdx\u0026gt; Bytecode Trace function fact(n) if n \u0026lt;= 1 then return 1 end return n * fact(n - 1) end print(fact(5)) main function:\nOP_CLOSURE 0 ; create closure (fact) OP_STORE_0 ; fact = closure OP_POP OP_CONSTANT 0 ; push 5 OP_CONSTANT 1 ; push funcIdx(0) OP_CALL 1 ; fact(5) OP_PRINTLN OP_HALT fact function:\nOP_LOAD_0 ; push n OP_CONSTANT 0 ; push 1 OP_LE ; n \u0026lt;= 1? OP_JZ → 7 ; skip return if false OP_CONSTANT 1 ; push 1 OP_RET OP_LOAD_0 ; push n OP_LOAD_0 ; push n OP_CONSTANT 2 ; push 1 OP_SUB ; n - 1 OP_CONSTANT 3 ; push funcIdx(1) OP_CALL 1 ; fact(n-1) OP_MUL ; n * fact(n-1) OP_RET VM Extensions The frontend required new instructions in the backend:\nOpcode Purpose OP_GET_INDEX t[k] indexed read OP_SET_INDEX t[k] = v indexed write OP_NEW_TABLE {} create table OP_CLOSURE Create closure OP_GET_UPVALUE Read upvalue OP_SET_UPVALUE Write upvalue Value types extended with TABLE and CLOSURE. Table implemented as unordered_map\u0026lt;Value, Value\u0026gt;.\nPhased Implementation Phase Content Lines Test 1 Lex + Parse + Expressions ~400 print(1 + 2 * 3) 2 Control flow + Variables ~300 if/while/for/short-circuit 3 Functions + Closures ~200 recursive factorial/fib 4 Table + Builtins ~200 table constructor/index Each phase has test files + expected output, verified by diff.\nTesting ./build/vm tests/arithmetic.kai \u0026gt; /tmp/out diff /tmp/out tests/arithmetic.expected Test directory:\ntests/ ├── arithmetic.kai ├── variables.kai ├── if.kai ├── while.kai ├── for.kai ├── recursion.kai ├── table.kai └── ... Summary This frontend covers the first steps of building a complete programming language:\nLexer: ~200 lines, hand-written state machine Parser: ~600 lines, recursive descent with precedence table Compiler: ~400 lines, AST → 40 instructions of bytecode Full frontend: ~1500 lines of C++17 Combined with the stack-based VM backend from the previous post, it can now run complete programs end-to-end.\nFull source: https://github.com/xyanrch1024/VM\n","permalink":"https://xyanrch1024.github.io/posts/kai-lang-frontend/","summary":"From lexer and parser to bytecode compiler — building a Lua-like language frontend for a stack-based VM.","title":"Building a Lua-like Language Frontend in C++"},{"content":"I recently built a stack-based virtual machine in C++ and wrote a detailed design document. This post is a summary of that document.\nSource code: https://github.com/xyanrch1024/VM\nWhy Stack-Based Two mainstream VM architectures:\nFeature Stack Register Instruction size Short (1-3 bytes) Longer Compiler backend Minimal Needs register allocation Interpretation Intuitive, fewer bugs ~30% fewer instructions JIT friendliness Harder Natural fit I chose stack-based because for pure interpretation, it\u0026rsquo;s simpler, easier to code-generate, and less error-prone.\nArchitecture The VM instance contains:\nOperand stack — vector\u0026lt;Value\u0026gt;, holds locals + temporaries Call stack — vector\u0026lt;CallFrame\u0026gt;, manages function calls String table — intern pool for O(1) string comparison Function table — all compiled functions PC / FP — program counter and frame pointer Value System Each Value is 8 bytes: 1-byte type tag + 7-byte payload:\n┌──────┬──────────────────────────────┐ │ type │ value │ │ 1B │ 7B │ ├──────┼──────────────────────────────┤ │ NIL │ padding │ │ BOOL │ bool │ │ INT │ int64_t │ │FLOAT │ double │ │STRING│ void* (→ string table) │ └──────┴──────────────────────────────┘ Type promotion: INT + FLOAT → FLOAT, string + does concatenation.\nInstruction Set (40 instructions) Each instruction: 1-byte opcode + variable-length operands.\nCategory Count Examples Constants 5 OP_CONSTANT, OP_NIL Stack ops 5 OP_DUP, OP_SWAP, OP_ROT Locals 10 OP_LOAD, OP_STORE_0~3 Arithmetic 7 OP_ADD, OP_SUB, OP_MUL Comparison 6 OP_EQ, OP_LT, OP_GE Bitwise 6 OP_BIT_AND, OP_SHL Logic 1 OP_NOT Control flow 4 OP_JMP, OP_JZ, OP_LOOP Function call 2 OP_CALL, OP_RET Other 4 OP_PRINT, OP_HALT Execution Model The operand stack has two regions:\n┌───────────────────┐ │ Locals │ [fp, fp+numLocals) │ slot 0,1,...,N │ accessed via LOAD/STORE ├───────────────────┤ │ Expression stack │ [fp+numLocals, sp] │ Temp values │ PUSH/POP operations └───────────────────┘ On function call: allocate locals → push CallFrame → execute → RET cleans up.\nMemory Management: String Interning Every string constant is interned on load:\nExists in hash map? → return existing pointer (O(1) compare) Not found? → create copy, add to table, return stable pointer Compilation Examples 1 + 2 × 3 bytecode:\nOP_CONSTANT 0 ; push 1 OP_CONSTANT 1 ; push 2 OP_CONSTANT 2 ; push 3 OP_MUL ; 2 × 3 = 6 OP_ADD ; 1 + 6 = 7 OP_PRINTLN ; print 7 OP_HALT Recursive factorial(5):\nSimilar to typical compiler IR — LOAD/STORE for locals, CALL/RET for function calls, LOOP for back-jumps.\nTest Coverage 13 tests covering: arithmetic, locals, conditionals, loops, recursion, floats, strings, comparisons, bitwise, stack ops, and double-recursion (fibonacci).\nFuture Optimizations Top-of-stack caching — cache TOS in a register Computed goto — replace switch with jump table Inline caching — inline frequently called functions JIT compilation — compile hot bytecode to native code Conclusion This design document describes a complete, working stack-based VM. The codebase is compact (C++17, 8 files) but covers the core paradigms of VM design. A good reference for anyone interested in compilers or language implementation.\nFull source: https://github.com/xyanrch1024/VM\n","permalink":"https://xyanrch1024.github.io/posts/stack-vm-design/","summary":"A deep dive into the design of a stack-based virtual machine, covering architecture, instruction set, execution model, and memory management.","title":"Building a Stack-Based VM: Design Walkthrough"},{"content":"I\u0026rsquo;m a programmer who loves coding.\nResume — full CV GitHub xyanrch — previous account (inaccessible, 2FA lost) xyanrch1024 — currently active account ","permalink":"https://xyanrch1024.github.io/about/","summary":"\u003cp\u003eI\u0026rsquo;m a programmer who loves coding.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/resume/\"\u003eResume\u003c/a\u003e — full CV\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"github\"\u003eGitHub\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/xyanrch\"\u003exyanrch\u003c/a\u003e — previous account (inaccessible, 2FA lost)\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/xyanrch1024\"\u003exyanrch1024\u003c/a\u003e — currently active account\u003c/li\u003e\n\u003c/ul\u003e","title":"About"},{"content":"yanrong cheng — yan********@gmail.com — 150****3410\nProfile Over nine years of C/C++ development experience. Proficient in STL, C++11/C++17, and modern C++ practices. Also experienced with scripting languages (Linux Shell, Python, Node.js). In-depth understanding and research of compiler frontend and backend, as well as virtual machine implementation. Also possess deep knowledge of operating system internals. Creator of overplus (170+ stars on GitHub), a widely adopted high-performance proxy server. Also actively building compilers, virtual machines, and infrastructure software as open-source projects. Passionate about open source; contributed patches to SerenityOS\u0026rsquo;s JavaScript engine. Education Donghua University 2013.9 - 2016.3 Shanghai\nMaster of Engineering in Software Engineering Research focus: Computer Graphics China University of Petroleum 2007.9 - 2011.12 Qingdao\nBachelor of Engineering in Computer Science Employment History Senior Software Engineer — Dell EMC 2021.6 - 2023.6 Working on virtualization storage for Dell Unity and PowerStore platforms. Primary languages: C++, Python, Bash, Perl Senior Software Engineer — Baidu 2020.9 - 2021.5 Core team of Baidu Input Method. Responsible for Baidu Input Method SDK development across multiple platforms (Android/iOS/Windows/macOS). Primary language: C++ Software Developer — CleNet Technologies 2016.5 - 2020.8 Consultant for Ericsson\u0026rsquo;s Packet Core team. Focused on OAM (Operations, Administration, and Maintenance), including EBM (Event-Based Monitoring), UE Trace, Configuration, and Command. Primary language: C++, with auxiliary scripting languages. Project Experience Dell EMC (Unity \u0026amp; PowerStore) 2021.6 - 2023.6 Worked on virtualization storage for Dell EMC\u0026rsquo;s Unity and PowerStore enterprise storage systems.\nUnity Login System Security Certificate Verification Implemented certificate validation and secure authentication mechanisms for the Unity storage system login process, ensuring compliance with enterprise security standards. PowerStore Control Layer Feature Development Developed control plane features for PowerStore, Dell EMC\u0026rsquo;s next-generation mid-range storage platform, including resource management, configuration services, and system orchestration. Baidu Input Method Baidu Input Method is a leading input method application with over 900 million users.\nBlacklist Feature Development 2021.3 - 2021.5 Cloud input sends user context to servers for processing. Implemented a blacklist dictionary to detect sensitive words and prevent transmission when matches are found. Collaborated with server-side teams on protobuf message design, wrote design documents, led code reviews, and implemented code and unit tests. Calculator Feature Development 2021.2 - 2021.3 Detects valid mathematical expressions in user input and evaluates results. Supports both binary and unary expressions. Authored design documents, participated in reviews, and implemented code and unit tests. User Trace Module \u0026amp; Wubi Module Refactoring 2020.10 - 2021.2 Added extensive unit tests and redesigned module architecture. For the User Trace module, reimplemented the encode/decode mechanism and redefined trace messages, improving code clarity and stability. Ericsson EPG (Ericsson Packet Gateway) 2016.5 - 2020.8 EPG (also known as PGW/SGW in the telecom domain) serves customers including Verizon and China Mobile, with industry-leading stability and performance.\nEBM and UE Trace Feature Development for Sx Interface The Sx interface is the standard interface between PGWC/SGWC/SMF and the User Plane, introduced in 5G. The protocol is PFCP (3GPP standard). Lead developer: wrote design documents, coordinated with Design Authority and Scrum Master, implemented code and unit tests, coordinated with Function Test and System Test teams, and resolved bugs. Successfully delivered on schedule. EPG Configuration Framework Adaptation for 5G CUPS (Control and User Plane Separation) in 5G required significant changes to EPG configuration. Adapted the legacy configuration framework to support the new architecture. Core developer: built a Python tool to auto-generate default configurations and a tool to split legacy configurations into control plane and user plane configurations. EBM Framework Performance Optimization Used profiling tools to analyze the EBM system, identified bottlenecks, and implemented optimizations. Achieved a 10% performance improvement, increasing session creation peak rates from 350k/s to 400k/s. UE Trace Feature Development for S2a/S2b Interfaces S2a/S2b are 3GPP standard interfaces using the GTPv2 protocol. Goal: trace and dump PFCP messages for given users, encode the content, and send to the EBM server. Collaborated with the European team, authored design proposals, participated in reviews, and implemented code and unit tests. Open-Source Projects Yan — https://github.com/xyanrch/Yan 2020 A pedagogical C compiler built from scratch in C++. Implements most C98 language features including expressions, control flow, functions, pointers, and structs, with generated x86 assembly output. Accompanied by a comprehensive test suite.\noverplus — https://github.com/xyanrch/overplus 2021 A high-performance proxy server implementing the Trojan protocol, written in C++17. Delivers lower latency, higher throughput, and stronger security guarantees compared to the reference implementation. A widely adopted open-source project with over 170 stars and 38 forks on GitHub. Supports TCP and WebSocket transport, with a Windows QT client built from scratch.\nCodis — https://github.com/xyanrch1024/Codis 2026 A C++20 AI coding agent with a client-server architecture. Supports multiple LLM providers (OpenAI, DeepSeek, GLM, Groq) with a config-driven provider registry. Features include a terminal TUI built with FTXUI, a Feishu bot integration via WebSocket, SQLite-based session persistence, a plugin system using C ABI dlopen, and one-command Docker deployment.\nkai VM — https://github.com/xyanrch1024/VM 2026 A stack-based bytecode virtual machine for kai, a Lua-like scripting language, written in C++17. Implements a full compilation pipeline: lexer → recursive-descent parser → AST → bytecode compiler → stack VM interpreter with 40 opcodes. Features include string interning, call frames with recursion support, and a REPL interface.\nTechnical Skills Languages: C/C++, Python, Node.js, Linux Shell, Perl Platforms: Linux, Windows Tools: Git, Kubernetes, Docker, Google Test, Protocol Buffers Methodologies: Agile Development Languages English: CET-6 certified; proficient in both verbal and written communication ","permalink":"https://xyanrch1024.github.io/resume/","summary":"\u003cp\u003eyanrong cheng — \u003ca href=\"mailto:yan********@gmail.com\"\u003eyan********@gmail.com\u003c/a\u003e — 150****3410\u003c/p\u003e\n\u003ch2 id=\"profile\"\u003eProfile\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eOver nine years of C/C++ development experience. Proficient in STL, C++11/C++17, and modern C++ practices. Also experienced with scripting languages (Linux Shell, Python, Node.js).\u003c/li\u003e\n\u003cli\u003eIn-depth understanding and research of compiler frontend and backend, as well as virtual machine implementation. Also possess deep knowledge of operating system internals.\u003c/li\u003e\n\u003cli\u003eCreator of \u003cstrong\u003eoverplus\u003c/strong\u003e (170+ stars on GitHub), a widely adopted high-performance proxy server. Also actively building compilers, virtual machines, and infrastructure software as open-source projects.\u003c/li\u003e\n\u003cli\u003ePassionate about open source; contributed patches to SerenityOS\u0026rsquo;s JavaScript engine.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"education\"\u003eEducation\u003c/h2\u003e\n\u003ch3 id=\"donghua-university-20139---20163\"\u003e\u003cstrong\u003eDonghua University\u003c/strong\u003e \u003ccode\u003e2013.9 - 2016.3\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003eShanghai\u003c/p\u003e","title":"Resume"},{"content":"A step-by-step guide to setting up a Hugo blog with automatic deployment to GitHub Pages using GitHub Actions.\nWhy Hugo + GitHub Pages Hugo: single binary, extremely fast build, Go ecosystem GitHub Pages: free hosting, HTTPS, custom domain support GitHub Actions: zero-cost CI/CD, push-to-deploy workflow 1. Initialize Hugo Site hugo new site blog --format yaml cd blog git init Add a theme as a git submodule:\ngit submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod Edit hugo.yaml:\nbaseURL: https://xyanrch1024.github.io languageCode: zh-cn title: xyanrch 的博客 theme: PaperMod 2. Two-Repo Strategy Repo Purpose You Touch blog Hugo source (markdown, theme, config) Yes — write articles here xyanrch1024.github.io Built static files (HTML/CSS/JS) No — managed by Actions GitHub Pages requires the repo name to be \u0026lt;username\u0026gt;.github.io to serve at https://username.github.io/.\n3. GitHub Actions Workflow Create .github/workflows/deploy.yml:\nname: Deploy to Pages on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: true - uses: peaceiris/actions-hugo@v3 - run: hugo --minify - uses: peaceiris/actions-gh-pages@v4 with: personal_token: ${{ secrets.DEPLOY_TOKEN }} publish_dir: ./public external_repository: xyanrch1024/xyanrch1024.github.io publish_branch: main Key points:\nsubmodules: true — needed for the PaperMod theme personal_token — required for cross-repo deployment; GITHUB_TOKEN cannot push to external repos external_repository — target repo where the built site lives 4. Personal Access Token GITHUB_TOKEN (auto-generated) can only push within the same repo. To deploy to xyanrch1024.github.io, create a classic PAT with repo scope:\nGo to https://github.com/settings/tokens Generate a classic token with repo scope Add it as a secret named DEPLOY_TOKEN in the blog repo: gh secret set DEPLOY_TOKEN --repo xyanrch1024/blog --body \u0026lt;your-token\u0026gt; 5. Enable GitHub Pages For user pages (\u0026lt;username\u0026gt;.github.io), GitHub Pages is auto-enabled but sometimes needs a push to trigger the first build. If the build gets stuck at building, push a dummy commit:\ngit clone git@github.com:xyanrch1024/xyanrch1024.github.io.git cd xyanrch1024.github.io echo \u0026#34;trigger\u0026#34; \u0026gt;\u0026gt; trigger.txt git add . \u0026amp;\u0026amp; git commit -m \u0026#34;trigger rebuild\u0026#34; \u0026amp;\u0026amp; git push After the build completes (status: built), the site goes live at https://xyanrch1024.github.io/.\n6. Writing Workflow # Create a new post hugo new posts/my-article.md # Preview locally hugo server -D # Deploy git add . \u0026amp;\u0026amp; git commit -m \u0026#34;new article: xxx\u0026#34; \u0026amp;\u0026amp; git push Push triggers the GitHub Action, which builds Hugo and deploys to Pages automatically.\nSummary Write → Push → Actions build → Pages live Local: hugo new + hugo server -D Git: git push origin main CI: Actions builds and deploys Hosting: GitHub Pages serves the static site Visit: https://xyanrch1024.github.io/\n","permalink":"https://xyanrch1024.github.io/posts/deploy-hugo-blog-to-github-pages/","summary":"A step-by-step guide to setting up a Hugo blog with automatic deployment to GitHub Pages using GitHub Actions.","title":"Deploy Hugo Blog to GitHub Pages"},{"content":"\u0026ldquo;As there is paradise in heaven, there are Suzhou and Hangzhou on earth.\u0026rdquo; Hangzhou is a city where natural beauty meets rich history.\nMust-Visit Attractions West Lake The soul of Hangzhou. Rent a bike or walk around the lake, passing Broken Bridge, Bai Causeway, and Su Causeway. The sunset view by the lake is unforgettable.\nLingyin Temple A千年 ancient temple nestled at the foot of Feilai Peak. The temple is bustling with worshippers, while the surrounding forest offers a peaceful retreat.\nLeifeng Pagoda Climb to the top for a panoramic view of West Lake. Legend says the story of the White Snake takes place here.\nHefang Street A well-preserved historical street from the late Qing Dynasty. Try local snacks and shop for traditional crafts.\nLocal Cuisine Dongpo Pork — rich but not greasy, melts in your mouth Longjing Shrimp — a perfect blend of tea aroma and fresh shrimp Pian\u0026rsquo;er Chuan — a local noodle soup, savory and comforting West Lake Vinegar Fish — sweet and sour, a signature Hangzhou dish Travel Tips Best season: Spring (Mar-May) and Autumn (Sep-Nov), mild weather Recommended stay: 2-3 days Transport: Metro covers major attractions; taxis are affordable Accommodation: Stay near West Lake or Wulin Square area ","permalink":"https://xyanrch1024.github.io/posts/hangzhou-travel-guide/","summary":"A travel guide to Hangzhou, covering West Lake, Lingyin Temple, and local cuisine.","title":"Hangzhou Travel Guide"},{"content":"My first blog post, built with Hugo + GitHub Pages.\nTech Stack Hugo - Static site generator PaperMod - Clean theme GitHub Pages - Free hosting GitHub Actions - Auto build \u0026amp; deploy ","permalink":"https://xyanrch1024.github.io/posts/my-first-post/","summary":"My first blog post powered by Hugo and GitHub Pages.","title":"Hello World"},{"content":"Page Not Found Back to Home\n","permalink":"https://xyanrch1024.github.io/404/","summary":"\u003ch2 id=\"page-not-found\"\u003ePage Not Found\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"/\"\u003eBack to Home\u003c/a\u003e\u003c/p\u003e\n\u003cscript src=\"https://volunteer.cdn-go.cn/404/latest/404.js\"\u003e\u003c/script\u003e","title":"404"}]