/* define SLOWGRAPH_IMPL */ #ifndef SLOWGRAPH_COMMON_H #define SLOWGRAPH_COMMON_H #ifndef SLOWGRAPH_FUNC #define SLOWGRAPH_FUNC /**/ #endif #include #include #include #include #include #define SLOWGRAPH_LENOF(x) (sizeof((x)) / sizeof(*(x))) typedef struct SlowGraph SlowGraph; typedef struct SlowGraphNode SlowGraphNode; typedef struct SlowGraphEdge SlowGraphEdge; typedef struct SlowGraphAttr SlowGraphAttr; struct SlowGraphAttr { SlowGraphAttr *next; unsigned hash; char *key, *val; }; struct SlowGraphNode { SlowGraphNode *next; unsigned name_hash; char *name; SlowGraphAttr *attr; SlowGraphEdge *children; unsigned gc_used : 1; }; struct SlowGraphEdge { SlowGraphEdge *next; SlowGraphAttr *attr; SlowGraphNode *node; }; struct SlowGraph { SlowGraphNode *first; }; SLOWGRAPH_FUNC void SlowGraphAttr_free(SlowGraphAttr *attrs); SLOWGRAPH_FUNC unsigned slowgraph_hash(char const *buf); SLOWGRAPH_FUNC unsigned slowgraph_hashn(void const *bufp, int len); SLOWGRAPH_FUNC char * slowgraph_next_edge(FILE *inp, char *buf, int bufn, void (*opt_attr_clbk)(char *node, char *key, char *val), void (*opt_last_edge_attr_clbk)(char *key, char *val)); SLOWGRAPH_FUNC SlowGraphAttr *SlowGraphAttr_find(SlowGraphAttr *a, unsigned hash); /* Step 1: call SlowGraph_markAllUnused(graph) Step 2: manually mark input nodes as used Step 3: call SlowGraph_gcUnused(graph) // only deletes nodes not used by manually marked used nodes */ SLOWGRAPH_FUNC void SlowGraph_markAllUnused(SlowGraph *graph); SLOWGRAPH_FUNC void SlowGraph_gcUnused(SlowGraph *graph); SLOWGRAPH_FUNC SlowGraphNode *SlowGraph_find(SlowGraph *g, unsigned name_hash); SLOWGRAPH_FUNC SlowGraphEdge *SlowGraphNode_findConnection(SlowGraphNode *from, SlowGraphNode *to); /* if edge from->to already exists, return it instead */ SLOWGRAPH_FUNC SlowGraphEdge *SlowGraphNode_connect(SlowGraphNode *from, SlowGraphNode *to); SLOWGRAPH_FUNC SlowGraphNode *SlowGraph_getOrCreate(SlowGraph *g, char const *name); SLOWGRAPH_FUNC void slowgraph_setAttr(SlowGraphAttr **list, char const *key, char const *val); /* returns 1 on failure */ SLOWGRAPH_FUNC int SlowGraph_readDGTXT(SlowGraph *g, FILE *f); #ifdef SLOWGRAPH_IMPL SLOWGRAPH_FUNC void SlowGraphAttr_free(SlowGraphAttr *attrs) { SlowGraphAttr *to_free; for (; attrs;) { to_free = attrs; attrs = attrs->next; free(to_free->key); free(to_free->val); free(to_free); } } SLOWGRAPH_FUNC unsigned slowgraph_hash(char const *buf) { unsigned res = 5381; for (; *buf; buf++) { res = (res << 5) + res + *buf; } return res; } SLOWGRAPH_FUNC unsigned slowgraph_hashn(void const *bufp, int len) { char const *buf = (char const *)bufp; unsigned res = 5381; for (; buf != ((char const *)bufp) + len; buf++) { res = (res << 5) + res + *buf; } return res; } SLOWGRAPH_FUNC char * slowgraph_next_edge(FILE *inp, char *buf, int bufn, void (*opt_attr_clbk)(char *node, char *key, char *val), void (*opt_last_edge_attr_clbk)(char *key, char *val)) { int c; char *n, *k, *v, *retv = 0; c = fgetc(inp); for (;;) { if (c == EOF) { retv = 0; break; } for (; isspace(c); c = fgetc(inp)) ; if (c == '#') { c = fgetc(inp); if (c == '#' && opt_attr_clbk && buf == fgets(buf, bufn, inp)) { n = strtok(buf + 1, " \r\n"); k = strtok(0, " \r\n"); v = strtok(0, " \r\n"); opt_attr_clbk(n, k, v); } else if (c == ':' && opt_last_edge_attr_clbk && buf == fgets(buf, bufn, inp)) { k = strtok(buf + 1, " \r\n"); v = strtok(0, " \r\n"); opt_last_edge_attr_clbk(k, v); } else { for (; c != EOF && c != '\n'; c = fgetc(inp)) ; c = fgetc(inp); } continue; } for (k = buf; c != EOF && !isspace(c); k++) { *k = c; c = fgetc(inp); } if (k == buf) continue; *k++ = 0; for (; isspace(c); c = fgetc(inp)) ; for (v = k; c != EOF && !isspace(c); v++) { *v = c; c = fgetc(inp); } *v = 0; if (v == k) continue; retv = k; break; } ungetc(c, inp); return retv; } SLOWGRAPH_FUNC SlowGraphAttr *SlowGraphAttr_find(SlowGraphAttr *a, unsigned hash) { for (; a; a = a->next) if (a->hash == hash) return a; return 0; } SLOWGRAPH_FUNC void SlowGraph_markAllUnused(SlowGraph *graph) { SlowGraphNode *n; for (n = graph->first; n; n = n->next) n->gc_used = 0; } SLOWGRAPH_FUNC void SlowGraph_gcUnused(SlowGraph *graph) { int changed = 0; SlowGraphNode *n, *o, **prev; SlowGraphEdge *e, *oe; do { for (n = graph->first; n; n = n->next) { if (n->gc_used) { for (e = n->children; e; e = e->next) { if (!e->node->gc_used) { e->node->gc_used = 1; changed = 1; } } } } } while (changed); prev = &graph->first; for (n = graph->first; n;) { if (n->gc_used) { prev = &n->next; n = n->next; } else { o = n; n = n->next; *prev = n; for (e = o->children; e;) { oe = e; e = e->next; SlowGraphAttr_free(oe->attr); free(oe); } SlowGraphAttr_free(o->attr); free(o->name); free(o); } } } SLOWGRAPH_FUNC SlowGraphNode *SlowGraph_find(SlowGraph *g, unsigned name_hash) { SlowGraphNode *n; for (n = g->first; n; n = n->next) if (n->name_hash == name_hash) return n; return 0; } SLOWGRAPH_FUNC SlowGraphEdge *SlowGraphNode_findConnection(SlowGraphNode *from, SlowGraphNode *to) { SlowGraphEdge *e; for (e = from->children; e; e = e->next) if (e->node == to) return e; return 0; } SLOWGRAPH_FUNC SlowGraphEdge *SlowGraphNode_connect(SlowGraphNode *from, SlowGraphNode *to) { SlowGraphEdge *e = SlowGraphNode_findConnection(from, to); if (e) return e; e = (SlowGraphEdge *)calloc(1, sizeof(SlowGraphEdge)); if (!e) return 0; e->node = to; e->next = from->children; from->children = e; return e; } SLOWGRAPH_FUNC SlowGraphNode *SlowGraph_getOrCreate(SlowGraph *g, char const *name) { unsigned hash, strln; SlowGraphNode *n; strln = strlen(name); hash = slowgraph_hashn(name, strln); if ((n = SlowGraph_find(g, hash))) return n; n = (SlowGraphNode *)calloc(1, sizeof(SlowGraphNode)); if (!n) return 0; n->name = (char *)calloc(strln + 1, sizeof(char)); if (!n->name) { free(n); return 0; } strcpy(n->name, name); n->name_hash = hash; n->next = g->first; g->first = n; return n; } SLOWGRAPH_FUNC void slowgraph_setAttr(SlowGraphAttr **list, char const *key, char const *val) { /* TODO: malloc fail handking */ unsigned keylen = strlen(key); unsigned vallen = strlen(val); unsigned hash = slowgraph_hashn(key, keylen); SlowGraphAttr *a = SlowGraphAttr_find(*list, hash); if (a) { free(a->val); } else { a = (SlowGraphAttr *)calloc(1, sizeof(SlowGraphAttr)); a->next = *list; *list = a; a->hash = hash; a->key = (char *)calloc(keylen + 1, sizeof(char)); strcpy(a->key, key); } a->val = (char *)calloc(vallen + 1, sizeof(char)); strcpy(a->val, val); } static SlowGraph *SlowGraph_read__graph; static SlowGraphEdge *SlowGraph_read__lastEdge; static void SlowGraph_read__nodeAttr(char *node, char *key, char *val) { SlowGraphNode *n = SlowGraph_getOrCreate(SlowGraph_read__graph, node); if (!n) return; slowgraph_setAttr(&n->attr, key, val); } static void SlowGraph_read__edgeAttr(char *key, char *val) { if (!SlowGraph_read__lastEdge) return; slowgraph_setAttr(&SlowGraph_read__lastEdge->attr, key, val); } /* returns 1 on failure */ SLOWGRAPH_FUNC int SlowGraph_readDGTXT(SlowGraph *g, FILE *f) { static char buf[512]; char *dest; SlowGraph_read__graph = g; SlowGraph_read__lastEdge = 0; while ((dest = slowgraph_next_edge(f, buf, SLOWGRAPH_LENOF(buf), SlowGraph_read__nodeAttr, SlowGraph_read__edgeAttr))) { SlowGraph_read__lastEdge = SlowGraphNode_connect( SlowGraph_getOrCreate(g, buf), SlowGraph_getOrCreate(g, dest)); if (!SlowGraph_read__lastEdge) return 1; } SlowGraph_read__graph = 0; SlowGraph_read__lastEdge = 0; return 0; } #endif #endif