Skip to content

Commit 4683ebd

Browse files
committed
feat: Add acyclic and tred support
Refactor graphviz to common up buffer usage Fixed typos in javadoc Added some simple tests for async and tred Fixed error reporting for unflatten Fixes #210 Signed-off-by: Gordon Smith <[email protected]>
1 parent be35fa4 commit 4683ebd

File tree

9 files changed

+231
-167
lines changed

9 files changed

+231
-167
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,4 @@
172172
"url": "https://github.com/hpcc-systems/hpcc-js-wasm/issues"
173173
},
174174
"homepage": "https://hpcc-systems.github.io/hpcc-js-wasm/"
175-
}
175+
}

src-cpp/graphviz/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ set_property(SOURCE main.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINA
3232
# --- --- ---
3333

3434
set(SRCS
35+
main.hpp
3536
main.cpp
37+
util.hpp
38+
util.cpp
3639
)
3740

3841
include_directories(

src-cpp/graphviz/main.cpp

Lines changed: 48 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ Graphviz::Graphviz(int yInvert, int nop)
6767
{
6868
Y_invert = yInvert > 0 ? yInvert : origYInvert;
6969
Nop = nop > 0 ? nop : origNop;
70+
71+
lastErrorStr[0] = '\0';
72+
agseterr(AGERR);
73+
agseterrf(vizErrorf);
74+
agreadline(1);
7075
}
7176

7277
Graphviz::~Graphviz()
@@ -86,127 +91,84 @@ void Graphviz::createFile(const char *path, const char *data)
8691
path, data);
8792
}
8893

89-
const char *Graphviz::lastResult()
90-
{
91-
return m_result.c_str();
92-
}
93-
9494
const char *Graphviz::layout(const char *src, const char *format, const char *engine)
9595
{
96-
lastErrorStr[0] = '\0';
97-
m_result = "";
98-
99-
// const auto demand_loading = false;
100-
// auto gvc = std::make_shared<GVC::GVContext>(lt_preloaded_symbols, demand_loading);
101-
// auto g = std::make_shared<CGraph::AGraph>(dot);
102-
// const auto layout = GVC::GVLayout(gvc, g, engine);
103-
// const auto result = layout.render(format);
104-
// m_result = result.string_view();
105-
106-
// return m_result.c_str();
96+
layout_result = "";
10797

10898
GVC_t *gvc = gvContextPlugins(lt_preloaded_symbols, true);
10999

110-
agseterr(AGERR);
111-
agseterrf(vizErrorf);
112-
113-
agreadline(1);
114-
115-
Agraph_t *graph;
116-
char *data = NULL;
117-
unsigned int length;
118-
while ((graph = agmemread(src)))
100+
Agraph_t *graph = agmemread(src);
101+
if (graph)
119102
{
120-
if (data == NULL)
121-
{
122-
gvLayout(gvc, graph, engine);
123-
gvRenderData(gvc, graph, format, &data, &length);
124-
gvFreeLayout(gvc, graph);
125-
}
126-
103+
char *data = NULL;
104+
unsigned int length;
105+
106+
gvLayout(gvc, graph, engine);
107+
gvRenderData(gvc, graph, format, &data, &length);
108+
layout_result = data;
109+
gvFreeRenderData(data);
110+
gvFreeLayout(gvc, graph);
127111
agclose(graph);
128-
129-
src = "";
130112
}
131-
m_result = data;
132-
gvFreeRenderData(data);
133113

134114
gvFinalize(gvc);
135115
gvFreeContext(gvc);
136116

137-
return m_result.c_str();
117+
return layout_result;
138118
}
139119

140120
bool Graphviz::acyclic(const char *src, bool doWrite, bool verbose)
141121
{
142-
lastErrorStr[0] = '\0';
143-
acyclic_outFile_buff = "";
144-
acyclic_outFile = acyclic_outFile_buff.c_str();
122+
acyclic_outFile = "";
145123
acyclic_num_rev = 0;
146124
bool retVal = false;
147-
Agraph_t *g = agmemread(src);
148-
if (g)
125+
126+
Agraph_t *graph = agmemread(src);
127+
if (graph)
149128
{
150-
FILE *outFile = fopen("tmp.dot", "w");
129+
TempFileBuffer outFile;
151130
graphviz_acyclic_options_t opts = {outFile, doWrite, verbose};
152-
retVal = graphviz_acyclic(g, &opts, &acyclic_num_rev);
153-
fclose(outFile);
154-
std::ifstream file("tmp.dot");
155-
std::string graph_str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
156-
remove("tmp.dot");
157-
acyclic_outFile_buff = graph_str;
158-
acyclic_outFile = acyclic_outFile_buff.c_str();
131+
retVal = graphviz_acyclic(graph, &opts, &acyclic_num_rev);
132+
acyclic_outFile = outFile;
133+
agclose(graph);
159134
}
160135
return retVal;
161136
}
162137

163138
void Graphviz::tred(const char *src, bool verbose, bool printRemovedEdges)
164139
{
165-
lastErrorStr[0] = '\0';
166-
tred_out_buff = "";
167-
tred_out = tred_out_buff.c_str();
168-
tred_err_buff = "";
169-
tred_err = tred_err_buff.c_str();
170-
Agraph_t *g = agmemread(src);
171-
if (g)
140+
tred_out = "";
141+
tred_err = "";
142+
143+
Agraph_t *graph = agmemread(src);
144+
if (graph)
172145
{
173-
FILE *outFile = fopen("tmp.dot", "w");
174-
FILE *errFile = fopen("tmp_err.dot", "w");
175-
graphviz_tred_options_t opts = {verbose, printRemovedEdges, outFile, errFile};
176-
graphviz_tred(g, &opts);
177-
fclose(outFile);
178-
std::ifstream file("tmp.dot");
179-
std::string outFile_str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
180-
remove("tmp.dot");
181-
tred_out_buff = outFile_str;
182-
tred_out = tred_out_buff.c_str();
183-
std::ifstream file_err("tmp_err.dot");
184-
std::string errFile_str((std::istreambuf_iterator<char>(file_err)), std::istreambuf_iterator<char>());
185-
remove("tmp_err.dot");
186-
tred_err_buff = errFile_str;
187-
tred_err = tred_err_buff.c_str();
146+
TempFileBuffer out;
147+
TempFileBuffer err;
148+
graphviz_tred_options_t opts = {verbose, printRemovedEdges, out, err};
149+
graphviz_tred(graph, &opts);
150+
tred_out = out;
151+
tred_err = err;
152+
agclose(graph);
188153
}
189154
}
190155

191156
const char *Graphviz::unflatten(const char *src, int maxMinlen, bool do_fans, int chainLimit)
192157
{
193-
lastErrorStr[0] = '\0';
194-
m_result = "";
195-
Agraph_t *g = agmemread(src);
196-
if (g)
158+
unflatten_out = "";
159+
160+
Agraph_t *graph = agmemread(src);
161+
if (graph)
197162
{
198163
graphviz_unflatten_options_t opts = {do_fans, maxMinlen, chainLimit};
199-
graphviz_unflatten(g, &opts);
200-
201-
FILE *fp = fopen("tmp.dot", "w");
202-
agwrite(g, fp);
203-
fclose(fp);
204-
std::ifstream file("tmp.dot");
205-
std::string graph_str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
206-
remove("tmp.dot");
207-
m_result = graph_str;
164+
graphviz_unflatten(graph, &opts);
165+
166+
TempFileBuffer tempFile;
167+
agwrite(graph, tempFile);
168+
unflatten_out = tempFile;
169+
agclose(graph);
208170
}
209-
return m_result.c_str();
171+
return unflatten_out;
210172
}
211173

212174
// Include JS Glue ---

src-cpp/graphviz/main.hpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
#include <string>
2+
#include "util.hpp"
23

34
class Graphviz
45
{
56
private:
6-
std::string m_result;
7-
87
public:
98
static const char *version();
109
static const char *lastError();
@@ -13,19 +12,18 @@ class Graphviz
1312
~Graphviz();
1413

1514
void createFile(const char *path, const char *data);
16-
const char *lastResult();
15+
16+
StringBuffer layout_result;
1717
const char *layout(const char *dot, const char *format, const char *engine);
1818

19-
std::string acyclic_outFile_buff;
20-
const char *acyclic_outFile;
19+
StringBuffer acyclic_outFile;
2120
size_t acyclic_num_rev;
2221
bool acyclic(const char *dot, bool doWrite = false, bool verbose = false);
2322

24-
std::string tred_out_buff;
25-
const char *tred_out;
26-
std::string tred_err_buff;
27-
const char *tred_err;
23+
StringBuffer tred_out;
24+
StringBuffer tred_err;
2825
void tred(const char *dot, bool verbose = false, bool printRemovedEdges = false);
2926

27+
StringBuffer unflatten_out;
3028
const char *unflatten(const char *dot, int maxMinlen = 0, bool do_fans = false, int chainLimit = 0);
3129
};

src-cpp/graphviz/main.idl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ interface Graphviz
44
[Const] static DOMString version();
55
[Const] static DOMString lastError();
66
void createFile([Const] DOMString file, [Const] DOMString data);
7-
[Const] DOMString lastResult();
7+
8+
[Const] attribute DOMString layout_result;
89
[Const] DOMString layout([Const] DOMString dot, [Const] DOMString format, [Const] DOMString engine);
910

1011
[Const] attribute DOMString acyclic_outFile;

src-cpp/graphviz/util.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "util.hpp"
2+
3+
StringBuffer::operator std::string() const
4+
{
5+
return m_buffer;
6+
}
7+
8+
StringBuffer::operator const char *() const
9+
{
10+
return m_buffer.c_str();
11+
}
12+
13+
StringBuffer &StringBuffer::operator=(const std::string &str)
14+
{
15+
m_buffer = str;
16+
return *this;
17+
}
18+
19+
TempFileBuffer::TempFileBuffer()
20+
{
21+
if (std::tmpnam(tempFileName) == nullptr)
22+
throw std::runtime_error("Failed to generate a unique temporary file name.");
23+
24+
filePointer = std::fopen(tempFileName, "w+");
25+
if (filePointer == nullptr)
26+
throw std::runtime_error("Failed to open temporary file for writing.");
27+
}
28+
29+
TempFileBuffer::~TempFileBuffer()
30+
{
31+
if (filePointer != nullptr)
32+
{
33+
std::fclose(filePointer);
34+
std::remove(tempFileName);
35+
}
36+
}
37+
38+
TempFileBuffer::operator FILE *() const
39+
{
40+
return filePointer;
41+
}
42+
43+
TempFileBuffer::operator std::string() const
44+
{
45+
std::string content;
46+
if (filePointer != nullptr)
47+
{
48+
std::rewind(filePointer);
49+
char buffer[256];
50+
size_t bytesRead;
51+
while ((bytesRead = std::fread(buffer, 1, sizeof(buffer), filePointer)) > 0)
52+
content.append(buffer, bytesRead);
53+
}
54+
return content;
55+
}

src-cpp/graphviz/util.hpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
class StringBuffer
6+
{
7+
private:
8+
std::string m_buffer;
9+
10+
public:
11+
operator std::string() const;
12+
operator const char *() const;
13+
StringBuffer &operator=(const std::string &str);
14+
};
15+
16+
class TempFileBuffer
17+
{
18+
private:
19+
FILE *filePointer = nullptr;
20+
char tempFileName[L_tmpnam];
21+
22+
public:
23+
TempFileBuffer();
24+
~TempFileBuffer();
25+
26+
operator FILE *() const;
27+
operator std::string() const;
28+
operator const char *() const;
29+
};

0 commit comments

Comments
 (0)