improved dot
This commit is contained in:
parent
38893379d8
commit
7c4789e71d
2 changed files with 198 additions and 183 deletions
|
@ -169,12 +169,18 @@ shuffle(int *array, size_t n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TEST_ARRAY_SIZE 50
|
#define TEST_ARRAY_SIZE 5
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int gen = 0, rc = 0;
|
||||||
|
|
||||||
|
FILE *of = fopen("/tmp/slm.dot", "w");
|
||||||
|
if (!of) {
|
||||||
|
perror("Failed to open file /tmp/slm.dot");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate and initialize a Skiplist. */
|
/* Allocate and initialize a Skiplist. */
|
||||||
slex_t *list = (slex_t *)malloc(sizeof(slex_t));
|
slex_t *list = (slex_t *)malloc(sizeof(slex_t));
|
||||||
|
@ -184,6 +190,7 @@ main()
|
||||||
rc = api_skip_init_slex(list, 12, __slm_key_compare);
|
rc = api_skip_init_slex(list, 12, __slm_key_compare);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
|
||||||
/* Test creating a snapshot of an empty Skiplist */
|
/* Test creating a snapshot of an empty Skiplist */
|
||||||
slex_snap_t *snap = api_skip_snapshot_slex(list);
|
slex_snap_t *snap = api_skip_snapshot_slex(list);
|
||||||
|
@ -200,13 +207,17 @@ main()
|
||||||
|
|
||||||
for (int i = 0; i < asz; i++) {
|
for (int i = 0; i < asz; i++) {
|
||||||
rc = api_skip_put_slex(list, array[i], to_lower(int_to_roman_numeral(array[i])));
|
rc = api_skip_put_slex(list, array[i], to_lower(int_to_roman_numeral(array[i])));
|
||||||
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
char *v = api_skip_get_slex(list, array[i]);
|
char *v = api_skip_get_slex(list, array[i]);
|
||||||
api_skip_set_slex(list, array[i], to_upper(v));
|
api_skip_set_slex(list, array[i], to_upper(v));
|
||||||
}
|
}
|
||||||
api_skip_dup_slex(list, -1, int_to_roman_numeral(-1));
|
api_skip_dup_slex(list, -1, int_to_roman_numeral(-1));
|
||||||
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
api_skip_dup_slex(list, 1, int_to_roman_numeral(1));
|
api_skip_dup_slex(list, 1, int_to_roman_numeral(1));
|
||||||
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
|
||||||
api_skip_del_slex(list, 0);
|
api_skip_del_slex(list, 0);
|
||||||
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
snap = api_skip_snapshot_slex(list);
|
snap = api_skip_snapshot_slex(list);
|
||||||
|
@ -239,15 +250,8 @@ main()
|
||||||
assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, 2)->value, int_to_roman_numeral(2)) == 0);
|
assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, 2)->value, int_to_roman_numeral(2)) == 0);
|
||||||
assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, (TEST_ARRAY_SIZE + 1))->value, int_to_roman_numeral(TEST_ARRAY_SIZE)) == 0);
|
assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, (TEST_ARRAY_SIZE + 1))->value, int_to_roman_numeral(TEST_ARRAY_SIZE)) == 0);
|
||||||
|
|
||||||
FILE *of = fopen("/tmp/slm.dot", "w");
|
|
||||||
if (!of) {
|
|
||||||
perror("Failed to open file /tmp/slm.dot");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
api_skip_dot_slex(of, list, 0, sprintf_slex_node);
|
|
||||||
fclose(of);
|
|
||||||
|
|
||||||
api_skip_destroy_slex(list);
|
api_skip_destroy_slex(list);
|
||||||
|
api_skip_dot_end_slex(of, gen);
|
||||||
|
fclose(of);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
111
include/sl.h
111
include/sl.h
|
@ -1006,17 +1006,38 @@
|
||||||
/* -- __skip_dot_node_ \
|
/* -- __skip_dot_node_ \
|
||||||
* Writes out a fragment of a DOT file representing a node. \
|
* Writes out a fragment of a DOT file representing a node. \
|
||||||
*/ \
|
*/ \
|
||||||
|
static size_t __skip_dot_width_##decl(decl##_t *slist, decl##_node_t *node, size_t level) \
|
||||||
|
{ \
|
||||||
|
size_t w = 0; \
|
||||||
|
decl##_node_t *n; \
|
||||||
|
\
|
||||||
|
if (node == slist->slh_tail) \
|
||||||
|
return 0; \
|
||||||
|
n = node->field.sle.next[level]; \
|
||||||
|
\
|
||||||
|
do { \
|
||||||
|
w++; \
|
||||||
|
n = prefix##skip_prev_node_##decl(slist, n); \
|
||||||
|
} while (n && n->field.sle.prev != node); \
|
||||||
|
\
|
||||||
|
return --w; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* -- __skip_dot_node_ \
|
||||||
|
* Writes out a fragment of a DOT file representing a node. \
|
||||||
|
*/ \
|
||||||
static void __skip_dot_node_##decl(FILE *os, decl##_t *slist, decl##_node_t *node, size_t nsg, skip_sprintf_node_##decl##_t fn) \
|
static void __skip_dot_node_##decl(FILE *os, decl##_t *slist, decl##_node_t *node, size_t nsg, skip_sprintf_node_##decl##_t fn) \
|
||||||
{ \
|
{ \
|
||||||
char buf[2048]; \
|
char buf[2048]; \
|
||||||
size_t level, height = node->field.sle.len; \
|
size_t level, width, height = node->field.sle.len; \
|
||||||
decl##_node_t *next; \
|
decl##_node_t *next; \
|
||||||
\
|
\
|
||||||
fprintf(os, "\"node%zu %p\"", nsg, (void *)node); \
|
fprintf(os, "\"node%zu %p\"", nsg, (void *)node); \
|
||||||
fprintf(os, " [label = \""); \
|
fprintf(os, " [label = \""); \
|
||||||
level = height; \
|
level = height; \
|
||||||
while (level--) { \
|
while (level--) { \
|
||||||
fprintf(os, " { <w%zu> | <f%zu> %p } |", level, level, (void *)node->field.sle.next[level]); \
|
width = __skip_dot_width_##decl(slist, node, level); \
|
||||||
|
fprintf(os, " { <w%zu> %zu | <f%zu> %p } |", level, width, level, (void *)node->field.sle.next[level]); \
|
||||||
} \
|
} \
|
||||||
if (fn) { \
|
if (fn) { \
|
||||||
fn(node, buf); \
|
fn(node, buf); \
|
||||||
|
@ -1040,10 +1061,10 @@
|
||||||
__skip_dot_node_##decl(os, slist, next, nsg, fn); \
|
__skip_dot_node_##decl(os, slist, next, nsg, fn); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/* -- __skip_dot_finish_ \
|
/* -- _skip_dot_finish_ \
|
||||||
* Finalize the DOT file of the internal representation. \
|
* Finalize the DOT file of the internal representation. \
|
||||||
*/ \
|
*/ \
|
||||||
static void __skip_dot_finish_##decl(FILE *os, size_t nsg) \
|
void prefix##skip_dot_end_##decl(FILE *os, size_t nsg) \
|
||||||
{ \
|
{ \
|
||||||
size_t i; \
|
size_t i; \
|
||||||
if (nsg > 0) { \
|
if (nsg > 0) { \
|
||||||
|
@ -1070,11 +1091,29 @@
|
||||||
fprintf(os, "}\n"); \
|
fprintf(os, "}\n"); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/* -- skip_dot_start_ */ \
|
/* -- skip_dot_ \
|
||||||
static int __skip_dot_start_##decl(FILE *os, decl##_t *slist, size_t nsg, skip_sprintf_node_##decl##_t fn) \
|
* Create a DOT file of the internal representation of the \
|
||||||
|
* Skiplist on the provided file descriptor (default: STDOUT). \
|
||||||
|
* \
|
||||||
|
* To view the output: \
|
||||||
|
* $ dot -Tps filename.dot -o outfile.ps \
|
||||||
|
* You can change the output format by varying the value after -T and \
|
||||||
|
* choosing an appropriate filename extension after -o. \
|
||||||
|
* See: https://graphviz.org/docs/outputs/ for the format options. \
|
||||||
|
* \
|
||||||
|
* https://en.wikipedia.org/wiki/DOT_(graph_description_language) \
|
||||||
|
*/ \
|
||||||
|
int prefix##skip_dot_##decl(FILE *os, decl##_t *slist, size_t nsg, skip_sprintf_node_##decl##_t fn) \
|
||||||
{ \
|
{ \
|
||||||
size_t level; \
|
size_t width, level; \
|
||||||
decl##_node_t *head, *tail; \
|
decl##_node_t *node, *next; \
|
||||||
|
\
|
||||||
|
if (slist == NULL || fn == NULL) \
|
||||||
|
return nsg; \
|
||||||
|
if (__skip_integrity_check_##decl(slist) != 0) { \
|
||||||
|
perror("Skiplist failed integrity checks, impossible to diagram."); \
|
||||||
|
return -1; \
|
||||||
|
} \
|
||||||
if (nsg == 0) { \
|
if (nsg == 0) { \
|
||||||
fprintf(os, "digraph Skiplist {\n"); \
|
fprintf(os, "digraph Skiplist {\n"); \
|
||||||
fprintf(os, "label = \"Skiplist.\"\n"); \
|
fprintf(os, "label = \"Skiplist.\"\n"); \
|
||||||
|
@ -1089,24 +1128,25 @@
|
||||||
fprintf(os, "label = \""); \
|
fprintf(os, "label = \""); \
|
||||||
\
|
\
|
||||||
/* Write out the fields */ \
|
/* Write out the fields */ \
|
||||||
head = slist->slh_head; \
|
if (prefix##skip_size_##decl(slist) == 0) \
|
||||||
if (SKIP_EMPTY(slist)) \
|
|
||||||
fprintf(os, "Empty HeadNode"); \
|
fprintf(os, "Empty HeadNode"); \
|
||||||
else { \
|
else { \
|
||||||
level = head->field.sle.len - 1; \
|
node = slist->slh_head->field.sle.next[0]; \
|
||||||
|
level = slist->slh_head->field.sle.len; \
|
||||||
|
next = node->field.sle.next[level] == NULL ? slist->slh_tail : node->field.sle.next[level]; \
|
||||||
do { \
|
do { \
|
||||||
decl##_node_t *node = head->field.sle.next[level]; \
|
width = __skip_dot_width_##decl(slist, node, level); \
|
||||||
fprintf(os, "{ <f%zu> %p }", level, (void *)node); \
|
fprintf(os, "{ %zu | <f%zu> %p }", width, level, (void *)next); \
|
||||||
if (level && head->field.sle.next[level] != slist->slh_tail) \
|
if (level) \
|
||||||
fprintf(os, " | "); \
|
fprintf(os, " | "); \
|
||||||
} while (level-- && head->field.sle.next[level] != slist->slh_tail); \
|
} while (level--); \
|
||||||
} \
|
} \
|
||||||
fprintf(os, "\"\n"); \
|
fprintf(os, "\"\n"); \
|
||||||
fprintf(os, "shape = \"record\"\n"); \
|
fprintf(os, "shape = \"record\"\n"); \
|
||||||
fprintf(os, "];\n"); \
|
fprintf(os, "];\n"); \
|
||||||
\
|
\
|
||||||
/* Edges for head node */ \
|
/* Edges for head node */ \
|
||||||
decl##_node_t *node = slist->slh_head; \
|
node = slist->slh_head; \
|
||||||
for (level = 0; level < slist->slh_head->field.sle.len; level++) { \
|
for (level = 0; level < slist->slh_head->field.sle.len; level++) { \
|
||||||
if (node->field.sle.next[level] == slist->slh_tail) \
|
if (node->field.sle.next[level] == slist->slh_tail) \
|
||||||
break; \
|
break; \
|
||||||
|
@ -1123,15 +1163,15 @@
|
||||||
fprintf(os, "\n"); \
|
fprintf(os, "\n"); \
|
||||||
\
|
\
|
||||||
/* The tail, sentinal node */ \
|
/* The tail, sentinal node */ \
|
||||||
tail = slist->slh_tail; \
|
node = slist->slh_tail; \
|
||||||
if (!SKIP_EMPTY(slist)) { \
|
if (prefix##skip_size_##decl(slist) > 0) { \
|
||||||
fprintf(os, "\"node%zu %p\" [label = \"", nsg, (void *)slist->slh_tail); \
|
fprintf(os, "\"node%zu %p\" [label = \"", nsg, (void *)slist->slh_tail); \
|
||||||
level = tail->field.sle.len - 1; \
|
level = node->field.sle.len; \
|
||||||
do { \
|
do { \
|
||||||
fprintf(os, "<w%zu> %p", level, (void *)node->field.sle.prev); \
|
fprintf(os, "<w%zu> %p", level, (void *)node); \
|
||||||
if (level && node->field.sle.prev != slist->slh_head) \
|
if (level && node->field.sle.prev != slist->slh_head) \
|
||||||
fprintf(os, " | "); \
|
fprintf(os, " | "); \
|
||||||
} while (level-- && node->field.sle.prev != slist->slh_head); \
|
} while (level--); \
|
||||||
fprintf(os, "\" shape = \"record\"];\n"); \
|
fprintf(os, "\" shape = \"record\"];\n"); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
@ -1140,35 +1180,6 @@
|
||||||
nsg += 1; \
|
nsg += 1; \
|
||||||
\
|
\
|
||||||
return nsg; \
|
return nsg; \
|
||||||
} \
|
|
||||||
\
|
|
||||||
/* -- skip_dot_ \
|
|
||||||
* Create a DOT file of the internal representation of the \
|
|
||||||
* Skiplist on the provided file descriptor (default: STDOUT). \
|
|
||||||
* \
|
|
||||||
* To view the output: \
|
|
||||||
* $ dot -Tps filename.dot -o outfile.ps \
|
|
||||||
* You can change the output format by varying the value after -T and \
|
|
||||||
* choosing an appropriate filename extension after -o. \
|
|
||||||
* See: https://graphviz.org/docs/outputs/ for the format options. \
|
|
||||||
* \
|
|
||||||
* https://en.wikipedia.org/wiki/DOT_(graph_description_language) \
|
|
||||||
*/ \
|
|
||||||
int prefix##skip_dot_##decl(FILE *os, decl##_t *slist, size_t nsg, skip_sprintf_node_##decl##_t fn) \
|
|
||||||
{ \
|
|
||||||
if (__skip_integrity_check_##decl(slist) != 0) { \
|
|
||||||
perror("Skiplist failed integrity checks, impossible to diagram."); \
|
|
||||||
return -1; \
|
|
||||||
} \
|
|
||||||
if (os == NULL) \
|
|
||||||
os = stdout; \
|
|
||||||
if (!os) { \
|
|
||||||
perror("Failed to open output file, unable to write DOT file."); \
|
|
||||||
return -1; \
|
|
||||||
} \
|
|
||||||
__skip_dot_start_##decl(os, slist, nsg, fn); \
|
|
||||||
__skip_dot_finish_##decl(os, nsg); \
|
|
||||||
return 0; \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _SKIPLIST_H_ */
|
#endif /* _SKIPLIST_H_ */
|
||||||
|
|
Loading…
Reference in a new issue