commit 2522effc2444a6cb4eb9f3b6168d344983d72170
Author: Louis Burda <quent.burda@gmail.com>
Date: Thu, 16 Mar 2023 01:09:07 +0100
Initial version
Diffstat:
A | .gitignore | | | 3 | +++ |
A | Makefile | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
A | include/stl.h | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | libstl.api | | | 2 | ++ |
A | libstl.lds | | | 6 | ++++++ |
A | src/bench.c | | | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/stl.c | | | 366 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
7 files changed, 564 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+build
+compile_commands.json
+.cache
diff --git a/Makefile b/Makefile
@@ -0,0 +1,38 @@
+PREFIX ?= /usr/local
+LIBDIR ?= /lib
+
+CFLAGS = -I include
+
+ifeq "$(DEBUG)" "1"
+CFLAGS += -g
+endif
+
+all: build/libstl.a build/libstl.so build/bench
+
+clean:
+ rm -rf build
+
+build:
+ mkdir build
+
+build/libstl.a: src/stl.c include/stl.h libstl.api | build
+ $(CC) -c -o build/tmp.o $< $(CFLAGS) $(LDLIBS)
+ objcopy --keep-global-symbols=libstl.api build/tmp.o build/fixed.o
+ ar rcs $@ build/fixed.o
+
+build/libstl.so: src/stl.c include/stl.h libstl.lds | build
+ $(CC) -o $@ $< -fPIC $(CFLAGS) \
+ -shared -Wl,-version-script libstl.lds
+
+build/bench: src/bench.c build/libstl.a | build
+ $(CC) -o $@ $^ $(CFLAGS) $(LDLIBS)
+
+install:
+ install -m755 build/libstl.a -t "$(DESTDIR)$(PREFIX)$(LIBDIR)"
+ install -m755 build/libstl.so -t "$(DESTDIR)$(PREFIX)$(LIBDIR)"
+
+uninstall:
+ rm -f "$(DESTDIR)$(PREFIX)$(LIBDIR)/libstl.a"
+ rm -f "$(DESTDIR)$(PREFIX)$(LIBDIR)/libstl.so"
+
+.PHONY: all clean install uninstall
diff --git a/include/stl.h b/include/stl.h
@@ -0,0 +1,92 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#define STL_BUFMAX 256
+
+#define STL_STRERROR_INIT \
+ [STL_OK] = "Success", \
+ [STL_DONE] = "Done processing", \
+ [STL_INVALID] = "Invalid stl file", \
+ [STL_INVALID_ARG] = "Invalid argument", \
+ [STL_INCOMPLETE] = "Unexpected end of file" \
+
+enum {
+ STL_OK,
+ STL_DONE,
+ STL_INVALID,
+ STL_INVALID_ARG,
+ STL_INCOMPLETE,
+};
+
+enum {
+ STL_TYPE_DETECT,
+ STL_TYPE_ASCII,
+ STL_TYPE_BINARY,
+};
+
+enum {
+ STL_RES_SOLID_NAME,
+ STL_RES_HEADER,
+ STL_RES_TRIANGLE,
+ STL_RES_FILETYPE
+};
+
+struct stl_vertex {
+ union {
+ struct {
+ float x, y, z;
+ };
+ struct {
+ float dim[3];
+ };
+ };
+};
+
+struct stl_triangle {
+ struct stl_vertex vtx[3];
+ struct stl_vertex normal;
+};
+
+struct stl_span {
+ const char *str;
+ size_t len;
+};
+
+struct stl_result {
+ int type;
+ union {
+ struct stl_span solid_name;
+ struct stl_span header;
+ struct stl_triangle tri;
+ int filetype;
+ };
+};
+
+struct stl {
+ char buf[STL_BUFMAX];
+ size_t buflen;
+
+ const void *chunk;
+ const void *pos;
+ size_t nleft;
+
+ struct stl_triangle tri;
+
+ int type;
+ union {
+ struct {
+ int state;
+ } ascii;
+ struct {
+ int state;
+ size_t index;
+ size_t count;
+ } bin;
+ };
+};
+
+void stl_init(struct stl *stl, int type);
+int stl_feed(struct stl *stl, struct stl_result *res,
+ const void *chunk, size_t size);
diff --git a/libstl.api b/libstl.api
@@ -0,0 +1,2 @@
+stl_init
+stl_feed
diff --git a/libstl.lds b/libstl.lds
@@ -0,0 +1,6 @@
+LIBSTL_1.0 {
+ global:
+ stl_init;
+ stl_feed;
+ local: *;
+};
diff --git a/src/bench.c b/src/bench.c
@@ -0,0 +1,57 @@
+#include "stl.h"
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static char buf[BUFSIZ];
+
+static char *stl_err[] = {
+ STL_STRERROR_INIT
+};
+
+int
+main(int argc, const char **argv)
+{
+ struct stl_result res;
+ struct stl stl;
+ size_t count;
+ int n, rc;
+
+ stl_init(&stl, STL_TYPE_DETECT);
+
+ count = 0;
+ while (1) {
+ n = fread(buf, 1, BUFSIZ, stdin);
+ if (n <= 0) errx(1, "truncated input");
+
+ while (1) {
+ rc = stl_feed(&stl, &res, buf, n);
+ if (rc == STL_DONE) break;
+ if (rc == STL_INCOMPLETE) break;
+ if (rc != STL_OK) errx(1, "libstl: %s", stl_err[rc]);
+
+ switch (res.type) {
+ case STL_RES_FILETYPE:
+ printf("filetype: %s\n",
+ res.filetype == STL_TYPE_ASCII
+ ? "ascii" : "binary");
+ break;
+ case STL_RES_HEADER:
+ printf("header: %s\n", res.header.str);
+ break;
+ case STL_RES_SOLID_NAME:
+ printf("name: %s\n", res.solid_name.str);
+ break;
+ case STL_RES_TRIANGLE:
+ count += 1;
+ break;
+ }
+ }
+
+ if (rc == STL_DONE)
+ break;
+ }
+
+ printf("triangles: %lu\n", count);
+}
diff --git a/src/stl.c b/src/stl.c
@@ -0,0 +1,366 @@
+#include "stl.h"
+
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) > (b) ? (b) : (a))
+
+enum {
+ STL_ASCII_SOLID,
+ STL_ASCII_SOLID_NAME,
+ STL_ASCII_ENDSOLID_CHECK,
+ STL_ASCII_FACET_NORMAL,
+ STL_ASCII_FACET_NORMAL_I,
+ STL_ASCII_FACET_NORMAL_J,
+ STL_ASCII_FACET_NORMAL_K,
+ STL_ASCII_OUTER_LOOP,
+ STL_ASCII_VERTEX_1,
+ STL_ASCII_VERTEX_1_X,
+ STL_ASCII_VERTEX_1_Y,
+ STL_ASCII_VERTEX_1_Z,
+ STL_ASCII_VERTEX_2,
+ STL_ASCII_VERTEX_2_X,
+ STL_ASCII_VERTEX_2_Y,
+ STL_ASCII_VERTEX_2_Z,
+ STL_ASCII_VERTEX_3,
+ STL_ASCII_VERTEX_3_X,
+ STL_ASCII_VERTEX_3_Y,
+ STL_ASCII_VERTEX_3_Z,
+ STL_ASCII_ENDLOOP,
+ STL_ASCII_ENDFACET,
+ STL_ASCII_ENDSOLID
+};
+
+enum {
+ STL_BIN_HEADER,
+ STL_BIN_TRIANGLE
+};
+
+static const uint8_t ascii_fsm[] = {
+ [STL_ASCII_SOLID] = STL_ASCII_SOLID_NAME,
+ [STL_ASCII_SOLID_NAME] = STL_ASCII_ENDSOLID_CHECK,
+ [STL_ASCII_ENDSOLID_CHECK] = STL_ASCII_FACET_NORMAL,
+ [STL_ASCII_FACET_NORMAL] = STL_ASCII_FACET_NORMAL_I,
+ [STL_ASCII_FACET_NORMAL_I] = STL_ASCII_FACET_NORMAL_K,
+ [STL_ASCII_FACET_NORMAL_K] = STL_ASCII_FACET_NORMAL_J,
+ [STL_ASCII_FACET_NORMAL_J] = STL_ASCII_OUTER_LOOP,
+ [STL_ASCII_OUTER_LOOP] = STL_ASCII_VERTEX_1,
+ [STL_ASCII_VERTEX_1] = STL_ASCII_VERTEX_1_X,
+ [STL_ASCII_VERTEX_1_X] = STL_ASCII_VERTEX_1_Y,
+ [STL_ASCII_VERTEX_1_Y] = STL_ASCII_VERTEX_1_Z,
+ [STL_ASCII_VERTEX_1_Z] = STL_ASCII_VERTEX_2,
+ [STL_ASCII_VERTEX_2] = STL_ASCII_VERTEX_2_X,
+ [STL_ASCII_VERTEX_2_X] = STL_ASCII_VERTEX_2_Y,
+ [STL_ASCII_VERTEX_2_Y] = STL_ASCII_VERTEX_2_Z,
+ [STL_ASCII_VERTEX_2_Z] = STL_ASCII_VERTEX_3,
+ [STL_ASCII_VERTEX_3] = STL_ASCII_VERTEX_3_X,
+ [STL_ASCII_VERTEX_3_X] = STL_ASCII_VERTEX_3_Y,
+ [STL_ASCII_VERTEX_3_Y] = STL_ASCII_VERTEX_3_Z,
+ [STL_ASCII_VERTEX_3_Z] = STL_ASCII_ENDLOOP,
+ [STL_ASCII_ENDLOOP] = STL_ASCII_ENDFACET,
+ [STL_ASCII_ENDFACET] = STL_ASCII_ENDSOLID_CHECK,
+};
+
+static void
+stl_skip_spn(struct stl *stl, const char *spn)
+{
+ const char *c;
+ size_t i;
+
+ c = stl->pos;
+ for (i = 0; i < stl->nleft; i++, c++) {
+ if (!strchr(spn, *c))
+ break;
+ }
+ stl->pos += i;
+ stl->nleft -= i;
+}
+
+static int
+stl_read_n(struct stl *stl, size_t n)
+{
+ size_t cnt;
+
+ if (stl->buflen >= n)
+ return STL_OK;
+
+ cnt = MIN(n - stl->buflen, stl->nleft);
+ memcpy(stl->buf + stl->buflen, stl->pos, cnt);
+ stl->buflen += cnt;
+ stl->pos += cnt;
+ stl->nleft -= cnt;
+
+ if (stl->buflen < n)
+ return STL_INCOMPLETE;
+
+ return STL_OK;
+}
+
+static int
+stl_read_expect(struct stl *stl, const char *str, size_t n)
+{
+ size_t i, cnt;
+ int rc;
+
+ rc = stl_read_n(stl, n);
+ if (rc) return rc;
+
+ if (strncmp(stl->buf, str, n))
+ return STL_INVALID;
+
+ stl->buflen = 0;
+
+ return STL_OK;
+}
+
+static int
+stl_read_until(struct stl *stl, size_t *len, char *any, size_t max)
+{
+ const char *c;
+ size_t i;
+
+ c = stl->pos;
+ for (i = 0; i < stl->nleft; i++, c++) {
+ if (strchr(any, *c)) {
+ *len = stl->buflen;
+ stl->buflen = 0;
+ return STL_OK;
+ }
+ if (stl->buflen >= max)
+ return STL_INVALID;
+ stl->buf[stl->buflen++] = *c;
+ stl->pos++;
+ stl->nleft--;
+ }
+
+ return STL_INCOMPLETE;
+}
+
+static int
+stl_feed_binary(struct stl *stl, struct stl_result *res,
+ const void *chunk, size_t size)
+{
+ int rc;
+
+ if (stl->bin.index == stl->bin.count)
+ return STL_DONE;
+
+ while (stl->nleft > 0) {
+ switch (stl->bin.state) {
+ case STL_BIN_HEADER:
+ rc = stl_read_n(stl, 84);
+ if (rc) return rc;
+ stl->bin.count = le32toh(*(uint32_t *)(stl->buf + 80));
+ stl->bin.state = STL_BIN_TRIANGLE;
+ res->type = STL_RES_HEADER;
+ res->header.str = stl->buf;
+ res->header.len = 80;
+ return STL_OK;
+ case STL_BIN_TRIANGLE:
+ rc = stl_read_n(stl, 50);
+ if (rc) return rc;
+ res->type = STL_RES_TRIANGLE;
+ res->tri.normal.x = le32toh(*(uint32_t *)(stl->buf + 0));
+ res->tri.normal.y = le32toh(*(uint32_t *)(stl->buf + 4));
+ res->tri.normal.z = le32toh(*(uint32_t *)(stl->buf + 8));
+ res->tri.vtx[0].x = le32toh(*(uint32_t *)(stl->buf + 12));
+ res->tri.vtx[0].y = le32toh(*(uint32_t *)(stl->buf + 16));
+ res->tri.vtx[0].z = le32toh(*(uint32_t *)(stl->buf + 20));
+ res->tri.vtx[1].x = le32toh(*(uint32_t *)(stl->buf + 24));
+ res->tri.vtx[1].y = le32toh(*(uint32_t *)(stl->buf + 28));
+ res->tri.vtx[1].z = le32toh(*(uint32_t *)(stl->buf + 32));
+ res->tri.vtx[2].x = le32toh(*(uint32_t *)(stl->buf + 36));
+ res->tri.vtx[2].y = le32toh(*(uint32_t *)(stl->buf + 40));
+ res->tri.vtx[2].z = le32toh(*(uint32_t *)(stl->buf + 40));
+ stl->bin.index++;
+ return STL_OK;
+ default:
+ return STL_INVALID_ARG;
+ }
+ }
+
+ return STL_INCOMPLETE;
+}
+
+static int
+stl_feed_ascii(struct stl *stl, struct stl_result *res,
+ const void *chunk, size_t size)
+{
+ char *end;
+ float val;
+ size_t len;
+ int rc;
+
+ while (stl->nleft > 0) {
+ switch (stl->ascii.state) {
+ case STL_ASCII_SOLID:
+ rc = stl_read_expect(stl, "solid ", 6);
+ if (rc) return rc;
+ break;
+ case STL_ASCII_SOLID_NAME:
+ rc = stl_read_until(stl, &len, "\n", STL_BUFMAX - 1);
+ if (rc) return rc;
+ stl->buf[len] = '\0';
+ res->type = STL_RES_SOLID_NAME;
+ res->solid_name.str = stl->buf;
+ res->solid_name.len = len;
+ stl->ascii.state = STL_ASCII_FACET_NORMAL;
+ return STL_OK;
+ case STL_ASCII_FACET_NORMAL:
+ if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r");
+ rc = stl_read_expect(stl, "facet normal ", 13);
+ if (rc) return rc;
+ break;
+ case STL_ASCII_OUTER_LOOP:
+ if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r");
+ rc = stl_read_expect(stl, "outer loop", 10);
+ if (rc) return rc;
+ break;
+ case STL_ASCII_FACET_NORMAL_I:
+ case STL_ASCII_FACET_NORMAL_J:
+ case STL_ASCII_FACET_NORMAL_K:
+ case STL_ASCII_VERTEX_1_X:
+ case STL_ASCII_VERTEX_1_Y:
+ case STL_ASCII_VERTEX_1_Z:
+ case STL_ASCII_VERTEX_2_X:
+ case STL_ASCII_VERTEX_2_Y:
+ case STL_ASCII_VERTEX_2_Z:
+ case STL_ASCII_VERTEX_3_X:
+ case STL_ASCII_VERTEX_3_Y:
+ case STL_ASCII_VERTEX_3_Z:
+ if (!stl->buflen) stl_skip_spn(stl, " \t");
+ rc = stl_read_until(stl, &len, " \n", STL_BUFMAX - 1);
+ if (rc) return rc;
+ stl->buf[len] = '\0';
+ val = strtof(stl->buf, &end);
+ if (end && *end) return STL_INVALID;
+ switch (stl->ascii.state) {
+ case STL_ASCII_FACET_NORMAL_I:
+ stl->tri.normal.x = val;
+ break;
+ case STL_ASCII_FACET_NORMAL_J:
+ stl->tri.normal.y = val;
+ break;
+ case STL_ASCII_FACET_NORMAL_K:
+ stl->tri.normal.z = val;
+ break;
+ case STL_ASCII_VERTEX_1_X:
+ stl->tri.vtx[0].x = val;
+ break;
+ case STL_ASCII_VERTEX_1_Y:
+ stl->tri.vtx[0].y = val;
+ break;
+ case STL_ASCII_VERTEX_1_Z:
+ stl->tri.vtx[0].z = val;
+ break;
+ case STL_ASCII_VERTEX_2_X:
+ stl->tri.vtx[1].x = val;
+ break;
+ case STL_ASCII_VERTEX_2_Y:
+ stl->tri.vtx[1].y = val;
+ break;
+ case STL_ASCII_VERTEX_2_Z:
+ stl->tri.vtx[1].z = val;
+ break;
+ case STL_ASCII_VERTEX_3_X:
+ stl->tri.vtx[2].x = val;
+ break;
+ case STL_ASCII_VERTEX_3_Y:
+ stl->tri.vtx[2].y = val;
+ break;
+ case STL_ASCII_VERTEX_3_Z:
+ stl->tri.vtx[2].z = val;
+ break;
+ }
+ break;
+ case STL_ASCII_VERTEX_1:
+ case STL_ASCII_VERTEX_2:
+ case STL_ASCII_VERTEX_3:
+ if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r");
+ rc = stl_read_expect(stl, "vertex", 6);
+ if (rc) return rc;
+ break;
+ case STL_ASCII_ENDLOOP:
+ if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r");
+ rc = stl_read_expect(stl, "endloop", 7);
+ if (rc) return rc;
+ break;
+ case STL_ASCII_ENDFACET:
+ if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r");
+ rc = stl_read_expect(stl, "endfacet", 8);
+ if (rc) return rc;
+ res->type = STL_RES_TRIANGLE;
+ res->tri = stl->tri;
+ stl->ascii.state = STL_ASCII_ENDSOLID_CHECK;
+ return STL_OK;
+ case STL_ASCII_ENDSOLID_CHECK:
+ if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r");
+ rc = stl_read_expect(stl, "endsolid", 8);
+ if (!rc) return STL_DONE;
+ break;
+ defualt:
+ return STL_INVALID_ARG;
+ };
+ stl->ascii.state = ascii_fsm[stl->ascii.state];
+ }
+
+ return STL_INCOMPLETE;
+}
+
+void
+stl_init(struct stl *stl, int type)
+{
+ stl->type = type;
+ stl->buflen = 0;
+ stl->chunk = NULL;
+ stl->pos = NULL;
+ stl->nleft = 0;
+}
+
+int
+stl_feed(struct stl *stl, struct stl_result *res,
+ const void *chunk, size_t size)
+{
+ size_t cnt;
+ int rc;
+
+ if (stl->chunk != chunk) {
+ if (stl->nleft > 0)
+ return STL_INVALID;
+ stl->chunk = chunk;
+ stl->pos = stl->chunk;
+ stl->nleft = size;
+ }
+
+ if (stl->type == STL_TYPE_DETECT) {
+ rc = stl_read_expect(stl, "solid ", 6);
+ if (rc == STL_INCOMPLETE)
+ return STL_INCOMPLETE;
+ if (rc == STL_OK) {
+ printf("ascii file\n");
+ stl->type = STL_TYPE_ASCII;
+ stl->ascii.state = STL_ASCII_SOLID_NAME;
+ } else {
+ printf("binary file\n");
+ stl->type = STL_TYPE_BINARY;
+ stl->bin.state = STL_BIN_HEADER;
+ stl->bin.index = 0;
+ stl->bin.count = SIZE_MAX;
+ }
+ res->type = STL_RES_FILETYPE;
+ res->filetype = stl->type;
+ return STL_OK;
+ }
+
+ switch (stl->type) {
+ case STL_TYPE_ASCII:
+ return stl_feed_ascii(stl, res, chunk, size);
+ case STL_TYPE_BINARY:
+ return stl_feed_binary(stl, res, chunk, size);
+ default:
+ return STL_INVALID_ARG;
+ }
+}