From: Matthew Fernandez Date: Thu, 24 Nov 2022 18:41:03 +0000 (-0800) Subject: ortho: allocate trapezoid structures on-demand X-Git-Tag: 7.0.4~2^2~2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8f60584180071ab9c0f212c3f31aac1d53ed4757;p=graphviz ortho: allocate trapezoid structures on-demand The ortho library estimated the number of trapezoid structures it would need upfront based on the number of segments it was operating on. This estimation was wrong. Some inputs could exceed the estimation, at which point Graphviz would abort with an error message. This change makes trapezoid allocation dynamic, with the trapezoid collection being expanded on-demand as new trapezoids are needed. The number of the trapezoids is now only constrained by how much available memory there is. Gitlab: fixes #56, fixes #1880 --- diff --git a/CHANGELOG.md b/CHANGELOG.md index d132113a0..0d773d85e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 demand loading disabled. - `gvpack` built with the CMake build system can now find plugins correctly at run time. #1838 +- The ortho library now allocates trapezoid structures on-demand, removing the + “Trapezoid-table overflow” error that previously occurred when its upfront + estimation was exceeded. #56, #1880 ## [7.0.2] – 2022-11-18 diff --git a/lib/ortho/partition.c b/lib/ortho/partition.c index 9814f21ce..aa972d243 100644 --- a/lib/ortho/partition.c +++ b/lib/ortho/partition.c @@ -24,7 +24,6 @@ #endif #define NPOINTS 4 /* only rectangles */ -#define TRSIZE(ss) (5*(ss)+1) #define TR_FROM_UP 1 /* for traverse-direction */ #define TR_FROM_DN 2 @@ -690,13 +689,15 @@ partition (cell* cells, int ncells, int* nrects, boxf bb) int nsegs = 4*(ncells+1); segment_t* segs = gv_calloc(nsegs + 1, sizeof(segment_t)); int* permute = gv_calloc(nsegs + 1, sizeof(int)); - int ntraps = TRSIZE(nsegs); - traps_t trs = {.length = ntraps, - .data = gv_calloc(ntraps, sizeof(trap_t))}; + + // First trapezoid is reserved as a sentinel. We will append later + // trapezoids by expanding this on-demand. + traps_t trs = {.length = 1, .data = gv_calloc(1, sizeof(trap_t))}; + int nt; if (DEBUG) { - fprintf (stderr, "cells = %d segs = %d traps = %d\n", ncells, nsegs, ntraps); + fprintf (stderr, "cells = %d segs = %d traps = dynamic\n", ncells, nsegs); } genSegments (cells, ncells, bb, segs, 0); if (DEBUG) { @@ -716,6 +717,10 @@ partition (cell* cells, int ncells, int* nrects, boxf bb) boxes_t hor_decomp = {0}; monotonate_trapezoids(nsegs, segs, &trs, 0, &hor_decomp); + // reset trapezoid collection + free(trs.data); + trs = (traps_t){.length = 1, .data = gv_calloc(1, sizeof(trap_t))}; + genSegments (cells, ncells, bb, segs, 1); generateRandomOrdering (nsegs, permute); nt = construct_trapezoids(nsegs, segs, permute, &trs); diff --git a/lib/ortho/trapezoid.c b/lib/ortho/trapezoid.c index de472f3e4..631b9405f 100644 --- a/lib/ortho/trapezoid.c +++ b/lib/ortho/trapezoid.c @@ -60,8 +60,6 @@ typedef struct { qnode_t *data; } qnodes_t; -static int tr_idx; - /* Return a new node to be added into the query tree */ static int newnode(qnodes_t *qs) { qs->data = gv_recalloc(qs->data, qs->length, qs->length + 1, sizeof(qnode_t)); @@ -71,17 +69,9 @@ static int newnode(qnodes_t *qs) { /* Return a free trapezoid */ static int newtrap(traps_t *tr) { - if (tr_idx < tr->length) { - tr->data[tr_idx].lseg = -1; - tr->data[tr_idx].rseg = -1; - tr->data[tr_idx].state = ST_VALID; - return tr_idx++; - } - else { - fprintf(stderr, "newtrap: Trapezoid-table overflow %d\n", tr_idx); - assert(0); - return -1; - } + tr->data = gv_recalloc(tr->data, tr->length, tr->length + 1, sizeof(trap_t)); + ++tr->length; + return tr->length - 1; } /* Return the maximum of the two points into the yval structure */ @@ -1025,9 +1015,6 @@ construct_trapezoids(int nseg, segment_t *seg, int *permute, traps_t* tr) { // sentinel. qnodes_t qs = {.length = 1, .data = gv_calloc(1, sizeof(qnode_t))}; - tr_idx = 1; - memset(tr->data, 0, tr->length * sizeof(trap_t)); - /* Add the first segment and get the query structure and trapezoid */ /* list initialised */ @@ -1049,5 +1036,5 @@ construct_trapezoids(int nseg, segment_t *seg, int *permute, traps_t* tr) { add_segment(permute[segi++], seg, tr, &qs); free(qs.data); - return tr_idx; + return tr->length; } diff --git a/tests/test_regression.py b/tests/test_regression.py index 405f426b3..b93bfe046 100644 --- a/tests/test_regression.py +++ b/tests/test_regression.py @@ -100,12 +100,6 @@ def test_56(): input = Path(__file__).parent / "56.dot" assert input.exists(), "unexpectedly missing test case" - # FIXME: remove this block when this #56 is fixed - if not is_ndebug_defined() and platform.system() != "Windows": - with pytest.raises(subprocess.CalledProcessError): - dot("svg", input) - return - # process it with Graphviz dot("svg", input) @@ -894,12 +888,6 @@ def test_1880(): input = Path(__file__).parent / "1880.dot" assert input.exists(), "unexpectedly missing test case" - # FIXME: remove this block when this #1880 is fixed - if not is_ndebug_defined() and platform.system() != "Windows": - with pytest.raises(subprocess.CalledProcessError): - dot("png", input) - return - # process it with Graphviz dot("png", input)