// net_text_codec_test — §1.10 fixed-length wire text: to_wire() / from_wire_str() // must apply a hard BYTE cap (like the stock client's strncpy) while never // splitting a UTF-8 multi-byte sequence. #include "../src/net/text_codec.h" #include #include #include static int g_fail = 0; #define CHECK(c, msg) \ do { \ if (!(c)) { \ std::fprintf(stderr, "FAIL: %s\n", msg); \ ++g_fail; \ } \ } while (0) // A CJK code point is 3 bytes in UTF-8 (U+6C49 "汉" = e6 b1 89). static std::string cjk(int n) { std::string s; for (int i = 0; i < n; ++i) { s += "\xE6\xB1\x89"; } return s; } int main() { using mtnet::to_wire; using mtnet::from_wire_str; // --- CHARACTER_NAME_MAX_LEN-equivalent cap = 24 bytes --- // 8 CJK chars == exactly 24 bytes: accepted whole. { const std::string in = cjk(8); CHECK(in.size() == 24, "8 CJK == 24 bytes"); const std::string w = to_wire(in, 24); CHECK(w.size() == 24 && w == in, "exactly-24 accepted unchanged"); } // 9 CJK chars == 27 bytes: truncated to 24 (8 chars), never 25/26. { const std::string in = cjk(9); CHECK(in.size() == 27, "9 CJK == 27 bytes"); const std::string w = to_wire(in, 24); CHECK(w.size() == 24, "27 -> 24 bytes"); CHECK(w == cjk(8), "truncated on the code-point boundary (8 chars)"); } // 25-byte input (8 CJK + 1 stray lead byte): a 24-byte cap keeps 8 chars, // and even a 25- or 26-byte cap cannot fit the 9th char -> still 24. { std::string in = cjk(8); in += '\xE6'; // dangling lead byte of a 9th char CHECK(in.size() == 25, "25-byte input"); CHECK(to_wire(in, 24).size() == 24, "cap 24 -> 24"); CHECK(to_wire(in, 25) == cjk(8), "cap 25 -> 24 (no split)"); CHECK(to_wire(in, 26) == cjk(8), "cap 26 -> 24 (9th char needs 3)"); } // Mid-sequence cap: cutting 2 bytes into a 3-byte char drops the whole char. { const std::string in = cjk(4); // 12 bytes CHECK(to_wire(in, 11).size() == 9, "cap 11 -> 9 (3 whole chars)"); CHECK(to_wire(in, 10).size() == 9, "cap 10 -> 9"); CHECK(to_wire(in, 12) == in, "cap 12 -> all 12"); } // ASCII round-trips against the same cap unchanged. { const std::string in = "Warrior"; CHECK(to_wire(in, 24) == in, "ascii under cap unchanged"); CHECK(to_wire(std::string(40, 'x'), 24).size() == 24, "ascii over cap -> 24"); } // --- from_wire_str: stop at NUL, then guard a dangling partial sequence --- { char buf[25] = {}; const std::string src = cjk(3); // 9 bytes std::memcpy(buf, src.data(), src.size()); CHECK(from_wire_str(buf, sizeof(buf) - 1) == src, "reads up to NUL"); } { char buf[25]; std::memset(buf, 0, sizeof(buf)); const std::string src = cjk(8); std::memcpy(buf, src.data(), src.size()); buf[24] = '\xE6'; // 25th byte: lead of a truncated char, no NUL room const std::string got = from_wire_str(buf, 25); CHECK(got == cjk(8), "dangling lead byte trimmed on read"); } { // Full 24-byte field, no NUL terminator: read all 8 chars. char buf[24]; const std::string src = cjk(8); std::memcpy(buf, src.data(), src.size()); CHECK(from_wire_str(buf, 24) == src, "unterminated full field read whole"); } if (g_fail) { std::fprintf(stderr, "%d checks FAILED\n", g_fail); return 1; } std::puts("net_text_codec_test: OK"); return 0; }