minetest-0001: mg_ore.cpp c_wherein vector CONTAINS in voxel inner loop O(V*W) HIGH 3.3x minetest-0002: mg_decoration.cpp c_place_on/c_spawnby vector CONTAINS O(S*P) MEDIUM 1.8x minetest-0003: l_env.cpp find_node_near/find_nodes_in_area filter CONTAINS O(V*F) MEDIUM 1.6x minetest-0004: nodedef.cpp nodeboxConnects sorted vector linear scan O(N) MEDIUM 2.1x minetest-0005: blockmodifier.cpp ABM neighbor check sorted vector O(N) LOW-MEDIUM 1.5x MOAD-0002: g_settings global singleton (architectural, not patchable) MOAD-0003: thread_local log streams (properly scoped, not leaked context) MOAD-0004: CLEAN (no credential logging found) MOAD-0005: CLEAN (no unsynchronized cache patterns found)
101 lines
3.6 KiB
C++
101 lines
3.6 KiB
C++
// minetest-0001-test: CWE-407 ore generation c_wherein linear scan
|
|
// Defect: CONTAINS(c_wherein, content) uses std::find O(N) per voxel node
|
|
// in ore generation inner loop, iterating 512K+ nodes per mapchunk.
|
|
// Fix: std::unordered_set<content_t> for O(1) lookup.
|
|
//
|
|
// Severity: HIGH
|
|
// Location: src/mapgen/mg_ore.cpp, all 6 ore type generate() methods
|
|
// Pattern: CONTAINS(c_wherein, vm->m_data[i].getContent())
|
|
|
|
#include <vector>
|
|
#include <unordered_set>
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <cstdint>
|
|
|
|
using content_t = uint16_t;
|
|
|
|
#define CONTAINS(c, v) (std::find((c).begin(), (c).end(), (v)) != (c).end())
|
|
|
|
// Simulate ore generation inner loop: for each voxel, check membership
|
|
// in the c_wherein collection.
|
|
|
|
static long long benchmark_vector(const std::vector<content_t> &c_wherein,
|
|
const std::vector<content_t> &voxel_data) {
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
int matches = 0;
|
|
for (content_t c : voxel_data) {
|
|
if (CONTAINS(c_wherein, c))
|
|
matches++;
|
|
}
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
|
|
// prevent optimization
|
|
assert(matches >= 0);
|
|
return ns;
|
|
}
|
|
|
|
static long long benchmark_unordered_set(const std::unordered_set<content_t> &c_wherein,
|
|
const std::vector<content_t> &voxel_data) {
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
int matches = 0;
|
|
for (content_t c : voxel_data) {
|
|
if (c_wherein.count(c) > 0)
|
|
matches++;
|
|
}
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
|
|
assert(matches >= 0);
|
|
return ns;
|
|
}
|
|
|
|
int main() {
|
|
// Heavy modded game: ore placeable in many node types via groups.
|
|
// "group:stone" can resolve to 100+ content IDs in modpacks.
|
|
const int WHEREIN_SIZE = 100;
|
|
// Mapchunk volume: 80x80x80 = 512000 nodes
|
|
const int VOLUME = 512000;
|
|
|
|
std::vector<content_t> wherein_vec;
|
|
std::unordered_set<content_t> wherein_set;
|
|
for (int i = 0; i < WHEREIN_SIZE; i++) {
|
|
content_t id = 100 + i * 7; // spread out IDs
|
|
wherein_vec.push_back(id);
|
|
wherein_set.insert(id);
|
|
}
|
|
|
|
// Generate voxel data: mostly non-matching (air, water, etc.)
|
|
// with ~10% matching (stone variants)
|
|
std::vector<content_t> voxel_data(VOLUME);
|
|
for (int i = 0; i < VOLUME; i++) {
|
|
if (i % 10 == 0)
|
|
voxel_data[i] = wherein_vec[i % WHEREIN_SIZE]; // match
|
|
else
|
|
voxel_data[i] = 1 + (i % 50); // non-match, common nodes
|
|
}
|
|
|
|
// Warmup
|
|
benchmark_vector(wherein_vec, voxel_data);
|
|
benchmark_unordered_set(wherein_set, voxel_data);
|
|
|
|
// Benchmark
|
|
long long vec_ns = benchmark_vector(wherein_vec, voxel_data);
|
|
long long set_ns = benchmark_unordered_set(wherein_set, voxel_data);
|
|
|
|
double ratio = (double)vec_ns / (double)set_ns;
|
|
|
|
printf("=== minetest-0001: ore c_wherein membership test ===\n");
|
|
printf("c_wherein size: %d, volume: %d nodes, ops: %lld\n",
|
|
WHEREIN_SIZE, VOLUME, (long long)WHEREIN_SIZE * VOLUME);
|
|
printf("vector (std::find): %lld ns\n", vec_ns);
|
|
printf("unordered_set (count): %lld ns\n", set_ns);
|
|
printf("ratio: %.1fx\n", ratio);
|
|
fflush(stdout);
|
|
|
|
// PASS criteria: unordered_set must be faster
|
|
assert(ratio >= 1.5 && "FAIL: unordered_set should be at least 1.5x faster");
|
|
printf("PASS\n");
|
|
return 0;
|
|
}
|