forked from 1b-agent/opencadstudio-upstream
fix(io): purge degenerate arcs/splines that OOM on load
Large survey drawings (105k entities, multi-million unit extents) could exhaust all RAM + swap on one core during load and get OOM-killed before display. Two parser-garbage entities slipped past the corrupt-entity purge and blew up truck's parameter_division tessellation: - Near-zero-sweep arcs: the zero-sweep guard only caught sweep < 1e-9, but a tiny non-zero sweep (e.g. 1.6e-6 rad) still places start/mid/end within truck's coincidence tolerance and recurses unboundedly. Gate on arc length (radius x sweep) instead so large-radius small-sweep arcs (still visible curves) survive while sub-precision arcs drop. - Desync splines with 100k control points and an invalid knot vector had no guard at all. Add a Spline arm rejecting the 100k desync signature, non-finite control points, degree < 1, and bad knot vectors. Also cap complex-linetype dash expansion (complex_lt::apply_along) at 1e6 pattern repeats, falling back to a solid stroke — a latent OOM of the same class on map-scale entities carrying a finely-scaled pattern. Adds regression tests for the arc-length and spline guards. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
e6ab0e70ac
commit
9cdb5ea5fd
2 files changed
+106
No files matched your search
@@ -47,6 +47,26 @@ pub fn apply_along(
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// Guard against pattern blow-up. The per-segment walk below emits at least
|
||||
// one stroke vertex per pattern element, so the total vertex count scales
|
||||
// with `path_len / pattern_len`. On a large-extent drawing (e.g. a city/
|
||||
// street map) carrying a finely-scaled complex linetype, that ratio can
|
||||
// reach billions — the strokes Vec grows until the process is OOM-killed,
|
||||
// single-threaded, before the drawing ever finishes loading. Past a sane
|
||||
// repeat count the dashes are sub-pixel anyway, so render the base wire
|
||||
// solid: returning empty here makes the caller fall back to the solid base.
|
||||
const MAX_PATTERN_REPEATS: f32 = 1_000_000.0;
|
||||
let path_len: f32 = path_pts
|
||||
.windows(2)
|
||||
.map(|w| {
|
||||
let d = [w[1][0] - w[0][0], w[1][1] - w[0][1], w[1][2] - w[0][2]];
|
||||
(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt()
|
||||
})
|
||||
.sum();
|
||||
if path_len / pattern_len > MAX_PATTERN_REPEATS {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let mut strokes: Vec<Vec<[f32; 3]>> = Vec::new();
|
||||
let mut cur_stroke: Vec<[f32; 3]> = Vec::new();
|
||||
|
||||
|
||||
Reference in new issue
Block a user