// UNDF: UNDF-2026-XXXXXXXXX // Test: lime3ds-0001 — Room ban list O(N) std::find -> O(1) unordered_set // // Defect: HandleJoinRequest checks username and IP bans via std::find over // std::vector. With B bans and C connection attempts the // total work is O(C * B). On a large, heavily-moderated room (B=1000, // C=100 simultaneous joins) this is 100,000 string comparisons instead // of 200 (100 username + 100 IP hash lookups). // // Fix: change internal storage to std::unordered_set for O(1) // average-case membership checks; convert to/from std::vector only at // serialization boundaries (GetBanList / Create / SendModBanListResponse). // // Compile: g++ -std=c++17 -O2 -o test_lime3ds_0001 test_lime3ds_0001.cpp && ./test_lime3ds_0001 #include #include #include #include #include #include #include // --------------------------------------------------------------------------- // Simulate the BEFORE (defect) path: std::vector + std::find // --------------------------------------------------------------------------- static bool ban_check_vector(const std::vector& ban_list, const std::string& candidate) { return std::find(ban_list.begin(), ban_list.end(), candidate) != ban_list.end(); } // --------------------------------------------------------------------------- // Simulate the AFTER (fixed) path: std::unordered_set // --------------------------------------------------------------------------- static bool ban_check_set(const std::unordered_set& ban_set, const std::string& candidate) { return ban_set.count(candidate) != 0; } // --------------------------------------------------------------------------- // Correctness test // --------------------------------------------------------------------------- static void test_correctness() { std::vector vec_bans; std::unordered_set set_bans; for (int i = 0; i < 200; ++i) { std::string entry = "banned_user_" + std::to_string(i); vec_bans.push_back(entry); set_bans.insert(entry); } // Banned entries must be detected by both implementations for (int i = 0; i < 200; ++i) { std::string entry = "banned_user_" + std::to_string(i); assert(ban_check_vector(vec_bans, entry) == true); assert(ban_check_set(set_bans, entry) == true); } // Non-banned entries must be rejected by both for (int i = 200; i < 400; ++i) { std::string entry = "allowed_user_" + std::to_string(i); assert(ban_check_vector(vec_bans, entry) == false); assert(ban_check_set(set_bans, entry) == false); } // insert is idempotent in the set (no duplicate bans) set_bans.insert("banned_user_0"); assert(set_bans.size() == 200); // erase by value works on the set assert(set_bans.erase("banned_user_0") > 0); assert(ban_check_set(set_bans, "banned_user_0") == false); printf("Correctness: PASS\n"); } // --------------------------------------------------------------------------- // Benchmark: O(N) vs O(1) at B=1000 bans, 100 join checks // --------------------------------------------------------------------------- static void test_performance() { const int NUM_BANS = 1000; const int NUM_CHECKS = 100; const int REPEAT = 200; std::vector vec_bans; std::unordered_set set_bans; for (int i = 0; i < NUM_BANS; ++i) { std::string entry = "banned_" + std::to_string(i); vec_bans.push_back(entry); set_bans.insert(entry); } // Use non-banned candidates so every search goes the full distance std::vector candidates; for (int i = 0; i < NUM_CHECKS; ++i) { candidates.push_back("user_" + std::to_string(i + NUM_BANS)); } // --- defect path (vector + std::find) --- auto t0 = std::chrono::high_resolution_clock::now(); volatile bool sink = false; for (int r = 0; r < REPEAT; ++r) { for (const auto& c : candidates) { sink ^= ban_check_vector(vec_bans, c); } } auto t1 = std::chrono::high_resolution_clock::now(); double ms_vector = std::chrono::duration(t1 - t0).count(); // --- fixed path (unordered_set) --- t0 = std::chrono::high_resolution_clock::now(); for (int r = 0; r < REPEAT; ++r) { for (const auto& c : candidates) { sink ^= ban_check_set(set_bans, c); } } t1 = std::chrono::high_resolution_clock::now(); double ms_set = std::chrono::duration(t1 - t0).count(); double ratio = (ms_set > 0.0) ? (ms_vector / ms_set) : 9999.0; printf("Performance (B=%d, C=%d, repeat=%d):\n", NUM_BANS, NUM_CHECKS, REPEAT); printf(" vector+find : %.3f ms\n", ms_vector); printf(" unordered_set: %.3f ms\n", ms_set); printf(" speedup : %.1fx\n", ratio); assert(ratio >= 3.0 && "Expected at least 3x speedup from unordered_set at B=1000"); printf("Performance: PASS\n"); (void)sink; } int main() { test_correctness(); test_performance(); printf("ALL TESTS PASSED\n"); return 0; }