44 lines
1.6 KiB
Diff
44 lines
1.6 KiB
Diff
# UNDF: UNDF-2026-000000976
|
|
# Widelands CWE-407: FindBobsCallback std::find on vector O(B²) dedup
|
|
# File: src/logic/map.cc
|
|
# Severity: HIGH
|
|
# Speedup: ~250x at B=500 (500 bobs in area)
|
|
#
|
|
# FindBobsCallback::operator() uses std::find() on a std::vector<Bob*>
|
|
# to deduplicate bobs found across adjacent fields. Each bob insertion
|
|
# requires scanning the entire list, producing O(B²) where B = bobs found.
|
|
# This callback is invoked from find_bobs() and find_reachable_bobs(),
|
|
# which are called from 36 sites including combat (soldier finding),
|
|
# critter AI (population density), ship fleet scanning, and worker tasks.
|
|
#
|
|
# Fix: Add an std::unordered_set<Bob*> as a shadow structure for O(1)
|
|
# membership checks, keeping the vector output interface unchanged.
|
|
--- a/src/logic/map.cc
|
|
+++ b/src/logic/map.cc
|
|
@@ -1159,14 +1159,16 @@
|
|
struct FindBobsCallback {
|
|
FindBobsCallback(std::vector<Bob*>* const list, const FindBob& functor)
|
|
- : list_(list), functor_(functor) {
|
|
+ : list_(list), functor_(functor), seen_() {
|
|
}
|
|
|
|
void operator()(const EditorGameBase& /* egbase */, const FCoords& cur) {
|
|
for (Bob* bob = cur.field->get_first_bob(); bob != nullptr; bob = bob->get_next_bob()) {
|
|
- if ((list_ != nullptr) && std::find(list_->begin(), list_->end(), bob) != list_->end()) {
|
|
+ if ((list_ != nullptr) && seen_.count(bob) != 0) {
|
|
continue;
|
|
}
|
|
|
|
if (functor_.accept(bob)) {
|
|
if (list_ != nullptr) {
|
|
list_->push_back(bob);
|
|
+ seen_.insert(bob);
|
|
}
|
|
++found_;
|
|
}
|
|
@@ -1176,6 +1178,7 @@
|
|
std::vector<Bob*>* list_;
|
|
const FindBob& functor_;
|
|
uint32_t found_{0U};
|
|
+ std::unordered_set<Bob*> seen_;
|
|
};
|