From 337f069d5d99af20eed9860e2c6157ab1187fcda Mon Sep 17 00:00:00 2001 From: Alexander Nutz Date: Thu, 4 Sep 2025 23:14:40 +0200 Subject: [PATCH] c --- .gitignore | 5 + adj_matrix.c | 12 -- common.h | 201 ---------------------- lint.sh | 60 +++++++ slowarr.h | 247 +++++++++++++++++++++++++++ slowarr/nostd1.c | 39 +++++ slowarr/std1.c | 12 ++ slowarr/std1.cxx | 16 ++ slowgraph.h | 367 +++++++++++++++++++++++++++++++++++++++++ slowgraph/dgtxt.typ | 75 +++++++++ slowgraph/sgraph-adj.c | 63 +++++++ slowurl.h | 23 +++ 12 files changed, 907 insertions(+), 213 deletions(-) create mode 100644 .gitignore delete mode 100644 adj_matrix.c delete mode 100644 common.h create mode 100755 lint.sh create mode 100644 slowarr.h create mode 100644 slowarr/nostd1.c create mode 100644 slowarr/std1.c create mode 100644 slowarr/std1.cxx create mode 100644 slowgraph.h create mode 100644 slowgraph/dgtxt.typ create mode 100644 slowgraph/sgraph-adj.c create mode 100644 slowurl.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3074e8a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +a.out +*.plist +*.o +*.exe +core diff --git a/adj_matrix.c b/adj_matrix.c deleted file mode 100644 index 73587a6..0000000 --- a/adj_matrix.c +++ /dev/null @@ -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; -} diff --git a/common.h b/common.h deleted file mode 100644 index b461d93..0000000 --- a/common.h +++ /dev/null @@ -1,201 +0,0 @@ -#ifndef SLOWGRAPH_COMMON_H -#define SLOWGRAPH_COMMON_H - -#include -#include -#include -#include -#include - -#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 diff --git a/lint.sh b/lint.sh new file mode 100755 index 0000000..40dcbc2 --- /dev/null +++ b/lint.sh @@ -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 diff --git a/slowarr.h b/slowarr.h new file mode 100644 index 0000000..4ffca53 --- /dev/null +++ b/slowarr.h @@ -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 +#define SLOWARR_ASSERT_USER_ERROR(expr) assert(expr) +#endif + +#ifndef SLOWARR_MEMZERO +#include +#define SLOWARR_MEMZERO(ptr, len) memset(ptr, 0, len) +#endif + +#ifndef SLOWARR_MEMMOVE +#include +#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 +#define SLOWARR_SZT size_t +#endif + +#ifndef SLOWARR_REALLOC +#include +#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 +#include +#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 struct SLOWARR_CXXT {}; + +#define SLOWARR_CXX_HEADER(T) \ + template <> struct SLOWARR_CXXT { \ + 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 diff --git a/slowarr/nostd1.c b/slowarr/nostd1.c new file mode 100644 index 0000000..ee8a38a --- /dev/null +++ b/slowarr/nostd1.c @@ -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; +} diff --git a/slowarr/std1.c b/slowarr/std1.c new file mode 100644 index 0000000..a59493e --- /dev/null +++ b/slowarr/std1.c @@ -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; +} diff --git a/slowarr/std1.cxx b/slowarr/std1.cxx new file mode 100644 index 0000000..3ce18a9 --- /dev/null +++ b/slowarr/std1.cxx @@ -0,0 +1,16 @@ +#include +#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 arrx = {arr}; + (void)arr; + (void)arrx; + std::vector vec(arrx.begin(), arrx.end()); + (void)vec; +} diff --git a/slowgraph.h b/slowgraph.h new file mode 100644 index 0000000..17e52cf --- /dev/null +++ b/slowgraph.h @@ -0,0 +1,367 @@ +/* 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 diff --git a/slowgraph/dgtxt.typ b/slowgraph/dgtxt.typ new file mode 100644 index 0000000..58b7c59 --- /dev/null +++ b/slowgraph/dgtxt.typ @@ -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 diff --git a/slowgraph/sgraph-adj.c b/slowgraph/sgraph-adj.c new file mode 100644 index 0000000..23c2332 --- /dev/null +++ b/slowgraph/sgraph-adj.c @@ -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; +} diff --git a/slowurl.h b/slowurl.h new file mode 100644 index 0000000..c681aec --- /dev/null +++ b/slowurl.h @@ -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