]> granicus.if.org Git - graphviz/commitdiff
ortho: allocate trapezoid structures on-demand
authorMatthew Fernandez <matthew.fernandez@gmail.com>
Thu, 24 Nov 2022 18:41:03 +0000 (10:41 -0800)
committerMatthew Fernandez <matthew.fernandez@gmail.com>
Wed, 30 Nov 2022 04:15:50 +0000 (20:15 -0800)
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

CHANGELOG.md
lib/ortho/partition.c
lib/ortho/trapezoid.c
tests/test_regression.py

index d132113a0126394152a5bc7b141ddd175271ed11..0d773d85e5bad695c4cb1ca148754c51d49cea4f 100644 (file)
@@ -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
 
index 9814f21cef2ae227161a74930e3fe405fe72ae66..aa972d2439a2bce7f000531596684bb3bc330f6d 100644 (file)
@@ -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);
index de472f3e476f7daac6af57dd19aee064d54fdbd6..631b9405f92b8dd857353de8f5e9ab8677eff9df 100644 (file)
@@ -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;
 }
index 405f426b3a6b449f930eb455205510f863600683..b93bfe04613837d655f9e73913c1d22ff4d58ed0 100644 (file)
@@ -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)