This commit is contained in:
2025-09-04 23:14:40 +02:00
parent 2b05f07c1f
commit 337f069d5d
12 changed files with 907 additions and 213 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
a.out
*.plist
*.o
*.exe
core

View File

@@ -1,12 +0,0 @@
#include "common.h"
int main(int argc, char **argv) {
char buf[512];
char *dest;
while (( dest = slowgraph_next_edge(stdin, buf, slowgraph_lenof(buf), 0) )) {
printf("%s -> %s\n", buf, dest);
}
return 0;
}

201
common.h
View File

@@ -1,201 +0,0 @@
#ifndef SLOWGRAPH_COMMON_H
#define SLOWGRAPH_COMMON_H
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define slowgraph_lenof(x) (sizeof((x)) / sizeof(*(x)))
static unsigned
slowgraph_hash(char const *buf) {
unsigned res = 5381;
for (; *buf; buf++) {
res = (res << 5) + res + *buf;
}
return res;
}
static unsigned
slowgraph_hashn(void const *bufp, int len) {
char const* buf = (char const *) bufp;
unsigned res = 5381;
for (; buf != buf + len; buf ++) {
res = (res << 5) + res + *buf;
}
return res;
}
static char *
slowgraph_next_edge(
FILE* inp,
char* buf, int bufn,
void (*opt_attr_clbk)(char* node, 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 {
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);
}
*k++=0;
for (; isspace(c); c = fgetc(inp))
;
for (v = k; c != EOF && !isspace(c); v ++) {
*v = c;
c = fgetc(inp);
}
*v = 0;
retv = k;
break;
}
ungetc(c, inp);
return retv;
}
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;
};
static char*
SlowGraphAttr_find(SlowGraphAttr* a, unsigned hash) {
for (; a; a = a->next)
if (a->hash == hash)
return a->val;
return 0;
}
struct SlowGraphNode {
SlowGraphNode *next;
unsigned name_hash;
char *name;
SlowGraphAttr* attr;
SlowGraphEdge* children;
unsigned gc_used : 1;
};
struct SlowGraphEdge {
SlowGraphEdge *next;
unsigned cost;
unsigned aig_inv : 1;
unsigned user_attr;
SlowGraphNode *node;
};
struct SlowGraph {
SlowGraphNode *first;
};
// 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
static void
SlowGraph_markAllUnused(SlowGraph *graph) {
SlowGraphNode *n;
for (n = graph->first; n; n = n->next)
n->gc_used = 0;
}
// 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
static void
SlowGraph_gcUnused(SlowGraph *graph) {
int changed = 0;
SlowGraphNode *n, *o, **prev;
SlowGraphEdge *e, *oe;
SlowGraphAttr *a, *oa;
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;
free(oe);
}
for (a = o->attr; a; ) {
oa = a;
a = a->next;
free(a->key);
free(a->val);
free(a);
}
free(o->name);
free(o);
}
}
}
#endif

60
lint.sh Executable file
View File

@@ -0,0 +1,60 @@
set -e
run () {
set -e
echo "# $@"
$@
}
echo "# ==== clang NATIVE ===="
for f in */std*.c; do
for std in c89 c90 c99 c11 c17 c23 gnu89 gnu99 gnu11 gnu17 gnu23 c2y gnu2y; do
run clang $f -Wall -Wextra -Wpedantic -Werror -Wno-extra-semi -std=$std --analyze -O0 -o /dev/null
done
done
echo
echo "# ==== clang rv32 FREESTANDING ===="
for f in */nostd*.c; do
for std in c89 c90 c99 c11 c17 c23 gnu89 gnu99 gnu11 gnu17 gnu23 c2y gnu2y; do
run clang $f -Wall -Wextra -Wpedantic -Werror -Wno-extra-semi -std=$std --analyze -O0 -target riscv32-unknown-freestanding -nostdlib -o /dev/null
done
done
echo
echo "# ==== clang wasm32 FREESTANDING ===="
for f in */nostd*.c; do
for std in c89 c90 c99 c11 c17 c23 gnu89 gnu99 gnu11 gnu17 gnu23 c2y gnu2y; do
run clang $f -Wall -Wextra -Wpedantic -Werror -Wno-extra-semi -std=$std --analyze -O0 -target wasm32-unknown-freestanding -nostdlib -o /dev/null
done
done
echo
echo "# ==== clang++ NATIVE ===="
for f in */std*.cxx; do
for std in c++03 c++98 gnu++98 c++11 gnu++11 c++14 c++17 gnu++17 c++20 gnu++20 c++23 gnu++23 c++2c gnu++2c; do
run clang++ $f -Wall -Wextra -Wpedantic -Werror -Wno-extra-semi -std=$std --analyze -O0 -o /dev/null
done
done
echo
echo "# ==== g++ NATIVE ===="
for f in */std*.cxx; do
for std in c++03 c++98 gnu++98 c++11 gnu++11 c++14 c++17 gnu++17 c++20 gnu++20 c++23 gnu++23 c++2c gnu++2c; do
run g++ $f -Wall -Wextra -Wpedantic -Werror -std=$std -fanalyzer -O0 -o /dev/null
done
done
echo
echo "# ==== gcc NATIVE ===="
for f in */std*.c; do
for std in c89 c90 c99 c11 c17 c23 gnu89 gnu99 gnu11 gnu17 gnu23; do
run gcc $f -Wall -Wextra -Wpedantic -Werror -std=$std -fanalyzer -O0 -o /dev/null
done
done
echo
echo "# ==== tcc NATIVE ===="
for f in */std*.c; do
run tcc $f -Wall -Werror -o /dev/null
done

247
slowarr.h Normal file
View File

@@ -0,0 +1,247 @@
#ifndef SLOWARR_H_
#define SLOWARR_H_
#ifndef SLOWARR_NAMESPACE
#define SLOWARR_NAMESPACE(X) SLOWARR__##X
#define SLOWARR_CXXT SlowArr
#endif
#ifndef SLOWARR_FUNC
#define SLOWARR_FUNC /**/
#endif
#ifndef SLOWARR_ASSERT_USER_ERROR
#include <assert.h>
#define SLOWARR_ASSERT_USER_ERROR(expr) assert(expr)
#endif
#ifndef SLOWARR_MEMZERO
#include <string.h>
#define SLOWARR_MEMZERO(ptr, len) memset(ptr, 0, len)
#endif
#ifndef SLOWARR_MEMMOVE
#include <string.h>
#define SLOWARR_MEMMOVE(dst, src, len) memmove(dst, src, len)
#endif
#ifdef SLOW_DEFINE_ACCESS
#ifndef SLOW_DEFINE_ACCESS__DONE
#define T(B, T) B##__##T
#define F(B, T, f) B##__##T##__##f
#define SLOW_DEFINE_ACCESS__DONE
#endif
#endif
#ifndef SLOWARR_SZT
#include <stddef.h>
#define SLOWARR_SZT size_t
#endif
#ifndef SLOWARR_REALLOC
#include <stdlib.h>
#define SLOWARR_REALLOC(ptr, old, news) realloc(ptr, news)
#define SLOWARR_FREE(ptr, size) free(ptr)
#endif
#ifndef SLOWARR_GROWTH_RATE
/** returns len * 1.5 */
#define SLOWARR_GROWTH_RATE(T, len) (((len) >> 1) + (len))
#endif
#ifndef SLOWARR_CAP_FOR_FIRST_ELEM
#define SLOWARR_CAP_FOR_FIRST_ELEM(T) (4) /* TODO: could do better */
#endif
/* TODO */
#define SLOWARR_MANGLE(T) SLOWARR_NAMESPACE(T)
#define SLOWARR_MANGLE_F(T, F) SLOWARR_NAMESPACE(T##__##F)
#ifdef __cplusplus
#define SLOWARR_BEGINC extern "C" {
#define SLOWARR_ENDC }
#else
#define SLOWARR_BEGINC /**/
#define SLOWARR_ENDC /**/
#endif
#ifndef SLOWARR_ON_MALLOC_FAIL
#include <stdio.h>
#include <stdlib.h>
#define SLOWARR_ON_MALLOC_FAIL(nb) \
do { \
fprintf(stderr, "\nmemory allocation of %lu bytes failed!\n", \
(unsigned long)nb); \
exit(1); \
} while (0)
#endif
#define SLOWARR__BORROWED (1 << 0) /** disable reallocation / freeing */
#define SLOWARR__ZEROIZE (1 << 1) /** for cryptography */
#ifdef __cplusplus
template <typename T> struct SLOWARR_CXXT {};
#define SLOWARR_CXX_HEADER(T) \
template <> struct SLOWARR_CXXT<T> { \
SLOWARR_MANGLE(T) arr; \
\
T *begin() { return arr.data; } \
T *cbegin() const { return arr.data; } \
T *cend() const { return arr.data + arr.len; } \
T *end() { return cend(); } \
SLOWARR_SZT length() const { return arr.len; } \
};
#else
#define SLOWARR_CXX_HEADER(T) /**/
#endif
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
static void SLOWARR___REQUIRE_SEMI(void) {}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#define SLOWARR_REQUIRE_SEMI static void SLOWARR___REQUIRE_SEMI(void)
#define SLOWARR_Header(T) \
SLOWARR_BEGINC \
/** usage: T(Arr,int) myarr = {0}; */ \
typedef struct { \
SLOWARR_SZT cap, len; \
T *data; \
unsigned char attr; \
} SLOWARR_MANGLE(T); \
SLOWARR_ENDC \
\
SLOWARR_CXX_HEADER(T) \
SLOWARR_BEGINC \
\
/** result can not be reallocated */ \
SLOWARR_FUNC SLOWARR_MANGLE(T) \
SLOWARR_MANGLE_F(T, borrow)(T * data, SLOWARR_SZT sz); \
\
/** usage: G(Arr,int,unsafeClear)(&arr) \
* this is marked unsafe, because it assumes that the elements don't need \
* to be destroyed each */ \
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, unsafeClear)(SLOWARR_MANGLE(T) * arr); \
\
/** resize to smallest cap required to hold current elems */ \
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, shrink)(SLOWARR_MANGLE(T) * arr); \
\
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, reserveTotal)(SLOWARR_MANGLE(T) * arr, \
SLOWARR_SZT num); \
\
SLOWARR_FUNC T *SLOWARR_MANGLE_F(T, pushRef)(SLOWARR_MANGLE(T) * arr); \
\
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, push)(SLOWARR_MANGLE(T) * arr, T val); \
\
/** fails if oob */ \
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, remove)(SLOWARR_MANGLE(T) * arr, \
T * out, SLOWARR_SZT i); \
\
SLOWARR_FUNC T SLOWARR_MANGLE_F(T, pop)(SLOWARR_MANGLE(T) * arr); \
\
SLOWARR_ENDC \
SLOWARR_REQUIRE_SEMI
/** call this only once in your program. call SLOWARR_Header(T) first */
#define SLOWARR_Impl(T) \
SLOWARR_BEGINC \
\
/** result can not be reallocated */ \
SLOWARR_FUNC SLOWARR_MANGLE(T) \
SLOWARR_MANGLE_F(T, borrow)(T * data, SLOWARR_SZT sz) { \
SLOWARR_MANGLE(T) arr; \
arr.data = data; \
arr.cap = sz; \
arr.len = sz; \
arr.attr = SLOWARR__BORROWED; \
return arr; \
} \
\
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, \
unsafeClear)(SLOWARR_MANGLE(T) * arr) { \
if (arr->data) \
SLOWARR_FREE(arr->data, arr->cap); \
arr->data = (T *)(void *)0; \
arr->cap = 0; \
arr->len = 0; \
/* don't change attrs */ \
} \
\
/* TODO: could make this align the cap num for better perf */ \
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, shrink)(SLOWARR_MANGLE(T) * arr) { \
if (arr->attr & SLOWARR__BORROWED) \
return; \
\
if (arr->len == arr->cap) \
return; \
if (arr->attr & SLOWARR__ZEROIZE) \
SLOWARR_MEMZERO(arr->data + arr->len, \
(arr->cap - arr->len) * sizeof(T)); \
arr->data = (T *)SLOWARR_REALLOC(arr->data, arr->cap * sizeof(T), \
arr->len * sizeof(T)); \
arr->cap = arr->len; \
} \
\
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, reserveTotal)(SLOWARR_MANGLE(T) * arr, \
SLOWARR_SZT num) { \
\
void *n; \
if (num <= arr->cap) \
return; \
SLOWARR_ASSERT_USER_ERROR(!(arr->attr & SLOWARR__BORROWED)); \
n = SLOWARR_REALLOC(arr->data, sizeof(T) * arr->cap, sizeof(T) * num); \
if (!n) { \
if (arr->attr & SLOWARR__ZEROIZE) \
SLOWARR_MEMZERO(arr->data, arr->cap * sizeof(T)); \
SLOWARR_FREE(arr->data, arr->cap * sizeof(T)); \
SLOWARR_ON_MALLOC_FAIL(sizeof(T) * num); \
} \
arr->data = (T *)n; \
arr->cap = num; \
} \
\
SLOWARR_FUNC T *SLOWARR_MANGLE_F(T, pushRef)(SLOWARR_MANGLE(T) * arr) { \
if (arr->cap == 0) { \
SLOWARR_MANGLE_F(T, reserveTotal)(arr, SLOWARR_CAP_FOR_FIRST_ELEM(T)); \
} else if (arr->len + 1 > arr->cap) { \
SLOWARR_MANGLE_F(T, reserveTotal)(arr, \
SLOWARR_GROWTH_RATE(T, arr->len)); \
} \
return &arr->data[arr->len++]; \
} \
\
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, push)(SLOWARR_MANGLE(T) * arr, \
T val) { \
*SLOWARR_MANGLE_F(T, pushRef)(arr) = val; \
} \
\
SLOWARR_FUNC void SLOWARR_MANGLE_F(T, remove)(SLOWARR_MANGLE(T) * arr, \
T * out, SLOWARR_SZT i) { \
SLOWARR_SZT too_much; \
SLOWARR_ASSERT_USER_ERROR(i < arr->len); \
*out = arr->data[i]; \
SLOWARR_MEMMOVE(&arr->data[i], &arr->data[i + 1], \
sizeof(T) * (arr->len - (i + 1))); \
arr->len -= 1; \
too_much = arr->cap - arr->len; \
if (too_much > SLOWARR_GROWTH_RATE(T, arr->len)) { \
SLOWARR_MANGLE_F(T, shrink)(arr); \
} \
} \
\
SLOWARR_FUNC T SLOWARR_MANGLE_F(T, pop)(SLOWARR_MANGLE(T) * arr) { \
T temp; \
SLOWARR_MANGLE_F(T, remove)(arr, &temp, arr->len - 1); \
return temp; \
} \
\
SLOWARR_ENDC \
SLOWARR_REQUIRE_SEMI
#endif

39
slowarr/nostd1.c Normal file
View File

@@ -0,0 +1,39 @@
void c_memzero(void *ptrin, unsigned len) {
char *ptr = ptrin;
for (; len;) {
*ptr = 0;
len -= 1;
ptr += 1;
}
}
void c_memmove(void *destp, void *srcp, unsigned len) {
char *dest = destp;
char *src = srcp;
for (; len;) {
*dest = *src;
len -= 1;
src += 1;
dest += 1;
}
}
#define SLOWARR_ASSERT_USER_ERROR(expr) /**/
#define SLOWARR_MEMZERO(ptr, len) c_memzero(ptr, len)
#define SLOWARR_MEMMOVE(dest, src, ln) c_memmove(dest, src, ln)
#define SLOWARR_SZT unsigned long
#define SLOWARR_REALLOC(ptr, old, news) ((void *)0)
#define SLOWARR_FREE(ptr, size) /**/
#define SLOWARR_ON_MALLOC_FAIL(x) /**/
#define SLOW_DEFINE_ACCESS
#include "../slowarr.h"
typedef char const *cstr;
SLOWARR_Header(cstr);
SLOWARR_Impl(cstr);
int main(int argc, char const **argv) {
T(SLOWARR, cstr) arr = F(SLOWARR, cstr, borrow)(argv, argc);
(void)arr;
return 0;
}

12
slowarr/std1.c Normal file
View File

@@ -0,0 +1,12 @@
#define SLOW_DEFINE_ACCESS
#include "../slowarr.h"
typedef char const *cstr;
SLOWARR_Header(cstr);
SLOWARR_Impl(cstr);
int main(int argc, char const **argv) {
T(SLOWARR, cstr) arr = F(SLOWARR, cstr, borrow)(argv, argc);
(void)arr;
return 0;
}

16
slowarr/std1.cxx Normal file
View File

@@ -0,0 +1,16 @@
#include <vector>
#define SLOW_DEFINE_ACCESS
#include "../slowarr.h"
typedef char const *cstr;
SLOWARR_Header(cstr);
SLOWARR_Impl(cstr);
int main(int argc, char const **argv) {
T(SLOWARR, cstr) arr = F(SLOWARR, cstr, borrow)(argv, argc);
SlowArr<cstr> arrx = {arr};
(void)arr;
(void)arrx;
std::vector<cstr> vec(arrx.begin(), arrx.end());
(void)vec;
}

367
slowgraph.h Normal file
View File

@@ -0,0 +1,367 @@
/* define SLOWGRAPH_IMPL */
#ifndef SLOWGRAPH_COMMON_H
#define SLOWGRAPH_COMMON_H
#ifndef SLOWGRAPH_FUNC
#define SLOWGRAPH_FUNC /**/
#endif
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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

75
slowgraph/dgtxt.typ Normal file
View File

@@ -0,0 +1,75 @@
= dgtxt file format
Human readable file format for directed graphs
== Design goals
- any valid input to `tsort`, not containing `#`, is always valid dgtxt.
- removing every occureance of the RegEx `#.*?\n` should produce a valid input to `tsort` (if it doesn't contain cycles of course)
- easy to generate
- human readable
- easy to parse fully
== Concepts
=== Node
Has:
- incoming edges
- outgoing edges
- name
- key-value attributes
=== Edge
Has:
- source node
- destination node
- key-value attributes
== Syntax
Note that `{x}` means none or more times.
```ebnf
ident char = anything - '\n' - '\r' - ' ' - '#';
ident = ident char, { ident char };
node attribute = '##', {spaces},
ident, {spaces}, (* referred node *)
ident, {spaces}, (* attribute key *)
{ not new line } (* attribute value *)
;
comment = '#', { note new line };
edge attribute = '#:', {spaces}, ident, {spaces}, {not new line} ;
edge = ident, {spaces}, (* source node *)
ident, {spaces}, (* destination node *)
{ {spaces}, edge attribute, {spaces} }
;
element = edge
| node attribute
| comment
;
grammar = { spaces, element, spaces };
```
== Example
```
# comment line (ignored)
DownloadLinux FlashDrive
# double hashtass start node attributes:
## DownloadLinux describtion Attrbiute key/value formats are not standardized her
FindFlashDrive FlashDrive #: cost 200
#: another-attr-key another attr value (until end of line)
DownloadDriveFlashSoftware FlashDrive
FlashDrive ShutdownWindows
ShutdownWindows RestartPc
RestartPc InstallLinux
```
== Recommendations
- tools should prefix attribute names with the tool names, unless they think that other tools might adopt that attribute too.
- tools that modify graphs should preserve all attributes
- tools should not error on unknown attributes (or invalid attribute values), as they might be valid in a different tool
- actual comments should have a space after the `#`, for future proofing
- node attributes be listed directly after node definitions

63
slowgraph/sgraph-adj.c Normal file
View File

@@ -0,0 +1,63 @@
#define SLOWGRAPH_IMPL
#include "../slowgraph.h"
static void write_csv_field(char const *s) {
char const *y;
int quote = 0;
for (y = s; *y; y++) {
if (*y == '\n' || *y == '"') {
quote = 1;
break;
}
}
if (quote)
putchar('"');
for (; *s; s++) {
if (*s == '"')
putchar('"');
putchar(*s);
}
if (quote)
putchar('"');
}
static void dump_adj_matrix_row(SlowGraph *g, SlowGraphNode *row) {
SlowGraphNode *col;
write_csv_field(row->name);
printf(",");
for (col = g->first; col; col = col->next) {
if (SlowGraphNode_findConnection(row, col)) {
printf("1,");
} else {
printf("0,");
}
}
printf("\n");
}
static void dump_adj_matrix(SlowGraph *g) {
SlowGraphNode *n;
printf(",");
for (n = g->first; n; n = n->next) {
if (n != g->first)
printf(",");
write_csv_field(n->name);
}
printf("\n");
for (n = g->first; n; n = n->next) {
dump_adj_matrix_row(g, n);
}
}
int main(int argc, char **argv) {
SlowGraph graph = {0};
if (SlowGraph_readDGTXT(&graph, stdin))
return 1;
dump_adj_matrix(&graph);
return 0;
}

23
slowurl.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef SLOWURL_H_
#define SLOWURL_H_
#ifndef SLOWARR_H_
Please include and configure slowarr.h first;
#include "slowarr.h"
#endif
#ifndef SLOWURL_NAMESPACE
#define SLOWURL_NAMESPACE(X) slowurl_##X
#endif
SLOWARR_Header(char);
typedef struct {
} SLOWURL_NAMESPACE();
#ifdef SLOWURL_IMPLEMENTATION
#endif
#endif