java-topology/defects/fbneo-0001/test/test_burn_drv_get_index.cpp
russell@unturf.com 5aaaac1ee3 fbneo: 1 CWE-407 defect, MOAD 0002-0005 CLEAN; mame: update scan to all 5 MOADs CLEAN
fbneo-0001: BurnDrvGetIndex O(N) linear scan over 24493 drivers -> O(1) unordered_map,
91x speedup at N=5000. Fix: hash index built at BurnLibInit, cleared at BurnLibExit.
mame: confirmed CLEAN on all 5 MOADs (binary search for driver lookup, single-threaded).
2026-03-31 17:50:45 -04:00

139 lines
4.5 KiB
C++

// Unit test for fbneo-0001: BurnDrvGetIndex O(N) linear scan -> O(1) hash lookup.
// Models the defect: with 5000+ game drivers, every call to BurnDrvGetIndex
// (game load, state load, Kaillera/netplay) scanned the full driver array.
// Fix: build std::unordered_map<string, INT32> at BurnLibInit time, O(1) lookup.
#include <cassert>
#include <cstdio>
#include <chrono>
#include <string>
#include <vector>
#include <unordered_map>
#include <cstring>
// Minimal driver struct matching FBNeo's BurnDriver
struct BurnDriver {
const char* szShortName;
};
// Original: O(N) linear scan
static int drv_get_index_original(const std::vector<BurnDriver*>& pDriver,
const char* szName)
{
if (!szName) return -1;
for (int i = 0; i < (int)pDriver.size(); i++) {
if (strcmp(szName, pDriver[i]->szShortName) == 0)
return i;
}
return -1;
}
// Patched: O(1) hash lookup
struct PatchedIndex {
std::unordered_map<std::string, int> map;
void build(const std::vector<BurnDriver*>& pDriver) {
map.clear();
map.reserve(pDriver.size());
for (int i = 0; i < (int)pDriver.size(); i++)
map[pDriver[i]->szShortName] = i;
}
int get(const char* szName) const {
if (!szName) return -1;
auto it = map.find(szName);
return (it != map.end()) ? it->second : -1;
}
};
int main()
{
// Build driver pool modeling FBNeo's ~5000 game drivers
const int N_DRIVERS = 5000;
std::vector<std::string> names;
names.reserve(N_DRIVERS);
for (int i = 0; i < N_DRIVERS; i++)
names.push_back("drv_" + std::to_string(i));
std::vector<BurnDriver> drivers(N_DRIVERS);
std::vector<BurnDriver*> pDriver(N_DRIVERS);
for (int i = 0; i < N_DRIVERS; i++) {
drivers[i].szShortName = names[i].c_str();
pDriver[i] = &drivers[i];
}
PatchedIndex idx;
idx.build(pDriver);
// Test 1: found at index 0 (best case original, same patched)
{
int orig = drv_get_index_original(pDriver, "drv_0");
int patched = idx.get("drv_0");
assert(orig == 0);
assert(patched == 0);
printf("PASS: lookup first driver\n");
}
// Test 2: found at last index (worst case original)
{
int orig = drv_get_index_original(pDriver, "drv_4999");
int patched = idx.get("drv_4999");
assert(orig == 4999);
assert(patched == 4999);
printf("PASS: lookup last driver\n");
}
// Test 3: not found returns -1
{
int orig = drv_get_index_original(pDriver, "no_such_game");
int patched = idx.get("no_such_game");
assert(orig == -1);
assert(patched == -1);
printf("PASS: missing driver returns -1\n");
}
// Test 4: null returns -1
{
int orig = drv_get_index_original(pDriver, nullptr);
int patched = idx.get(nullptr);
assert(orig == -1);
assert(patched == -1);
printf("PASS: null name returns -1\n");
}
// Test 5: correctness across all drivers
{
for (int i = 0; i < N_DRIVERS; i += 100) {
assert(drv_get_index_original(pDriver, names[i].c_str()) == i);
assert(idx.get(names[i].c_str()) == i);
}
printf("PASS: correctness across all %d drivers\n", N_DRIVERS);
}
// Test 6: performance -- lookup all N drivers once (covers entire range, no elision)
{
volatile int sink = 0;
auto t0 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N_DRIVERS; i++)
sink += drv_get_index_original(pDriver, names[i].c_str());
auto t1 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N_DRIVERS; i++)
sink += idx.get(names[i].c_str());
auto t2 = std::chrono::high_resolution_clock::now();
(void)sink;
double orig_us = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
double patch_us = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
double ratio = orig_us / (patch_us > 0.0 ? patch_us : 1.0);
printf("PASS: performance N=%d all-driver scan: original=%.0fus patched=%.0fus ratio=%.1fx\n",
N_DRIVERS, orig_us, patch_us, ratio);
// Scanning all N=5000 drivers takes O(N^2/2) comparisons vs O(N) hash lookups.
// Even with vectorization the ratio must exceed 5x at N=5000.
assert(ratio > 5.0);
}
printf("ALL TESTS PASSED\n");
return 0;
}