diff --git a/examples/slm.c b/examples/slm.c index 84020a2..676584e 100644 --- a/examples/slm.c +++ b/examples/slm.c @@ -143,7 +143,7 @@ main() perror("Failed to open file /tmp/slm.dot"); return EXIT_FAILURE; } - api_skip_dot_slex(of, list, sprintf_slex_node); + api_skip_dot_slex(of, list, 0, sprintf_slex_node); fclose(of); api_skip_destroy_slex(list); diff --git a/include/sl.h b/include/sl.h index e408cea..05ccab0 100644 --- a/include/sl.h +++ b/include/sl.h @@ -277,8 +277,9 @@ return NULL; \ } \ \ - /* -- skip_insert_ */ \ - int prefix##skip_insert_##decl(decl##_t *slist, decl##_node_t *n) \ + /* -- __skip_insert_ */ \ + static int __skip_insert_##decl(decl##_t *slist, decl##_node_t *n, \ + int flags) \ { \ unsigned int i; \ decl##_node_t *prev, *elm = slist->slh_head; \ @@ -298,26 +299,28 @@ __skip_key_compare_##decl(slist, elm->field.sle_next[i], n, \ slist->aux) < 0) \ elm = elm->field.sle_next[i]; \ - path[i] = elm; \ + path[i] = elm; \ ARRAY_SET_LENGTH(path, ARRAY_LENGTH(path) + 1); \ } while (i--); \ i = 0; \ prev = elm; \ elm = elm->field.sle_next[0]; \ if (__skip_key_compare_##decl(slist, elm, n, slist->aux) == 0) { \ - /* Don't overwrite, to do that use _REPLACE not _INSERT */ \ - ARRAY_FREE(path); \ - return -1; \ + if (flags == 0) { \ + /* Don't overwrite, to do that use _REPLACE not _INSERT */ \ + ARRAY_FREE(path); \ + return -1; \ + } \ } \ size_t level = __skip_toss_##decl(slist->max, slist->fanout); \ ARRAY_SET_LENGTH(n->field.sle_next, level); \ if (level > slist->level) { \ - for (i = slist->level + 1; i <= level; i++) { \ + for (i = slist->level + 1; i <= level; i++) { \ path[i] = slist->slh_tail; \ } \ slist->level = level; \ } \ - for (i = 0; i <= level; i++) { \ + for (i = 0; i <= level; i++) { \ n->field.sle_next[i] = path[i]->field.sle_next[i]; \ path[i]->field.sle_next[i] = n; \ } \ @@ -330,14 +333,17 @@ return 0; \ } \ \ - /* -- skip_insert_dup_ TODO \ + /* -- skip_insert_ */ \ + int prefix##skip_insert_##decl(decl##_t *slist, decl##_node_t *n) \ + { \ + return __skip_insert_##decl(slist, n, 0); \ + } \ + \ + /* -- skip_insert_dup_ */ \ int prefix##skip_insert_dup_##decl(decl##_t *slist, decl##_node_t *n) \ { \ - ((void)slist); \ - ((void)n); \ - return 0; \ + return __skip_insert_##decl(slist, n, 1); \ } \ - */ \ \ /* -- skip_find_ \ * Find a node that matches another node. This differs from the locate() \ @@ -477,7 +483,7 @@ ARRAY_FREE(path); \ free_node_blk; \ /* Find all levels in the first element in the list that point \ - at the tail and shrink the level*/ \ + at the tail and shrink the level. */ \ while (slist->level > 0 && \ slist->slh_head->field.sle_next[slist->level] == slist->slh_tail) { \ slist->level--; \ @@ -531,6 +537,31 @@ return 0; \ } \ \ + /* -- skip_snapshot_ \ + * A snapshot is a read-only view of a Skiplist at a point in \ + * time. Once taken, a snapshot must be restored or disposed. \ + * Any number of snapshots can be created. \ + */ \ + int prefix##skip_snapshot_##decl(decl##_t *slist) \ + { \ + ((void)slist); /* TODO */ \ + return 0; \ + } \ + \ + /* -- skip_restore_snapshot_ */ \ + int prefix##skip_restore_snapshot_##decl(decl##_t *slist) \ + { \ + ((void)slist); /* TODO */ \ + return 0; \ + } \ + \ + /* -- skip_dispose_snapshot_ */ \ + int prefix##skip_dispose_snapshot_##decl(decl##_t *slist) \ + { \ + ((void)slist); /* TODO */ \ + return 0; \ + } \ + \ /* -- __skip_integrity_check_ */ \ static int __skip_integrity_check_##decl(decl##_t *slist) \ { \ @@ -573,181 +604,179 @@ return (vtype)0; \ } -#define SKIPLIST_DECL_DOT(decl, prefix, field) \ - \ - /* A type for a function that writes into a char[2048] buffer \ - * a description of the value within the node. */ \ - typedef void (*skip_sprintf_node_##decl##_t)(decl##_node_t *, char *); \ - \ - /* -- __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) \ - { \ - char buf[2048]; \ - size_t level, height = ARRAY_LENGTH(node->field.sle_next); \ - fprintf(os, "\"node%zu%p\"", nsg, (void *)node); \ - fprintf(os, " [label = \""); \ - level = height; \ - do { \ - fprintf(os, " { | %p }", level + 1, level + 1, \ - (void *)node->field.sle_next[level]); \ - if (level != 0) \ - fprintf(os, " | "); \ - } while (level--); \ - if (fn) { \ - fn(node, buf); \ - fprintf(os, " %s\"\n", buf); \ - } else { \ - fprintf(os, " ?\"\n"); \ - } \ - fprintf(os, "shape = \"record\"\n"); \ - fprintf(os, "];\n"); \ - \ - /* Now edges */ \ - level = 0; \ - for (level = 0; level < height; level++) { \ - fprintf(os, "\"node%zu%p\"", nsg, (void *)node); \ - fprintf(os, ":f%zu -> ", level + 1); \ - fprintf(os, "\"node%zu%p\"", nsg, (void *)node->field.sle_next[level]); \ - fprintf(os, ":w%zu [];\n", level + 1); \ - } \ - \ - if (node->field.sle_next[0] != SKIP_LAST(slist)) \ - __skip_dot_node_##decl(os, slist, node->field.sle_next[0], nsg, fn); \ - } \ - \ - /* -- __skip_dot_finish_ \ - * Finalise the DOT file of the internal representation. \ - */ \ - static void __skip_dot_finish_##decl(FILE *os, size_t nsg) \ - { \ - size_t i; \ - if (nsg > 0) { \ - /* Link the nodes together with an invisible node. \ - * node0 [shape=record, label = " | | | | \ - * | | | | | ", style=invis, width=0.01]; \ - */ \ - fprintf(os, "node0 [shape=record, label = \""); \ - for (i = 0; i < nsg; ++i) { \ - fprintf(os, " | ", i); \ - } \ - fprintf(os, "\", style=invis, width=0.01];\n"); \ - \ - /* Now connect nodes with invisible edges \ - * \ - * node0:f0 -> HeadNode [style=invis]; \ - * node0:f1 -> HeadNode1 [style=invis]; \ - */ \ - for (i = 0; i < nsg; ++i) { \ - fprintf(os, "node0:f%zu -> HeadNode%zu [style=invis];\n", i, i); \ - } \ - nsg = 0; \ - } \ - fprintf(os, "}\n"); \ - } \ - \ - /* -- skip_dot_start_ */ \ - static int __skip_dot_start_##decl(FILE *os, decl##_t *slist, size_t nsg, \ - skip_sprintf_node_##decl##_t fn) \ - { \ - size_t level; \ - decl##_node_t *head; \ - if (nsg == 0) { \ - fprintf(os, "digraph Skiplist {\n"); \ - fprintf(os, "label = \"Skiplist.\"\n"); \ - fprintf(os, "graph [rankdir = \"LR\"];\n"); \ - fprintf(os, "node [fontsize = \"12\" shape = \"ellipse\"];\n"); \ - fprintf(os, "edge [];\n\n"); \ - } \ - fprintf(os, "subgraph cluster%zu {\n", nsg); \ - fprintf(os, "style=dashed\n"); \ - fprintf(os, "label=\"Skip list iteration %zu\"\n\n", nsg); \ - fprintf(os, "\"HeadNode%zu\" [\n", nsg); \ - fprintf(os, "label = \""); \ - \ - /* Write out the fields */ \ - head = slist->slh_head; \ - if (SKIP_EMPTY(slist)) \ - fprintf(os, "Empty HeadNode"); \ - else { \ - level = ARRAY_LENGTH(head->field.sle_next); \ - while (level--) { \ - decl##_node_t *node = head->field.sle_next[level]; \ - fprintf(os, "{ %p }", level + 1, (void *)node); \ - if (level + 1 != 0) \ - fprintf(os, " | "); \ - } \ - } \ - fprintf(os, "\"\n"); \ - fprintf(os, "shape = \"record\"\n"); \ - fprintf(os, "];\n"); \ - \ - /* Edges for head node */ \ - decl##_node_t *node = slist->slh_head; \ - level = 0; \ - do { \ - fprintf(os, "\"HeadNode%zu\":f%zu -> ", nsg, level + 1); \ - fprintf(os, "\"node%zu%p\"", nsg, (void *)node->field.sle_next[level]); \ - fprintf(os, ":w%zu [];\n", level + 1); \ - } while (level++ < slist->level); \ - fprintf(os, "}\n\n"); \ - \ - /* Now all nodes via level 0, if non-empty */ \ - node = slist->slh_head; \ - if (ARRAY_LENGTH(node->field.sle_next)) \ - __skip_dot_node_##decl(os, slist, node->field.sle_next[0], nsg, fn); \ - fprintf(os, "\n"); \ - \ - /* The tail, sentinal node */ \ - if (!SKIP_EMPTY(slist)) { \ - fprintf(os, "\"node%zu0x0\" [label = \"", nsg); \ - level = slist->level; \ - do { \ - fprintf(os, " NULL", level + 1); \ - if (level != 0) \ - fprintf(os, " | "); \ - } while (level-- > 0); \ - fprintf(os, "\" shape = \"record\"];\n"); \ - } \ - \ - /* End: "subgraph cluster1 {" */ \ - fprintf(os, "}\n\n"); \ - nsg += 1; \ - \ - return 0; \ - } \ - \ - /* -- 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, \ - skip_sprintf_node_##decl##_t fn) \ - { \ - size_t nsg = 0; \ - 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; \ +#define SKIPLIST_DECL_DOT(decl, prefix, field) \ + \ + /* A type for a function that writes into a char[2048] buffer \ + * a description of the value within the node. */ \ + typedef void (*skip_sprintf_node_##decl##_t)(decl##_node_t *, char *); \ + \ + /* -- __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) \ + { \ + char buf[2048]; \ + size_t level, height = ARRAY_LENGTH(node->field.sle_next) - 1; \ + decl##_node_t *next; \ + \ + fprintf(os, "\"node%zu %p\"", nsg, (void *)node); \ + fprintf(os, " [label = \""); \ + level = height; \ + do { \ + fprintf(os, " { | %p } |", level, level, \ + (void *)node->field.sle_next[level]); \ + } while (level--); \ + if (fn) { \ + fn(node, buf); \ + fprintf(os, " %s\"\n", buf); \ + } else { \ + fprintf(os, " ?\"\n"); \ + } \ + fprintf(os, "shape = \"record\"\n"); \ + fprintf(os, "];\n"); \ + \ + /* Now edges */ \ + level = 0; \ + for (level = 0; level < height; level++) { \ + fprintf(os, "\"node%zu %p\"", nsg, (void *)node); \ + fprintf(os, ":f%zu -> ", level); \ + fprintf(os, "\"node%zu %p\"", nsg, (void *)node->field.sle_next[level]); \ + fprintf(os, ":w%zu [];\n", level); \ + } \ + next = prefix##skip_next_node_##decl(slist, node); \ + if (next) \ + __skip_dot_node_##decl(os, slist, next, nsg, fn); \ + } \ + \ + /* -- __skip_dot_finish_ \ + * Finalize the DOT file of the internal representation. \ + */ \ + static void __skip_dot_finish_##decl(FILE *os, size_t nsg) \ + { \ + size_t i; \ + if (nsg > 0) { \ + /* Link the nodes together with an invisible node. \ + * node0 [shape=record, label = " | | | | \ + * | | | | | ", style=invis, width=0.01]; \ + */ \ + fprintf(os, "node0 [shape=record, label = \""); \ + for (i = 0; i < nsg; ++i) { \ + fprintf(os, " | ", i); \ + } \ + fprintf(os, "\", style=invis, width=0.01];\n"); \ + \ + /* Now connect nodes with invisible edges \ + * \ + * node0:f0 -> HeadNode [style=invis]; \ + * node0:f1 -> HeadNode1 [style=invis]; \ + */ \ + for (i = 0; i < nsg; ++i) { \ + fprintf(os, "node0:f%zu -> HeadNode%zu [style=invis];\n", i, i); \ + } \ + nsg = 0; \ + } \ + } \ + \ + /* -- skip_dot_start_ */ \ + static int __skip_dot_start_##decl(FILE *os, decl##_t *slist, size_t nsg, \ + skip_sprintf_node_##decl##_t fn) \ + { \ + size_t level; \ + decl##_node_t *head; \ + if (nsg == 0) { \ + fprintf(os, "digraph Skiplist {\n"); \ + fprintf(os, "label = \"Skiplist.\"\n"); \ + fprintf(os, "graph [rankdir = \"LR\"];\n"); \ + fprintf(os, "node [fontsize = \"12\" shape = \"ellipse\"];\n"); \ + fprintf(os, "edge [];\n\n"); \ + } \ + fprintf(os, "subgraph cluster%zu {\n", nsg); \ + fprintf(os, "style=dashed\n"); \ + fprintf(os, "label=\"Skip list iteration %zu\"\n\n", nsg); \ + fprintf(os, "\"HeadNode%zu\" [\n", nsg); \ + fprintf(os, "label = \""); \ + \ + /* Write out the fields */ \ + head = slist->slh_head; \ + if (SKIP_EMPTY(slist)) \ + fprintf(os, "Empty HeadNode"); \ + else { \ + level = ARRAY_LENGTH(head->field.sle_next) - 1; \ + do { \ + decl##_node_t *node = head->field.sle_next[level]; \ + fprintf(os, "{ %p }", level, (void *)node); \ + if (level) \ + fprintf(os, " | "); \ + } while (level--); \ + } \ + fprintf(os, "\"\n"); \ + fprintf(os, "shape = \"record\"\n"); \ + fprintf(os, "];\n"); \ + \ + /* Edges for head node */ \ + decl##_node_t *node = slist->slh_head; \ + level = 0; \ + do { \ + fprintf(os, "\"HeadNode%zu\":f%zu -> ", nsg, level); \ + fprintf(os, "\"node%zu %p\"", nsg, (void *)node->field.sle_next[level]); \ + fprintf(os, ":w%zu [];\n", level); \ + } while (++level < slist->level); \ + fprintf(os, "}\n\n"); \ + \ + /* Now all nodes via level 0, if non-empty */ \ + node = prefix##skip_head_##decl(slist); \ + if (node) \ + __skip_dot_node_##decl(os, slist, node->field.sle_next[0], nsg, fn); \ + fprintf(os, "\n"); \ + \ + /* The tail, sentinal node */ \ + if (!SKIP_EMPTY(slist)) { \ + fprintf(os, "\"node%zu 0x0\" [label = \"", nsg); \ + level = slist->level; \ + do { \ + fprintf(os, " NULL", level); \ + if (level) \ + fprintf(os, " | "); \ + } while (--level); \ + fprintf(os, "\" shape = \"record\"];\n"); \ + } \ + \ + /* End: "subgraph cluster0 {" */ \ + fprintf(os, "}\n\n"); \ + nsg += 1; \ + \ + 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_ */