undf: assign UNDF numbers, stamp patches; 886 total
This commit is contained in:
parent
3aaecab0e1
commit
4d2d38fe6e
9 changed files with 361 additions and 1 deletions
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# UNDF: UNDF-2026-000000883
|
||||
--- a/src/io/export_amf.cc
|
||||
+++ b/src/io/export_amf.cc
|
||||
@@ -36,6 +36,7 @@
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
BIN
defects/pcsx2-0001/test/PatchReloadEnabledListsTest.class
Normal file
BIN
defects/pcsx2-0001/test/PatchReloadEnabledListsTest.class
Normal file
Binary file not shown.
165
defects/pcsx2-0001/test/PatchReloadEnabledListsTest.java
Normal file
165
defects/pcsx2-0001/test/PatchReloadEnabledListsTest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
BIN
defects/pcsx2-0002/test/GSCaptureCodecDedupTest.class
Normal file
BIN
defects/pcsx2-0002/test/GSCaptureCodecDedupTest.class
Normal file
Binary file not shown.
80
defects/pcsx2-0002/test/GSCaptureCodecDedupTest.java
Normal file
80
defects/pcsx2-0002/test/GSCaptureCodecDedupTest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue