undf: assign UNDF numbers, stamp patches; 886 total

This commit is contained in:
russell@unturf.com 2026-03-30 17:21:56 -04:00
parent 3aaecab0e1
commit 4d2d38fe6e
9 changed files with 361 additions and 1 deletions

View file

@ -880,5 +880,9 @@
"nagioscore-0001-0001": "UNDF-2026-000000879",
"nagioscore-0002-0002": "UNDF-2026-000000880",
"zabbix-0001-0001": "UNDF-2026-000000881",
"zabbix-0002-0002": "UNDF-2026-000000882"
"zabbix-0002-0002": "UNDF-2026-000000882",
"openscad-0001": "UNDF-2026-000000883",
"pcsx2-0001-0001": "UNDF-2026-000000884",
"pcsx2-0002-0002": "UNDF-2026-000000885",
"wine-0001-0001": "UNDF-2026-000000886"
}

View file

@ -1,3 +1,4 @@
# UNDF: UNDF-2026-000000883
--- a/src/io/export_amf.cc
+++ b/src/io/export_amf.cc
@@ -36,6 +36,7 @@

View file

@ -0,0 +1,61 @@
# UNDF: UNDF-2026-000000884
--- a/pcsx2/Patch.cpp
+++ b/pcsx2/Patch.cpp
@@ -583,6 +583,8 @@
void Patch::ReloadEnabledLists()
{
+ // Convert lookup targets to sets for O(1) membership testing instead of
+ // O(N) linear scans on std::vector, eliminating four O(N^2) loops below.
const std::vector<std::string> prev_enabled_cheats = std::move(s_enabled_cheats);
if (EmuConfig.EnableCheats && !Achievements::IsHardcoreModeActive())
s_enabled_cheats = Host::GetStringListSetting(CHEATS_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY);
@@ -591,6 +593,10 @@
const std::vector<std::string> prev_enabled_patches = std::exchange(s_enabled_patches, Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY));
const std::vector<std::string> disabled_patches = Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_DISABLE_CONFIG_KEY);
+ const std::unordered_set<std::string> disabled_patches_set(disabled_patches.begin(), disabled_patches.end());
+ const std::unordered_set<std::string> prev_enabled_cheats_set(prev_enabled_cheats.begin(), prev_enabled_cheats.end());
+ const std::unordered_set<std::string> prev_enabled_patches_set(prev_enabled_patches.begin(), prev_enabled_patches.end());
+
// Name based matching for widescreen/NI settings.
if (EmuConfig.EnableWideScreenPatches)
@@ -612,7 +618,7 @@
for (auto it = s_enabled_patches.begin(); it != s_enabled_patches.end();)
{
- if (std::find(disabled_patches.begin(), disabled_patches.end(), *it) != disabled_patches.end())
+ if (disabled_patches_set.count(*it))
{
it = s_enabled_patches.erase(it);
}
@@ -626,13 +632,13 @@
s_just_enabled_patches.clear();
for (const auto& p : s_enabled_cheats)
{
- if (std::find(prev_enabled_cheats.begin(), prev_enabled_cheats.end(), p) == prev_enabled_cheats.end())
+ if (!prev_enabled_cheats_set.count(p))
{
s_just_enabled_cheats.emplace_back(p);
}
}
for (const auto& p : s_enabled_patches)
{
- if (std::find(prev_enabled_patches.begin(), prev_enabled_patches.end(), p) == prev_enabled_patches.end())
+ if (!prev_enabled_patches_set.count(p))
{
s_just_enabled_patches.emplace_back(p);
}
@@ -644,10 +650,11 @@
u32 Patch::EnablePatches(const std::vector<PatchGroup>* patches, const std::vector<std::string>& enable_list, const std::vector<std::string>* enable_immediately_list)
{
+ const std::unordered_set<std::string> enable_set(enable_list.begin(), enable_list.end());
u32 count = 0;
for (const PatchGroup& p : *patches)
{
// For compatibility, we auto enable anything that's not labelled.
// Also for gamedb patches.
- if (!p.name.empty() && std::find(enable_list.begin(), enable_list.end(), p.name) == enable_list.end())
+ if (!p.name.empty() && !enable_set.count(p.name))
continue;

View file

@ -0,0 +1,165 @@
import java.util.*;
/**
* Unit test for PCSX2 pcsx2-0001: Patch::ReloadEnabledLists and EnablePatches
* use std::find on std::vector for membership checks inside loops, giving O(N^2).
*
* Four sites in ReloadEnabledLists:
* 1. disabled_patches lookup O(E*D)
* 2. prev_enabled_cheats lookup O(C*P)
* 3. prev_enabled_patches lookup O(P*P)
* 4. EnablePatches enable_list lookup O(G*E)
*
* Fix: convert lookup vectors to HashSet for O(1) membership.
*
* Defect file: pcsx2/Patch.cpp lines 616, 630, 637, 651
*/
public class PatchReloadEnabledListsTest {
// --- Defective: linear scan on list ---
static List<String> reloadEnabledListsDefective(
List<String> enabledPatches,
List<String> disabledPatches,
List<String> prevEnabledPatches) {
// Filter disabled patches: O(E*D) with list.contains
List<String> filtered = new ArrayList<>();
for (String patch : enabledPatches) {
if (!disabledPatches.contains(patch)) { // O(D) scan per patch
filtered.add(patch);
}
}
// Find newly enabled: O(F*P) with list.contains
List<String> justEnabled = new ArrayList<>();
for (String p : filtered) {
if (!prevEnabledPatches.contains(p)) { // O(P) scan per patch
justEnabled.add(p);
}
}
return justEnabled;
}
// --- Fixed: hash set for O(1) lookups ---
static List<String> reloadEnabledListsFixed(
List<String> enabledPatches,
List<String> disabledPatches,
List<String> prevEnabledPatches) {
Set<String> disabledSet = new HashSet<>(disabledPatches);
Set<String> prevSet = new HashSet<>(prevEnabledPatches);
List<String> filtered = new ArrayList<>();
for (String patch : enabledPatches) {
if (!disabledSet.contains(patch)) { // O(1)
filtered.add(patch);
}
}
List<String> justEnabled = new ArrayList<>();
for (String p : filtered) {
if (!prevSet.contains(p)) { // O(1)
justEnabled.add(p);
}
}
return justEnabled;
}
// --- EnablePatches defective: O(G*E) ---
static int enablePatchesDefective(List<String> patchGroups, List<String> enableList) {
int count = 0;
for (String name : patchGroups) {
if (!name.isEmpty() && !enableList.contains(name)) // O(E) per group
continue;
count++;
}
return count;
}
// --- EnablePatches fixed: O(G+E) ---
static int enablePatchesFixed(List<String> patchGroups, List<String> enableList) {
Set<String> enableSet = new HashSet<>(enableList);
int count = 0;
for (String name : patchGroups) {
if (!name.isEmpty() && !enableSet.contains(name)) // O(1)
continue;
count++;
}
return count;
}
public static void main(String[] args) {
int N = 500;
// Build test data
List<String> enabled = new ArrayList<>();
List<String> disabled = new ArrayList<>();
List<String> prevEnabled = new ArrayList<>();
List<String> patchGroups = new ArrayList<>();
List<String> enableList = new ArrayList<>();
for (int i = 0; i < N; i++) {
String name = "patch_" + i;
enabled.add(name);
patchGroups.add(name);
enableList.add(name);
if (i % 5 == 0) disabled.add(name);
if (i < N / 2) prevEnabled.add(name);
}
// Correctness check
List<String> resultDef = reloadEnabledListsDefective(enabled, disabled, prevEnabled);
List<String> resultFix = reloadEnabledListsFixed(enabled, disabled, prevEnabled);
assert resultDef.equals(resultFix) : "ReloadEnabledLists mismatch";
int countDef = enablePatchesDefective(patchGroups, enableList);
int countFix = enablePatchesFixed(patchGroups, enableList);
assert countDef == countFix : "EnablePatches mismatch";
// Warm up
for (int i = 0; i < 200; i++) {
reloadEnabledListsDefective(enabled, disabled, prevEnabled);
reloadEnabledListsFixed(enabled, disabled, prevEnabled);
}
// Benchmark ReloadEnabledLists
int ITER = 2000;
long t0 = System.nanoTime();
for (int i = 0; i < ITER; i++) {
reloadEnabledListsDefective(enabled, disabled, prevEnabled);
}
long defectNs = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int i = 0; i < ITER; i++) {
reloadEnabledListsFixed(enabled, disabled, prevEnabled);
}
long fixedNs = System.nanoTime() - t0;
double ratio = (double) defectNs / fixedNs;
System.out.printf("ReloadEnabledLists N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n",
N, defectNs / 1e6, fixedNs / 1e6, ratio);
// Benchmark EnablePatches
t0 = System.nanoTime();
for (int i = 0; i < ITER; i++) {
enablePatchesDefective(patchGroups, enableList);
}
long epDefNs = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int i = 0; i < ITER; i++) {
enablePatchesFixed(patchGroups, enableList);
}
long epFixNs = System.nanoTime() - t0;
double epRatio = (double) epDefNs / epFixNs;
System.out.printf("EnablePatches N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n",
N, epDefNs / 1e6, epFixNs / 1e6, epRatio);
assert ratio > 2.0 : "Expected >2x speedup for ReloadEnabledLists, got " + ratio;
assert epRatio > 2.0 : "Expected >2x speedup for EnablePatches, got " + epRatio;
System.out.println("PASS");
}
}

View file

@ -0,0 +1,21 @@
# UNDF: UNDF-2026-000000885
--- a/pcsx2/GS/GSCapture.cpp
+++ b/pcsx2/GS/GSCapture.cpp
@@ -1488,6 +1488,7 @@
}
void* iter = nullptr;
const AVCodec* codec;
+ std::unordered_set<std::string> seen_codecs;
while ((codec = wrap_av_codec_iterate(&iter)) != nullptr)
{
// only get audio codecs
@@ -1498,7 +1499,7 @@
if (!wrap_avformat_query_codec(output_format, codec->id, FF_COMPLIANCE_NORMAL))
continue;
- if (std::find_if(ret.begin(), ret.end(), [codec](const auto& it) { return it.first == codec->name; }) != ret.end())
+ if (!seen_codecs.insert(codec->name).second)
continue;
ret.emplace_back(codec->name, codec->long_name ? codec->long_name : codec->name);

Binary file not shown.

View file

@ -0,0 +1,80 @@
import java.util.*;
/**
* Unit test for PCSX2 pcsx2-0002: GSCapture::GetCodecListForContainer
* uses std::find_if on vector to deduplicate codec names during enumeration,
* giving O(N^2) where N = number of codecs iterated.
*
* Fix: track seen codec names in an unordered_set for O(1) dedup.
*
* Defect file: pcsx2/GS/GSCapture.cpp line 1501
*/
public class GSCaptureCodecDedupTest {
// --- Defective: linear scan on list for dedup ---
static List<String> getCodecListDefective(List<String> codecs) {
List<String> ret = new ArrayList<>();
for (String name : codecs) {
boolean found = false;
for (String existing : ret) {
if (existing.equals(name)) {
found = true;
break;
}
}
if (!found) {
ret.add(name);
}
}
return ret;
}
// --- Fixed: hash set dedup ---
static List<String> getCodecListFixed(List<String> codecs) {
List<String> ret = new ArrayList<>();
Set<String> seen = new HashSet<>();
for (String name : codecs) {
if (seen.add(name)) {
ret.add(name);
}
}
return ret;
}
public static void main(String[] args) {
int N = 500;
// Build codec list with ~50% duplicates
List<String> codecs = new ArrayList<>();
for (int i = 0; i < N; i++) {
codecs.add("codec_" + (i % (N / 2)));
}
// Correctness
List<String> resDef = getCodecListDefective(codecs);
List<String> resFix = getCodecListFixed(codecs);
assert resDef.equals(resFix) : "Mismatch";
// Warmup
for (int i = 0; i < 500; i++) {
getCodecListDefective(codecs);
getCodecListFixed(codecs);
}
int ITER = 5000;
long t0 = System.nanoTime();
for (int i = 0; i < ITER; i++) getCodecListDefective(codecs);
long defNs = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int i = 0; i < ITER; i++) getCodecListFixed(codecs);
long fixNs = System.nanoTime() - t0;
double ratio = (double) defNs / fixNs;
System.out.printf("GSCapture codec dedup N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n",
N, defNs / 1e6, fixNs / 1e6, ratio);
assert ratio > 2.0 : "Expected >2x speedup, got " + ratio;
System.out.println("PASS");
}
}

View file

@ -0,0 +1,28 @@
# UNDF: UNDF-2026-000000886
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -860,16 +860,22 @@
static LDR_DEPENDENCY *find_module_dependency( LDR_DDAG_NODE *from, LDR_DDAG_NODE *to )
{
+ /* DEFECT: O(D) linear scan through circular singly-linked list of
+ * dependencies. Called from add_module_dependency_after() on every
+ * DLL import, yielding O(I*D) per module where I = imports and
+ * D = accumulated dependencies. For complex DLL trees with hundreds
+ * of imports this is quadratic.
+ *
+ * Ideal fix: maintain a hash set (e.g. wine_rb_tree keyed on
+ * dependency_to pointer) alongside the linked list. Check the
+ * hash set for O(1) dedup instead of walking the list.
+ * The linked list must be preserved for Windows ABI compatibility. */
SINGLE_LIST_ENTRY *entry, *mark = from->Dependencies.Tail;
if (!mark) return NULL;
for (entry = mark->Next; entry != mark; entry = entry->Next)
{
LDR_DEPENDENCY *dep = CONTAINING_RECORD( entry, LDR_DEPENDENCY, dependency_to_entry );
if (dep->dependency_to == to && dep->dependency_from == from) return dep;
}
return NULL;
}