Magnus Jacobsson [Sat, 20 Aug 2022 16:12:54 +0000 (18:12 +0200)]
tests: SVGAnalyzer: add 're_create_and_verify_svg' method and use in test_svg_analyzer
Upcoming commits will extend the SVG analyzer's ability to handle new
SVG attributes and add new test cases verifying its ability to
re-create correct SVG containing those attributes. This change will
facilitate the creation of those tests and keep the code DRY.
Magnus Jacobsson [Sat, 20 Aug 2022 16:12:54 +0000 (18:12 +0200)]
tests: SVGAnalyzer: extend 'make_from_dot' method and use in test_svg_analyzer
This change adds support to the SVGAnalyzer for saving a
copy of the Graphviz version and build ID which are needed to exactly
re-create the SVG document.
Upcoming commits will extend the SVG analyzer's ability to handle new
SVG attributes and add new test cases verifying its ability to
re-create correct SVG containing those attributes. This change will
facilitate the creation of those tests and keep the code DRY.
tests: SVGAnalyzer: save a copy of the original SVG and give access to it
This is needed to be able to verify the SVG re-creating capablility of
the SVGAnalyzer.
Upcoming commits will extend the SVG analyzer's ability to handle new
SVG attributes and add new test cases verifying its ability to
re-create correct SVG containing those attributes. This change will
facilitate the creation of those tests and keep the code DRY.
The copying is necessary since the original SVG is modified by the XML
parser in destructive mode. See
http://rapidxml.sourceforge.net/manual.html#namespacerapidxml_1destructive_non_destructive
and we don't want to use the non-destructive mode since no entity
reference translation is done then and we want to avoid having to do
that ourselves.
Magnus Jacobsson [Tue, 23 Aug 2022 19:09:44 +0000 (21:09 +0200)]
tests: test_rankdir: remove useless generation of copy
The generator expression will not outlive the scope of
'node_shapes_without_svg_shape' or 'all_node_shapes' which both live
in the global scope, so copying is unnecessary.
For details, see
https://github.com/catchorg/Catch2/blob/devel/docs/generators.md#provided-generators.
gvpr: fix: wrap members needing cleanup in a struct and outline
When seeing this code, the compiler emitted numerous warnings like:
gvpr.c: In function ‘gvpr’:
gvpr.c:929:17: warning: variable ‘prog’ might be clobbered by
‘longjmp’ or ‘vfork’ [-Wclobbered]
parse_prog *prog = 0;
^~~~
What it is trying to tell us is that this code violates the assumptions of
`setjmp`/`longjmp`. Referencing any non-volatile stack-allocated variable after
a `longjmp` is undefined behavior.¹
This change extracts all the members that need to be referenced after `longjmp`
into a wrapper function. This basically puts a function call boundary in a place
such that the code no longer violates assumptions of the C standard. It is a
little awkward that we have to do this by hand, as the transformation is
mechanical and the compiler technically has enough information to do this for
us. But `setjmp`/`longjmp` come from the old world and are low level mechanisms.
If the compiler sees a call to them, it assumes you are doing something unusual
and want to do it exactly as you wrote.
While making this adjustment, some white space is adjusted and some coercion to
boolean is changed to explicit comparisons against `NULL`.
I do not understand why, but after this change the compiler still believes
`nextg` can be clobbered by a `longjmp`. While this is true, `nextg` is not used
after `longjmp`-ing back to `gvpr_core`, so I do not know why the compiler
thinks it needs to warn us about this.
Gitlab: #1801
¹ This restriction stems from how `setjmp`/`longjmp` work on certain platforms.
If a variable is live in a register instead of on the stack, it is not
guaranteed to be restored during a `longjmp`. Hence why the value of such
variables is undefined after a `longjmp`.
gxl2gv endElementHandler: use cgraph wrapper for allocation
The lib/cgraph/alloc.h wrappers are similar to the older lib/common/memory.h
wrappers except (1) they are header-only and (2) they live in a directory
(cgraph) that is at the root of the dependency tree. The long term plan is to
replace all use of lib/common/memory.h with lib/cgraph/alloc.h.
mm2gv SparseMatrix_import_matrix_market: use cgraph wrappers for allocation
The lib/cgraph/alloc.h wrappers are similar to the older lib/common/memory.h
wrappers except (1) they are header-only and (2) they live in a directory
(cgraph) that is at the root of the dependency tree. The long term plan is to
replace all use of lib/common/memory.h with lib/cgraph/alloc.h.
This change also fixes a number of unchecked allocation failures where `malloc`
was being called directly and the result was not checked.
cgraph memresize: fix out-of-bounds write on allocation failure
Callers of this function appear to anticipate the possibility of failure, e.g.
`agrealloc`. But the function itself was attempting to zero newly allocated
memory even if the allocation call failed.
It is not clear to me exactly what “warnings about initialization” were being
suppressed here, but I am assuming something well before C99. In C99, we have a
simpler and more concise way of doing zero initialization.
This caused no ill-effects because defining these within Smyrna is a no-op. All
use of these macros occurs outside cmd/smyrna, in the libraries on which Smyrna
depends. But defining these unused macros was creating unnecessary confusion.
fix 'id' attribute in SVG output omitting input graph id
When using an ID attribute on the input graph,¹ this attribute would only be
propagated to some output elements. In particular, generated `linearGradient`
and `radialGradient` elements in SVGs would be missing the ID.
use 'agxbprint' to round points instead of casting in 'Show_boxes'
This has the advantage of avoiding relying on values fitting in the range of an
int. Going through C stdio this way, the full range of doubles can be preserved.
use agxbbuf for dynamic 'Show_boxes' string construction
We have some recent evidence¹ people are using this debug feature. This change
makes some allocations it does more robust (they now exit on failure) and
abbreviates construction in a stack buffer and then duplication into a heap
buffer into direct construction in a heap buffer.
pathplan Pshortestpath: use longs instead of ints for index variables
Within this function, these indices are sometimes calculated using pointer
arithmetic which has a long (technically `ptrdiff_t`) result. Using a long type
for these variables squashes 2 -Wconversion warnings and reduces the likelihood
of arithmetic overflows.
In future, it is possible we should convert this code to `ptrdiff_t` instead. It
is unclear if this would be an improvement, given interaction with the
surrounding code and cases where these indices are set to literals like `-1`.
Changing to `ptrdiff_t` would also be more invasive than just widening the value
range as done by this change. On most contemporary platforms, `long` and
`ptrdiff_t` are the same type, so perhaps the difference is academic.
sparse: remove always true branch in 'SparseMatrix_sum_repeat_entries'
`what_to_sum` can only take on two values, `SUM_REPEATED_NONE` and
`SUM_REPEATED_ALL`. If it is `SUM_REPEATED_NONE` there is an early exit at the
beginning of this function.
API BREAK: libxdot: use size_t instead of int for stats and polygon line points
This allows exceeding counts of 2³¹ - 1, which is not particularly likely. But
its more important effect is to make arithmetic operations less error prone and
make it impossible by-construction to arrive at a negative count in these fields.
There is some interaction between the stats fields and polygon line points
fields, so doing these two separately would have meant an intermediate state
with lots of casting. It seemed simpler and less error prone to do this all at
once.
There is still casting back to int for the `gvrender_*` functions. Modifying
these would have been too invasive. Surprisingly the call to `gvrender_polyline`
affected in this commit is the only instance of a call to it with a non-literal
point count. But modifying it to take a size_t would still have required
too-invasive changes due to other functions it calls.
For lines that would have been changed anyway, this commit also fixes white
space and tightens variable scoping where possible.
API BREAK: libxdot: use size_t instead of int for op sizes/counts
This allows exceeding operation counts of 2³¹ - 1, which is not particularly
likely. But its more important effect is to make arithmetic operations less
error prone and make it impossible by-construction to arrive at a negative
operation size or count.
Doing this as a single sweeping change seemed less error prone and clearer,
despite how large the resulting diff is.
For lines that would have been changed anyway, this commit also fixes white
space and tightens variable scoping where possible.