From: Matthew Fernandez Date: Mon, 18 Apr 2022 04:31:41 +0000 (-0700) Subject: core plugin: remove 'GVPUTS' optimization X-Git-Tag: 4.0.0~78^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cd96927315c3522a7c0c9be500ea5d825675c785;p=graphviz core plugin: remove 'GVPUTS' optimization This macro was implemented to optimize the case of calling `gvputs` with a string literal. It has since proven error prone to use.¹ The sharp edge is that it is possible to call the macro with something that is not a string literal: const char *hello = "hello world"; GVPUTS(job, hello); This will pass through the compiler with no warnings, but on e.g. x86-64 will result in a `gvputs` of `"hello wo"`. For safety, we remove it. This effectively reverts commit 1764f50bcbdf944e003fe475aba2c26e2fd370ff. The `GVPUTS` macro was originally introduced to optimize I/O resulting in a ~1% runtime improvement on tests/regression_tests/large/long_chain. That example should probably no longer be considered “expensive.” I/O is typically an insignificant fraction of the computation done by currently known expensive inputs that spend most of their time in layout or cycle resolution. If I/O _is_ a factor in an expensive graph, we could consider providing a vectorized interface in future in the style of `writev`. ¹ E.g. https://gitlab.com/graphviz/graphviz/-/merge_requests/2472#note_861834540 --- diff --git a/plugin/core/gvrender_core_svg.c b/plugin/core/gvrender_core_svg.c index 85e748b7e..97d9204b7 100644 --- a/plugin/core/gvrender_core_svg.c +++ b/plugin/core/gvrender_core_svg.c @@ -43,9 +43,6 @@ #define EDGEALIGN 0 #endif -// optimized gvputs for string literals -#define GVPUTS(job, str) gvwrite((job), (str), sizeof(str) - 1) - typedef enum { FORMAT_SVG, FORMAT_SVGZ, } format_type; /* SVG dash array */ @@ -65,7 +62,7 @@ static void svg_bzptarray(GVJ_t * job, pointf * A, int n) for (i = 0; i < n; i++) { gvwrite(job, &c, 1); gvprintdouble(job, A[i].x); - GVPUTS(job, ","); + gvputc(job, ','); gvprintdouble(job, -A[i].y); if (i == 0) c = 'C'; /* second point */ @@ -77,7 +74,7 @@ static void svg_bzptarray(GVJ_t * job, pointf * A, int n) for (i = n-1; i >= 0; i--) { gvwrite(job, &c, 1); gvprintdouble(job, A[i].x); - GVPUTS(job, ","); + gvputc(job, ','); gvprintdouble(job, -A[i].y); if (i == 0) c = 'C'; /* second point */ @@ -92,18 +89,18 @@ static void svg_print_id_class(GVJ_t * job, char* id, char* idx, char* kind, voi { char* str; - GVPUTS(job, "obj; - GVPUTS(job, " fill=\""); + gvputs(job, " fill=\""); if (filled == GRADIENT) { gvprintf(job, "url(#l_%d)", gid); } else if (filled == RGRADIENT) { @@ -141,12 +138,12 @@ static void svg_grstyle(GVJ_t * job, int filled, int gid) gvprintf(job, "\" fill-opacity=\"%f", ((float) obj->fillcolor.u.rgba[3] / 255.0)); } else { - GVPUTS(job, "none"); + gvputs(job, "none"); } - GVPUTS(job, "\" stroke=\""); + gvputs(job, "\" stroke=\""); svg_print_color(job, obj->pencolor); if (obj->penwidth != PENWIDTH_NORMAL) { - GVPUTS(job, "\" stroke-width=\""); + gvputs(job, "\" stroke-width=\""); gvprintdouble(job, obj->penwidth); } if (obj->pen == PEN_DASHED) { @@ -159,36 +156,36 @@ static void svg_grstyle(GVJ_t * job, int filled, int gid) gvprintf(job, "\" stroke-opacity=\"%f", ((float) obj->pencolor.u.rgba[3] / 255.0)); - GVPUTS(job, "\""); + gvputc(job, '"'); } static void svg_comment(GVJ_t * job, char *str) { - GVPUTS(job, "\n"); + gvputs(job, " -->\n"); } static void svg_begin_job(GVJ_t * job) { char *s; - GVPUTS(job, + gvputs(job, "\n"); if ((s = agget(job->gvc->g, "stylesheet")) && s[0]) { - GVPUTS(job, "\n"); + gvputs(job, "\" type=\"text/css\"?>\n"); } - GVPUTS(job, "\n" "\n"); } @@ -196,9 +193,9 @@ static void svg_begin_graph(GVJ_t * job) { obj_state_t *obj = job->obj; - GVPUTS(job, "\n", @@ -212,7 +209,7 @@ static void svg_begin_graph(GVJ_t * job) job->canvasBox.UR.x, job->canvasBox.UR.y); /* namespace of svg */ - GVPUTS(job, " xmlns=\"http://www.w3.org/2000/svg\"" + gvputs(job, " xmlns=\"http://www.w3.org/2000/svg\"" /* namespace of xlink */ " xmlns:xlink=\"http://www.w3.org/1999/xlink\"" ">\n"); @@ -220,7 +217,7 @@ static void svg_begin_graph(GVJ_t * job) static void svg_end_graph(GVJ_t * job) { - GVPUTS(job, "\n"); + gvputs(job, "\n"); } static void svg_begin_layer(GVJ_t * job, char *layername, int layerNum, @@ -232,12 +229,12 @@ static void svg_begin_layer(GVJ_t * job, char *layername, int layerNum, obj_state_t *obj = job->obj; svg_print_id_class(job, layername, NULL, "layer", obj->u.g); - GVPUTS(job, ">\n"); + gvputs(job, ">\n"); } static void svg_end_layer(GVJ_t * job) { - GVPUTS(job, "\n"); + gvputs(job, "\n"); } /* svg_begin_page: @@ -251,25 +248,25 @@ static void svg_begin_page(GVJ_t * job) /* its really just a page of the graph, but its still a graph, * and it is the entire graph if we're not currently paging */ svg_print_id_class(job, obj->id, NULL, "graph", obj->u.g); - GVPUTS(job, " transform=\"scale("); + gvputs(job, " transform=\"scale("); // cannot be gvprintdouble because 2 digits precision insufficient gvprintf(job, "%g %g", job->scale.x, job->scale.y); gvprintf(job, ") rotate(%d) translate(", -job->rotation); gvprintdouble(job, job->translation.x); - GVPUTS(job, " "); + gvputc(job, ' '); gvprintdouble(job, -job->translation.y); - GVPUTS(job, ")\">\n"); + gvputs(job, ")\">\n"); /* default style */ if (agnameof(obj->u.g)[0] && agnameof(obj->u.g)[0] != LOCALNAMEPREFIX) { - GVPUTS(job, ""); + gvputs(job, "<title>"); gvputs_xml(job, agnameof(obj->u.g)); - GVPUTS(job, "\n"); + gvputs(job, "\n"); } } static void svg_end_page(GVJ_t * job) { - GVPUTS(job, "\n"); + gvputs(job, "\n"); } static void svg_begin_cluster(GVJ_t * job) @@ -277,15 +274,15 @@ static void svg_begin_cluster(GVJ_t * job) obj_state_t *obj = job->obj; svg_print_id_class(job, obj->id, NULL, "cluster", obj->u.sg); - GVPUTS(job, ">\n" + gvputs(job, ">\n" ""); gvputs_xml(job, agnameof(obj->u.g)); - GVPUTS(job, "\n"); + gvputs(job, "\n"); } static void svg_end_cluster(GVJ_t * job) { - GVPUTS(job, "\n"); + gvputs(job, "\n"); } static void svg_begin_node(GVJ_t * job) @@ -298,15 +295,15 @@ static void svg_begin_node(GVJ_t * job) else idx = NULL; svg_print_id_class(job, obj->id, idx, "node", obj->u.n); - GVPUTS(job, ">\n" + gvputs(job, ">\n" ""); gvputs_xml(job, agnameof(obj->u.n)); - GVPUTS(job, "\n"); + gvputs(job, "\n"); } static void svg_end_node(GVJ_t * job) { - GVPUTS(job, "\n"); + gvputs(job, "\n"); } static void svg_begin_edge(GVJ_t * job) @@ -315,56 +312,56 @@ static void svg_begin_edge(GVJ_t * job) char *ename; svg_print_id_class(job, obj->id, NULL, "edge", obj->u.e); - GVPUTS(job, ">\n" + gvputs(job, ">\n" ""); ename = strdup_and_subst_obj("\\E", obj->u.e); gvputs_xml(job, ename); free(ename); - GVPUTS(job, "\n"); + gvputs(job, "\n"); } static void svg_end_edge(GVJ_t * job) { - GVPUTS(job, "\n"); + gvputs(job, "\n"); } static void svg_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target, char *id) { - GVPUTS(job, "" + gvputs(job, ">" "\n"); + gvputs(job, ">\n"); } static void svg_end_anchor(GVJ_t * job) { - GVPUTS(job, "\n" + gvputs(job, "\n" "\n"); } @@ -375,26 +372,26 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) char *family = NULL, *weight = NULL, *stretch = NULL, *style = NULL; unsigned int flags; - GVPUTS(job, "just) { case 'l': - GVPUTS(job, " text-anchor=\"start\""); + gvputs(job, " text-anchor=\"start\""); break; case 'r': - GVPUTS(job, " text-anchor=\"end\""); + gvputs(job, " text-anchor=\"end\""); break; default: case 'n': - GVPUTS(job, " text-anchor=\"middle\""); + gvputs(job, " text-anchor=\"middle\""); break; } p.y += span->yoffset_centerline; if (!obj->labeledgealigned) { - GVPUTS(job, " x=\""); + gvputs(job, " x=\""); gvprintdouble(job, p.x); - GVPUTS(job, "\" y=\""); + gvputs(job, "\" y=\""); gvprintdouble(job, -p.y); - GVPUTS(job, "\""); + gvputs(job, "\""); } pA = span->font->postscript_alias; if (pA) { @@ -421,7 +418,7 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) gvprintf(job, " font-family=\"%s", family); if (pA->svg_font_family) gvprintf(job, ",%s", pA->svg_font_family); - GVPUTS(job, "\""); + gvputc(job, '"'); if (weight) gvprintf(job, " font-weight=\"%s\"", weight); if (stretch) @@ -432,14 +429,14 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) gvprintf(job, " font-family=\"%s\"", span->font->name); if ((span->font) && (flags = span->font->flags)) { if ((flags & HTML_BF) && !weight) - GVPUTS(job, " font-weight=\"bold\""); + gvputs(job, " font-weight=\"bold\""); if ((flags & HTML_IF) && !style) - GVPUTS(job, " font-style=\"italic\""); + gvputs(job, " font-style=\"italic\""); if ((flags & (HTML_UL|HTML_S|HTML_OL))) { int comma = 0; - GVPUTS(job, " text-decoration=\""); + gvputs(job, " text-decoration=\""); if ((flags & HTML_UL)) { - GVPUTS(job, "underline"); + gvputs(job, "underline"); comma = 1; } if ((flags & HTML_OL)) { @@ -448,12 +445,12 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) } if ((flags & HTML_S)) gvprintf(job, "%sline-through", (comma?",":"")); - GVPUTS(job, "\""); + gvputc(job, '"'); } if ((flags & HTML_SUP)) - GVPUTS(job, " baseline-shift=\"super\""); + gvputs(job, " baseline-shift=\"super\""); if ((flags & HTML_SUB)) - GVPUTS(job, " baseline-shift=\"sub\""); + gvputs(job, " baseline-shift=\"sub\""); } gvprintf(job, " font-size=\"%.2f\"", span->font->size); @@ -472,19 +469,19 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) default: assert(0); /* internal error */ } - GVPUTS(job, ">"); + gvputc(job, '>'); if (obj->labeledgealigned) { - GVPUTS(job, "id); - GVPUTS(job, "_p\" startOffset=\"50%\">"); + gvputs(job, "\">"); } const xml_flags_t xml_flags = {.raw = 1, .dash = 1, .nbsp = 1}; xml_escape(span->str, xml_flags, (int(*)(void*, const char*))gvputs, job); if (obj->labeledgealigned) - GVPUTS(job, ""); - GVPUTS(job, "\n"); + gvputs(job, ""); + gvputs(job, "\n"); } /* svg_gradstyle @@ -503,39 +500,39 @@ static int svg_gradstyle(GVJ_t * job, pointf * A, int n) gvprintf(job, "\n\n"); + gvputs(job, "\" >\n"); if (obj->gradient_frac > 0) gvprintf(job, "gradient_frac - 0.001); else - GVPUTS(job, "fillcolor); - GVPUTS(job, ";stop-opacity:"); + gvputs(job, ";stop-opacity:"); if (obj->fillcolor.type == RGBA_BYTE && obj->fillcolor.u.rgba[3] > 0 && obj->fillcolor.u.rgba[3] < 255) gvprintf(job, "%f", ((float) obj->fillcolor.u.rgba[3] / 255.0)); else - GVPUTS(job, "1."); - GVPUTS(job, ";\"/>\n"); + gvputs(job, "1."); + gvputs(job, ";\"/>\n"); if (obj->gradient_frac > 0) gvprintf(job, "gradient_frac); else - GVPUTS(job, "stopcolor); - GVPUTS(job, ";stop-opacity:"); + gvputs(job, ";stop-opacity:"); if (obj->stopcolor.type == RGBA_BYTE && obj->stopcolor.u.rgba[3] > 0 && obj->stopcolor.u.rgba[3] < 255) gvprintf(job, "%f", ((float) obj->stopcolor.u.rgba[3] / 255.0)); else - GVPUTS(job, "1."); - GVPUTS(job, ";\"/>\n\n\n"); + gvputs(job, "1."); + gvputs(job, ";\"/>\n\n\n"); return id; } @@ -560,24 +557,24 @@ static int svg_rgradstyle(GVJ_t * job) "\n\n", id, ifx, ify); - GVPUTS(job, "fillcolor); - GVPUTS(job, ";stop-opacity:"); + gvputs(job, ";stop-opacity:"); if (obj->fillcolor.type == RGBA_BYTE && obj->fillcolor.u.rgba[3] > 0 && obj->fillcolor.u.rgba[3] < 255) gvprintf(job, "%f", ((float) obj->fillcolor.u.rgba[3] / 255.0)); else - GVPUTS(job, "1."); - GVPUTS(job, ";\"/>\n" + gvputs(job, "1."); + gvputs(job, ";\"/>\n" "stopcolor); - GVPUTS(job, ";stop-opacity:"); + gvputs(job, ";stop-opacity:"); if (obj->stopcolor.type == RGBA_BYTE && obj->stopcolor.u.rgba[3] > 0 && obj->stopcolor.u.rgba[3] < 255) gvprintf(job, "%f", ((float) obj->stopcolor.u.rgba[3] / 255.0)); else - GVPUTS(job, "1."); - GVPUTS(job, ";\"/>\n\n\n"); + gvputs(job, "1."); + gvputs(job, ";\"/>\n\n\n"); return id; } @@ -592,17 +589,17 @@ static void svg_ellipse(GVJ_t * job, pointf * A, int filled) } else if (filled == (RGRADIENT)) { gid = svg_rgradstyle(job); } - GVPUTS(job, "\n"); + gvputs(job, "\"/>\n"); } static void @@ -620,16 +617,16 @@ svg_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start, } else if (filled == (RGRADIENT)) { gid = svg_rgradstyle(job); } - GVPUTS(job, "labeledgealigned) { - GVPUTS(job, " id=\""); + gvputs(job, " id=\""); gvputs_xml(job, obj->id); - GVPUTS(job, "_p\" "); + gvputs(job, "_p\" "); } svg_grstyle(job, filled, gid); - GVPUTS(job, " d=\""); + gvputs(job, " d=\""); svg_bzptarray(job, A, n); - GVPUTS(job, "\"/>\n"); + gvputs(job, "\"/>\n"); } static void svg_polygon(GVJ_t * job, pointf * A, int n, int filled) @@ -640,36 +637,36 @@ static void svg_polygon(GVJ_t * job, pointf * A, int n, int filled) } else if (filled == (RGRADIENT)) { gid = svg_rgradstyle(job); } - GVPUTS(job, "\n"); + gvputs(job, "\"/>\n"); } static void svg_polyline(GVJ_t * job, pointf * A, int n) { int i; - GVPUTS(job, "\n"); + gvputs(job, "\"/>\n"); } /* color names from http://www.w3.org/TR/SVG/types.html */