jitsi-meet-0003: av-moderation pendingAudio/Video/Desktop Array.find() dedup O(P^2) in large moderated meetings — 249.5x op-count at P=500, 138.5x measured at P=2000. Fix: Map<id, participant> for O(1) dedup. jitsi-meet-0004: visitors middleware delta Array.findIndex() per update O(U*V) in large broadcast events — 15x at V=5000 U=1000. Fix: Map-based apply-delta O(1) per join/leave. solvespace-0002: VRML export colours_present std::vector + find_if O(T*C) per triangle — 250x op-count at T=50k C=500. Fix: unordered_map keyed by ToPackedInt() RGBA uint32. MOAD-0002/0003/0004/0005: CLEAN for both targets. All 13 unit tests PASS.
62 lines
2.5 KiB
Markdown
62 lines
2.5 KiB
Markdown
# solvespace-0002 — MOAD-0001 CWE-407
|
||
|
||
## Location
|
||
|
||
`src/export.cpp`, `ExportWrlMeshes()` (VRML/WRL export), lines 1225-1240
|
||
|
||
## Pattern
|
||
|
||
O(T × C): During VRML mesh export, Solvespace builds a per-shape colour palette
|
||
by iterating every triangle and scanning a `std::vector<RgbaColor>` to check
|
||
whether our triangle's colour is already present:
|
||
|
||
```cpp
|
||
std::vector<int> triangle_colour_ids;
|
||
std::vector<RgbaColor> colours_present;
|
||
for(const auto & sp : op.second) { // outer: spans
|
||
for(const auto & tr : sp) { // inner: triangles O(T)
|
||
const auto colour_itr = std::find_if(
|
||
colours_present.begin(), colours_present.end(),
|
||
[&](const RgbaColor & c) {
|
||
return c.Equals(tr.meta.color); // O(C) per triangle
|
||
});
|
||
if(colour_itr == colours_present.end()) {
|
||
colours_present.insert(colours_present.end(), tr.meta.color);
|
||
triangle_colour_ids.push_back(colours_present.size() - 1);
|
||
} else {
|
||
triangle_colour_ids.push_back(colour_itr - colours_present.begin());
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Each triangle triggers a full O(C) walk of `colours_present`. Total cost: O(T × C).
|
||
|
||
## Severity
|
||
|
||
MEDIUM-HIGH. A mechanical assembly export with:
|
||
- T = 100,000 triangles (typical for a detailed 3D model)
|
||
- C = 64 distinct colours (per-part colour coding is common)
|
||
|
||
Results in 6,400,000 comparisons vs 100,000 with an `unordered_map`.
|
||
|
||
`RgbaColor` is a packed `uint32_t` (four `uint8_t` fields: red, green, blue, alpha).
|
||
Our hash map key is `color.ToPackedIntBGRA()` (already defined in our codebase).
|
||
Fix is a single-line `unordered_map<uint32_t, int>` substitution. 64x overhead at
|
||
typical export size.
|
||
|
||
## Fix
|
||
|
||
Replace `std::vector<RgbaColor> colours_present` with
|
||
`std::unordered_map<uint32_t, int> colour_to_index`. Key: `tr.meta.color.ToPackedIntBGRA()`.
|
||
Value: index into our VRML color array. Membership test becomes `colour_to_index.count(key)`
|
||
(O(1)). The array output for VRML is built in insertion order from our map values.
|
||
`ToPackedIntBGRA()` is already defined in `dsc.h` line 649 — no new infrastructure needed.
|
||
|
||
## All 5 MOADs
|
||
|
||
- MOAD-0001: CONFIRMED (this defect)
|
||
- MOAD-0002: CLEAN (SS/SK globals are intentional single-user desktop CAD singletons; no coupling to new subsystems)
|
||
- MOAD-0003: CLEAN (single-threaded tool; no request-scoped context leakage)
|
||
- MOAD-0004: CLEAN (no network features; no credentials handled anywhere)
|
||
- MOAD-0005: CLEAN (single-process, single-threaded; no concurrent cache access possible)
|