summaryrefslogtreecommitdiffhomepage
path: root/src/build
diff options
context:
space:
mode:
Diffstat (limited to 'src/build')
-rw-r--r--src/build/cmeta.c2
-rw-r--r--src/build/cmeta.h2
-rw-r--r--src/build/codegen.c2
-rw-r--r--src/build/mkentprops.c208
-rw-r--r--src/build/mkgamedata.c2
-rw-r--r--src/build/skiplist.h205
6 files changed, 417 insertions, 4 deletions
diff --git a/src/build/cmeta.c b/src/build/cmeta.c
index 3d9281a..bf18903 100644
--- a/src/build/cmeta.c
+++ b/src/build/cmeta.c
@@ -5,7 +5,7 @@
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
diff --git a/src/build/cmeta.h b/src/build/cmeta.h
index 20672f4..4757654 100644
--- a/src/build/cmeta.h
+++ b/src/build/cmeta.h
@@ -5,7 +5,7 @@
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
diff --git a/src/build/codegen.c b/src/build/codegen.c
index 6d5fc99..d96059d 100644
--- a/src/build/codegen.c
+++ b/src/build/codegen.c
@@ -5,7 +5,7 @@
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
diff --git a/src/build/mkentprops.c b/src/build/mkentprops.c
new file mode 100644
index 0000000..5dd2fea
--- /dev/null
+++ b/src/build/mkentprops.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../intdefs.h"
+#include "../kv.h"
+#include "../noreturn.h"
+#include "../os.h"
+#include "skiplist.h"
+#include "vec.h"
+
+#ifdef _WIN32
+#define fS "S"
+#else
+#define fS "s"
+#endif
+
+static noreturn die(const char *s) {
+ fprintf(stderr, "mkentprops: %s\n", s);
+ exit(100);
+}
+
+struct prop {
+ const char *varname; /* the C global name */
+ const char *propname; /* the entity property name */
+ struct prop *next;
+};
+struct vec_prop VEC(struct prop *);
+
+DECL_SKIPLIST(static, class, struct class, const char *, 4)
+struct class {
+ const char *name; /* the entity class name */
+ struct vec_prop props;
+ struct skiplist_hdr_class hdr;
+};
+static inline int cmp_class(struct class *c, const char *s) {
+ return strcmp(c->name, s);
+}
+static inline struct skiplist_hdr_class *hdr_class(struct class *c) {
+ return &c->hdr;
+}
+DEF_SKIPLIST(static, class, cmp_class, hdr_class)
+static struct skiplist_hdr_class classes = {0};
+static int nclasses = 0;
+
+struct parsestate {
+ const os_char *filename;
+ struct kv_parser *parser;
+ char *lastvar;
+};
+
+static noreturn badparse(struct parsestate *state, const char *e) {
+ fprintf(stderr, "mkentprops: %" fS ":%d:%d: parse error: %s",
+ state->filename, state->parser->line, state->parser->col, e);
+ exit(1);
+}
+
+static void kv_cb(enum kv_token type, const char *p, uint len, void *ctxt) {
+ struct parsestate *state = ctxt;
+ switch (type) {
+ case KV_IDENT: case KV_IDENT_QUOTED:
+ state->lastvar = malloc(len + 1);
+ if (!state->lastvar) die("couldn't allocate memory");
+ memcpy(state->lastvar, p, len); state->lastvar[len] = '\0';
+ break;
+ case KV_NEST_START: badparse(state, "unexpected nested block");
+ case KV_NEST_END: badparse(state, "unexpected closing brace");
+ case KV_VAL: case KV_VAL_QUOTED:;
+ struct prop *prop = malloc(sizeof(*prop));
+ if (!p) die("couldn't allocate memory");
+ prop->varname = state->lastvar;
+ char *classname = malloc(len + 1);
+ if (!classname) die("couldn't allocate memory");
+ memcpy(classname, p, len); classname[len] = '\0';
+ char *propname = strchr(classname, '/');
+ if (!propname) {
+ badparse(state, "network name not in class/prop format");
+ }
+ *propname = '\0'; ++propname; // split!
+ prop->propname = propname;
+ struct class *class = skiplist_get_class(&classes, classname);
+ if (!class) {
+ class = malloc(sizeof(*class));
+ if (!class) die("couldn't allocate memory");
+ *class = (struct class){.name = classname};
+ skiplist_insert_class(&classes, classname, class);
+ ++nclasses;
+ }
+ // (if class is already there just leak classname, no point freeing)
+ if (!vec_push(&class->props, prop)) die("couldn't append to array");
+ break;
+ case KV_COND_PREFIX: case KV_COND_SUFFIX:
+ badparse(state, "unexpected conditional");
+ }
+}
+
+static inline noreturn diewrite(void) { die("couldn't write to file"); }
+
+#define _(x) \
+ if (fprintf(out, "%s\n", x) < 0) diewrite();
+#define F(f, ...) \
+ if (fprintf(out, f "\n", __VA_ARGS__) < 0) diewrite();
+#define H() \
+_( "/* This file is autogenerated by src/build/mkentprops.c. DO NOT EDIT! */") \
+_( "")
+
+static void decls(FILE *out) {
+ for (struct class *c = classes.x[0]; c; c = c->hdr.x[0]) {
+ for (struct prop **pp = c->props.data;
+ pp - c->props.data < c->props.sz; ++pp) {
+F( "extern bool has_%s;", (*pp)->varname)
+F( "extern int %s;", (*pp)->varname)
+ }
+ }
+}
+
+static void defs(FILE *out) {
+ for (struct class *c = classes.x[0]; c; c = c->hdr.x[0]) {
+ for (struct prop **pp = c->props.data;
+ pp - c->props.data < c->props.sz; ++pp) {
+F( "bool has_%s = false;", (*pp)->varname)
+F( "int %s;", (*pp)->varname)
+ }
+ }
+_( "")
+_( "static void initentprops(struct ServerClass *class) {")
+F( " for (int needclasses = %d; class; class = class->next) {", nclasses)
+ char *else1 = "";
+ for (struct class *c = classes.x[0]; c; c = c->hdr.x[0]) {
+ // TODO(opt): some sort of PHF instead of chained strcmp, if we ever
+ // have more than a few classes/properties?
+F( " %sif (!strcmp(class->name, \"%s\")) {", else1, c->name)
+_( " struct SendTable *st = class->table;")
+ // christ this is awful :(
+F( " int needprops = %d;", c->props.sz)
+_( " for (struct SendProp *p = st->props; (char *)p -")
+_( " (char *)st->props < st->nprops * sz_SendProp;")
+_( " p = mem_offset(p, sz_SendProp)) {")
+ char *else2 = "";
+ for (struct prop **pp = c->props.data;
+ pp - c->props.data < c->props.sz; ++pp) {
+F( " %sif (!strcmp(*(const char **)mem_offset(p, off_SP_varname), \"%s\")) {",
+ else2, (*pp)->propname) // ugh
+F( " has_%s = true;", (*pp)->varname)
+F( " %s = *(int *)mem_offset(p, off_SP_offset);", (*pp)->varname)
+_( " if (!--needprops) break;")
+_( " }")
+ else2 = "else ";
+ }
+_( " }")
+_( " if (!--needclasses) break;")
+_( " }")
+ else1 = "else ";
+ }
+_( " }")
+_( "}")
+}
+
+int OS_MAIN(int argc, os_char *argv[]) {
+ for (++argv; *argv; ++argv) {
+ int fd = os_open(*argv, O_RDONLY);
+ if (fd == -1) die("couldn't open file");
+ struct kv_parser kv = {0};
+ struct parsestate state = {*argv, &kv};
+ char buf[1024];
+ int nread;
+ while (nread = read(fd, buf, sizeof(buf))) {
+ if (nread == -1) die("couldn't read file");
+ if (!kv_parser_feed(&kv, buf, nread, &kv_cb, &state)) goto ep;
+ }
+ if (!kv_parser_done(&kv)) {
+ep: fprintf(stderr, "mkentprops: %" fS ":%d:%d: bad syntax: %s\n",
+ *argv, kv.line, kv.col, kv.errmsg);
+ exit(1);
+ }
+ close(fd);
+ }
+
+ FILE *out = fopen(".build/include/entprops.gen.h", "wb");
+ if (!out) die("couldn't open entprops.gen.h");
+ H();
+ decls(out);
+
+ out = fopen(".build/include/entpropsinit.gen.h", "wb");
+ if (!out) die("couldn't open entpropsinit.gen.h");
+ H();
+ defs(out);
+ return 0;
+}
+
+// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/build/mkgamedata.c b/src/build/mkgamedata.c
index d17d7df..ce6490e 100644
--- a/src/build/mkgamedata.c
+++ b/src/build/mkgamedata.c
@@ -5,7 +5,7 @@
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
diff --git a/src/build/skiplist.h b/src/build/skiplist.h
new file mode 100644
index 0000000..b747d89
--- /dev/null
+++ b/src/build/skiplist.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INC_SKIPLIST_H
+#define INC_SKIPLIST_H
+
+#include <stdlib.h>
+
+#include "../intdefs.h"
+#include "../os.h"
+
+#ifdef _WIN32
+static inline int _skiplist_ffs(uint x) {
+ uint ret;
+ // on Windows, sizeof(ulong) == sizeof(uint)
+ if (_BitScanForward((ulong *)&ret, x)) return ret + 1; else return 0;
+}
+#else
+#include <strings.h>
+#define _skiplist_ffs ffs
+#endif
+
+// WARNING: this is a really hacked-up version of the skiplist.h from cbits in
+// order to support windows. It probably isn't a good idea to plop straight into
+// your own use case.
+
+#if defined(__GNUC__) || defined(__clang__)
+#define _skiplist_unused __attribute__((unused)) // heck off gcc
+#else
+#define _skiplist_unused
+#endif
+
+// NOTE: using xoroshiro128++, a comparatively bad (i.e. non-cryptographic) prng
+// for the sake of simplicity; original cbits skiplist.h relies on libcpoly to
+// get arc4random() everywhere but since we're only using this at build time
+// that seemed like a silly dependency to bother with.
+//#define _skiplist_rng arc4random
+
+// ALSO NOTE: the PRNG code here is *decidedly not* thread safe. again, this
+// isn't a problem for our use case. just keep it in mind if reusing this header
+// for something else. or ideally, don't reuse this header for something else...
+static inline uvlong _skiplist_rotl(const uvlong x, int k) {
+ return (x << k) | (x >> (64 - k));
+}
+_skiplist_unused static uvlong _skiplist_rng(void) {
+ static uvlong s[2];
+ static bool init = false;
+ if (!init) { os_randombytes(s, sizeof(s)); init = true; }
+ uvlong s0 = s[0], s1 = s[1];
+ uvlong ret = _skiplist_rotl(s0 * 5, 7) * 9;
+ s1 ^= s0;
+ s[0] = _skiplist_rotl(s0, 24) ^ s1 ^ (s1 << 16);
+ s[1] = _skiplist_rotl(s1, 37);
+ return ret;
+}
+
+/*
+ * Declares the skiplist header struct skiplist_hdr##name, but none of the
+ * associated functions. Use when the structure needs to be passed around in
+ * some way but actual operations on the list are a private implementation
+ * detail. Otherwise, see DECL_SKIPLIST below.
+ */
+#define DECL_SKIPLIST_TYPE(name, dtype, ktype, levels) \
+typedef dtype _skiplist_dt_##name; \
+typedef ktype _skiplist_kt_##name; \
+enum { skiplist_lvls_##name = (levels) }; \
+struct skiplist_hdr_##name { dtype *x[levels]; };
+
+/*
+ * Declares the skiplist header struct skiplist_hdr_##name, with functions
+ * skiplist_{get,del,pop,insert}_##name for operating on the list. A single
+ * occurrence of DEF_SKIPLIST is required to actually implement the
+ * functions.
+ *
+ * This macro implies DECL_SKIPLIST_TYPE (both should not be used).
+ *
+ * mod should be either static or extern.
+ *
+ * dtype should be the struct type that the skiplist header will be embedded in,
+ * forming the linked structure.
+ *
+ * ktype should be the type of the struct member used for comparisons, for
+ * example int or char *.
+ *
+ * levels should be the number of levels in each node. 4 is probably a
+ * reasonable number, depending on the size of the structure and how many
+ * entries need to be stored and looked up.
+ *
+ * The resulting get, del, pop and insert functions are hopefully self-
+ * explanatory - get and del return the relevant node or a null pointer if
+ * no such node is found.
+ */
+#define DECL_SKIPLIST(mod, name, dtype, ktype, levels) \
+DECL_SKIPLIST_TYPE(name, dtype, ktype, levels) \
+\
+_skiplist_unused mod dtype *skiplist_get_##name(struct skiplist_hdr_##name *l, \
+ ktype k); \
+_skiplist_unused mod dtype *skiplist_del_##name(struct skiplist_hdr_##name *l, \
+ ktype k); \
+_skiplist_unused mod dtype *skiplist_pop_##name(struct skiplist_hdr_##name *l); \
+_skiplist_unused mod void skiplist_insert_##name(struct skiplist_hdr_##name *l, \
+ ktype k, dtype *node);
+
+/*
+ * Implements the functions corresponding to a skiplist - must come after
+ * DECL_SKIPLIST with the same modifier and name.
+ *
+ * compfunc should be a function declared as follows (or an equivalent macro):
+ * int cf(dtype *x, ktype y);
+ *
+ * hdrfunc should be a function declared as follows (or an equivalent macro):
+ * struct skiplist_hdr_##name *hf(dtype *l);
+ */
+#define DEF_SKIPLIST(mod, name, compfunc, hdrfunc) \
+static inline int _skiplist_lvl_##name(void) { \
+ int i; \
+ /* for 2 levels we get 1 50% of the time, 2 25% of the time, 0 25% of the
+ time. loop if 0 to distribute this evenly (this gets less likely the more
+ levels there are: at 4 levels, only loops 6% of the time) */ \
+ while (!(i = _skiplist_ffs(_skiplist_rng() & \
+ ((1 << skiplist_lvls_##name) - 1)))); \
+ /* ffs gives bit positions as 1-N but we actually want an array index */ \
+ return i - 1; \
+} \
+\
+_skiplist_unused \
+mod _skiplist_dt_##name *skiplist_get_##name(struct skiplist_hdr_##name *l, \
+ _skiplist_kt_##name k) { \
+ for (int cmp, lvl = skiplist_lvls_##name - 1; lvl > -1; --lvl) { \
+ while (l->x[lvl] && (cmp = compfunc(l->x[lvl], k)) < 0) { \
+ l = hdrfunc(l->x[lvl]); \
+ } \
+ /* NOTE: cmp can be uninitialised here, but only if the list is
+ completely empty, in which case we'd return 0 anyway - so it doesn't
+ actually matter! */ \
+ if (cmp == 0) return l->x[lvl]; \
+ } \
+ /* reached the end, no match */ \
+ return 0; \
+} \
+\
+_skiplist_unused \
+_skiplist_dt_##name *skiplist_del_##name(struct skiplist_hdr_##name *l, \
+ _skiplist_kt_##name k) { \
+ _skiplist_dt_##name *ret = 0; \
+ /* ALSO NOTE: in *this* case, cmp DOES need to be initialised to prevent a
+ possible null-deref via hdrfunc(l->x[lvl])->x */ \
+ for (int cmp = 1, lvl = skiplist_lvls_##name - 1; lvl > -1; --lvl) { \
+ while (l->x[lvl] && (cmp = compfunc(l->x[lvl], k)) < 0) { \
+ l = hdrfunc(l->x[lvl]); \
+ } \
+ if (cmp == 0) { \
+ ret = l->x[lvl]; \
+ /* just shift each link by 1 */ \
+ l->x[lvl] = hdrfunc(l->x[lvl])->x[0]; \
+ /* ... and update every level of links via loop */ \
+ } \
+ } \
+ /* reached the end, return whatever was found */ \
+ return ret; \
+} \
+\
+_skiplist_unused \
+mod _skiplist_dt_##name *skiplist_pop_##name(struct skiplist_hdr_##name *l) { \
+ _skiplist_dt_##name *cur = l->x[0]; \
+ if (!cur) return 0; \
+ l->x[0] = hdrfunc(cur)->x[0]; \
+ for (int lvl = 1; lvl < skiplist_lvls_##name; ++lvl) { \
+ if (l->x[lvl]) l->x[lvl] = hdrfunc(l->x[lvl])->x[lvl]; \
+ } \
+ return cur; \
+} \
+\
+_skiplist_unused \
+mod void skiplist_insert_##name(struct skiplist_hdr_##name *l, \
+ _skiplist_kt_##name k, _skiplist_dt_##name *node) { \
+ /* note: higher levels are unset but also skipped in other searches */ \
+ int inslvl = _skiplist_lvl_##name(); \
+ for (int lvl = skiplist_lvls_##name - 1; lvl > -1; --lvl) { \
+ while (l->x[lvl] && compfunc(l->x[lvl], k) < 0) { \
+ l = hdrfunc(l->x[lvl]); \
+ } \
+ if (lvl <= inslvl) { \
+ hdrfunc(node)->x[lvl] = l->x[lvl]; \
+ l->x[lvl] = node; \
+ } \
+ } \
+} \
+
+#endif
+
+// vi: sw=4 ts=4 noet tw=80 cc=80