java-topology/defects/solvespace-0002/SCAN-NOTES.md
russell@unturf.com 5575f6cd9c jitsi-meet+solvespace: 5-MOAD scan; 3 new CWE-407 defects
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.
2026-03-31 22:42:46 -04:00

2.5 KiB
Raw Blame History

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:

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)