summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2022-01-14 15:08:02 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2022-01-14 15:08:02 +0000
commit8d76eb12d5223d0fc3ea65d8d97f1feda2fcc340 (patch)
tree0fd0dd005a08843d538923c1a31d968a417dc4bf /src
parentInitial commit. (diff)
downloadtinyframe-upstream.tar.xz
tinyframe-upstream.zip
Adding upstream version 0.1.1.upstream/0.1.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am21
-rw-r--r--src/test/Makefile.am29
-rw-r--r--src/test/test.fstrmbin0 -> 242 bytes
-rw-r--r--src/test/test1.c125
-rwxr-xr-xsrc/test/test1.sh8
-rw-r--r--src/test/test2.c141
-rwxr-xr-xsrc/test/test2.sh4
-rw-r--r--src/test/test3.c93
-rwxr-xr-xsrc/test/test3.sh3
-rw-r--r--src/tinyframe.c403
-rw-r--r--src/tinyframe/tinyframe.h145
-rw-r--r--src/tinyframe/version.h.in31
12 files changed, 1003 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..723b783
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,21 @@
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+CLEANFILES = *.gcda *.gcno *.gcov
+
+EXTRA_DIST =
+
+SUBDIRS = test
+
+lib_LTLIBRARIES = libtinyframe.la
+
+libtinyframe_la_SOURCES = tinyframe.c
+nobase_include_HEADERS = tinyframe/tinyframe.h
+nobase_nodist_include_HEADERS = tinyframe/version.h
+libtinyframe_la_LDFLAGS = -version-info $(TINYFRAME_LIBRARY_VERSION)
+EXTRA_DIST += tinyframe/version.h.in
+
+if ENABLE_GCOV
+gcov-local:
+ for src in $(libtinyframe_la_SOURCES); do \
+ gcov -l -r -s "$(srcdir)" "$$src"; \
+ done
+endif
diff --git a/src/test/Makefile.am b/src/test/Makefile.am
new file mode 100644
index 0000000..5e0e8a2
--- /dev/null
+++ b/src/test/Makefile.am
@@ -0,0 +1,29 @@
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+CLEANFILES = test*.log test*.trs test2.fstrm *.gcda *.gcno *.gcov
+
+AM_CFLAGS = -I$(top_srcdir)/src
+
+check_PROGRAMS = test1 test2 test3
+TESTS = test1.sh test2.sh test3.sh
+EXTRA_DIST = $(TESTS)
+
+test1_SOURCES = test1.c
+test1_LDADD = ../libtinyframe.la
+test1_LDFLAGS = -static
+EXTRA_DIST += test.fstrm
+
+test2_SOURCES = test2.c
+test2_LDADD = ../libtinyframe.la
+test2_LDFLAGS = -static
+
+test3_SOURCES = test3.c
+test3_LDADD = ../libtinyframe.la
+test3_LDFLAGS = -static
+
+if ENABLE_GCOV
+gcov-local:
+ for src in $(test1_SOURCES) $(test2_SOURCES) $(test3_SOURCES); do \
+ gcov -l -r -s "$(srcdir)" "$$src"; \
+ done
+endif
diff --git a/src/test/test.fstrm b/src/test/test.fstrm
new file mode 100644
index 0000000..a3d164f
--- /dev/null
+++ b/src/test/test.fstrm
Binary files differ
diff --git a/src/test/test1.c b/src/test/test1.c
new file mode 100644
index 0000000..d6d18d1
--- /dev/null
+++ b/src/test/test1.c
@@ -0,0 +1,125 @@
+/*
+ * Author Jerry Lundström <jerry@dns-oarc.net>
+ * Copyright (c) 2020, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the tinyframe library.
+ *
+ * tinyframe library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * tinyframe library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with tinyframe library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <tinyframe/tinyframe.h>
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+static void
+print_string(const void* data, size_t len)
+{
+ uint8_t* str = (uint8_t*)data;
+ putc('"', stdout);
+ while (len-- != 0) {
+ unsigned c = *(str++);
+ if (isprint(c)) {
+ if (c == '"')
+ puts("\\\"");
+ else
+ putc(c, stdout);
+ } else {
+ printf("\\x%02x", c);
+ }
+ }
+ putc('"', stdout);
+}
+
+int main(int argc, const char* argv[])
+{
+ if (argc < 3) {
+ return 1;
+ }
+
+ FILE* fp = fopen(argv[1], "r");
+ if (!fp) {
+ return 2;
+ }
+
+ int rbuf_len = atoi(argv[2]);
+
+ struct tinyframe_reader h = TINYFRAME_READER_INITIALIZER;
+
+ size_t s = 0, r;
+ uint8_t buf[4096], rbuf[rbuf_len];
+ while ((r = fread(rbuf, 1, sizeof(rbuf), fp)) > 0) {
+ printf("read %zu\n", r);
+
+ if (s + r > sizeof(buf)) {
+ printf("overflow\n");
+ break;
+ }
+ memcpy(&buf[s], rbuf, r);
+ s += r;
+
+ int r = 1;
+ while (r) {
+ switch (tinyframe_read(&h, buf, s)) {
+ case tinyframe_have_control:
+ printf("control type %" PRIu32 " len %" PRIu32 "\n", h.control.type, h.control.length);
+ break;
+ case tinyframe_have_control_field:
+ printf("control_field type %" PRIu32 " len %" PRIu32 " data: ", h.control_field.type, h.control_field.length);
+ print_string(h.control_field.data, h.control_field.length);
+ printf("\n");
+ break;
+ case tinyframe_have_frame:
+ printf("frame len %" PRIu32 " data: ", h.frame.length);
+ print_string(h.frame.data, h.frame.length);
+ printf("\n");
+ break;
+ case tinyframe_need_more:
+ printf("need more\n");
+ r = 0;
+ break;
+ case tinyframe_error:
+ printf("error\n");
+ fclose(fp);
+ return 2;
+ case tinyframe_stopped:
+ printf("stopped\n");
+ fclose(fp);
+ return 0;
+ case tinyframe_finished:
+ printf("finished\n");
+ fclose(fp);
+ return 0;
+ default:
+ printf("unexpected return code\n");
+ fclose(fp);
+ return 3;
+ }
+
+ if (r && h.bytes_read && h.bytes_read <= s) {
+ s -= h.bytes_read;
+ if (s) {
+ memmove(buf, &buf[h.bytes_read], s);
+ }
+ }
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
diff --git a/src/test/test1.sh b/src/test/test1.sh
new file mode 100755
index 0000000..df3d86c
--- /dev/null
+++ b/src/test/test1.sh
@@ -0,0 +1,8 @@
+#!/bin/sh -xe
+
+./test1 "$srcdir/test.fstrm" 7
+./test1 "$srcdir/test.fstrm" 15
+./test1 "$srcdir/test.fstrm" 24
+./test1 "$srcdir/test.fstrm" 44
+./test1 "$srcdir/test.fstrm" 79
+./test1 "$srcdir/test.fstrm" 134
diff --git a/src/test/test2.c b/src/test/test2.c
new file mode 100644
index 0000000..c25198b
--- /dev/null
+++ b/src/test/test2.c
@@ -0,0 +1,141 @@
+/*
+ * Author Jerry Lundström <jerry@dns-oarc.net>
+ * Copyright (c) 2020, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the tinyframe library.
+ *
+ * tinyframe library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * tinyframe library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with tinyframe library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <tinyframe/tinyframe.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static char content_type[] = "tinyframe.test";
+
+int main(int argc, const char* argv[])
+{
+ if (argc < 3) {
+ return 1;
+ }
+
+ if (argv[1][0] == 'w') {
+ FILE* fp = fopen(argv[2], "w");
+ if (!fp) {
+ return 1;
+ }
+
+ struct tinyframe_writer writer = TINYFRAME_WRITER_INITIALIZER;
+
+ uint8_t out[sizeof(content_type) + (TINYFRAME_CONTROL_FRAME_LENGTH_MAX * 3)];
+ size_t len = sizeof(out);
+ size_t wrote = 0;
+
+ if (tinyframe_write_control_start(&writer, &out[wrote], len, content_type, sizeof(content_type) - 1) != tinyframe_ok) {
+ return 1;
+ }
+ wrote += writer.bytes_wrote;
+ len -= writer.bytes_wrote;
+
+ if (tinyframe_write_frame(&writer, &out[wrote], len, (uint8_t*)content_type, sizeof(content_type)) != tinyframe_ok) {
+ return 1;
+ }
+ wrote += writer.bytes_wrote;
+ len -= writer.bytes_wrote;
+
+ if (tinyframe_write_control_stop(&writer, &out[wrote], len) != tinyframe_ok) {
+ return 1;
+ }
+ wrote += writer.bytes_wrote;
+ len -= writer.bytes_wrote;
+
+ if (fwrite(out, 1, wrote, fp) != wrote) {
+ return 1;
+ }
+
+ fclose(fp);
+ return 0;
+ } else if (argv[1][0] == 'r') {
+ FILE* fp = fopen(argv[2], "r");
+ if (!fp) {
+ return 1;
+ }
+
+ long content_length;
+
+ if (fseek(fp, 0, SEEK_END)) {
+ return 1;
+ }
+ content_length = ftell(fp);
+ if (fseek(fp, 0, SEEK_SET)) {
+ return 1;
+ }
+
+ uint8_t* content = malloc(content_length);
+ if (!content) {
+ return 1;
+ }
+
+ if (fread(content, 1, content_length, fp) != content_length) {
+ return 1;
+ }
+
+ fclose(fp);
+
+ struct tinyframe_reader reader = TINYFRAME_READER_INITIALIZER;
+ uint8_t* decoding = content;
+ size_t left = content_length;
+
+ if (tinyframe_read(&reader, decoding, left) != tinyframe_have_control
+ || reader.control.type != TINYFRAME_CONTROL_START) {
+ return 1;
+ }
+ decoding += reader.bytes_read;
+ left -= reader.bytes_read;
+
+ if (tinyframe_read(&reader, decoding, left) != tinyframe_have_control_field
+ || reader.control_field.type != TINYFRAME_CONTROL_FIELD_CONTENT_TYPE
+ || strncmp(content_type, (const char*)reader.control_field.data, reader.control_field.length)) {
+ return 1;
+ }
+ decoding += reader.bytes_read;
+ left -= reader.bytes_read;
+
+ while (1) {
+ switch (tinyframe_read(&reader, decoding, left)) {
+ case tinyframe_have_frame: {
+ if (strncmp(content_type, (char*)reader.frame.data, reader.frame.length)) {
+ return 1;
+ }
+ break;
+ }
+
+ case tinyframe_stopped:
+ case tinyframe_finished:
+ return 0;
+
+ default:
+ return 1;
+ }
+
+ decoding += reader.bytes_read;
+ left -= reader.bytes_read;
+ }
+ return 0;
+ }
+ return 1;
+}
diff --git a/src/test/test2.sh b/src/test/test2.sh
new file mode 100755
index 0000000..977ea1e
--- /dev/null
+++ b/src/test/test2.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -xe
+
+./test2 w test2.fstrm
+./test2 r test2.fstrm
diff --git a/src/test/test3.c b/src/test/test3.c
new file mode 100644
index 0000000..bb2b135
--- /dev/null
+++ b/src/test/test3.c
@@ -0,0 +1,93 @@
+/*
+ * Author Jerry Lundström <jerry@dns-oarc.net>
+ * Copyright (c) 2020, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the tinyframe library.
+ *
+ * tinyframe library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * tinyframe library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with tinyframe library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <tinyframe/tinyframe.h>
+
+#include <assert.h>
+#include <string.h>
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#else
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#else
+#ifdef HAVE_MACHINE_ENDIAN_H
+#include <machine/endian.h>
+#endif
+#endif
+#endif
+
+static inline uint32_t _need32(const void* ptr)
+{
+ uint32_t v;
+ memcpy(&v, ptr, sizeof(v));
+ return be32toh(v);
+}
+
+int main(void)
+{
+ assert(tinyframe_frame_size(0) == 4);
+
+ struct tinyframe_writer writer = TINYFRAME_WRITER_INITIALIZER;
+ static struct tinyframe_control_field _content_type = {
+ TINYFRAME_CONTROL_FIELD_CONTENT_TYPE,
+ 4,
+ (uint8_t*)"test",
+ };
+ uint8_t out[64];
+
+ // correct control
+ assert(tinyframe_write_control(&writer, out, sizeof(out), TINYFRAME_CONTROL_READY, 0, 0) == tinyframe_ok);
+ assert(tinyframe_write_control(&writer, out, sizeof(out), TINYFRAME_CONTROL_READY, &_content_type, 1) == tinyframe_ok);
+
+ // wrong control field type
+ _content_type.type = 0;
+ assert(tinyframe_write_control(&writer, out, sizeof(out), TINYFRAME_CONTROL_READY, &_content_type, 1) == tinyframe_error);
+ _content_type.type = TINYFRAME_CONTROL_FIELD_CONTENT_TYPE;
+
+ // too large control field
+ _content_type.length = TINYFRAME_CONTROL_FIELD_CONTENT_TYPE_LENGTH_MAX + 1;
+ assert(tinyframe_write_control(&writer, out, sizeof(out), TINYFRAME_CONTROL_READY, &_content_type, 1) == tinyframe_error);
+ _content_type.length = 4;
+
+ // too small buffer
+ assert(tinyframe_write_control(&writer, out, 1, TINYFRAME_CONTROL_READY, &_content_type, 1) == tinyframe_need_more);
+
+ // too large control field
+ assert(tinyframe_write_control_start(&writer, out, sizeof(out), "test", TINYFRAME_CONTROL_FIELD_CONTENT_TYPE_LENGTH_MAX + 1) == tinyframe_error);
+
+ // too small buffer
+ assert(tinyframe_write_control_start(&writer, out, 1, "test", 4) == tinyframe_need_more);
+
+ // too small buffer
+ assert(tinyframe_write_frame(&writer, out, 1, out, 4) == tinyframe_need_more);
+
+ // too small buffer
+ assert(tinyframe_write_control_stop(&writer, out, 1) == tinyframe_need_more);
+
+ // correct
+ tinyframe_set_header(out, 111);
+ assert(_need32(out) == 111);
+
+ return 0;
+}
diff --git a/src/test/test3.sh b/src/test/test3.sh
new file mode 100755
index 0000000..26b0f6e
--- /dev/null
+++ b/src/test/test3.sh
@@ -0,0 +1,3 @@
+#!/bin/sh -xe
+
+./test3
diff --git a/src/tinyframe.c b/src/tinyframe.c
new file mode 100644
index 0000000..3615b59
--- /dev/null
+++ b/src/tinyframe.c
@@ -0,0 +1,403 @@
+/*
+ * Author Jerry Lundström <jerry@dns-oarc.net>
+ * Copyright (c) 2020, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the tinyframe library.
+ *
+ * tinyframe library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * tinyframe library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with tinyframe library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "tinyframe/tinyframe.h"
+
+#include <string.h>
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#else
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#else
+#ifdef HAVE_MACHINE_ENDIAN_H
+#include <machine/endian.h>
+#endif
+#endif
+#endif
+#include <assert.h>
+#ifdef TINYFRAME_TRACE
+#include <stdio.h>
+#include <ctype.h>
+static const char* printable_string(const uint8_t* data, size_t len)
+{
+ static char buf[512], hex;
+ size_t r = 0, w = 0;
+
+ while (r < len && w < sizeof(buf) - 1) {
+ if (isprint(data[r])) {
+ buf[w++] = data[r++];
+ } else {
+ if (w + 4 >= sizeof(buf) - 1) {
+ break;
+ }
+
+ buf[w++] = '\\';
+ buf[w++] = 'x';
+ hex = (data[r] & 0xf0) >> 4;
+ if (hex > 9) {
+ buf[w++] = 'a' + (hex - 10);
+ } else {
+ buf[w++] = '0' + hex;
+ }
+ hex = data[r++] & 0xf;
+ if (hex > 9) {
+ buf[w++] = 'a' + (hex - 10);
+ } else {
+ buf[w++] = '0' + hex;
+ }
+ }
+ }
+ if (w >= sizeof(buf)) {
+ buf[sizeof(buf) - 1] = 0;
+ } else {
+ buf[w] = 0;
+ }
+
+ return buf;
+}
+#define trace(x...) \
+ fprintf(stderr, "tinyframe %s(): ", __func__); \
+ fprintf(stderr, x); \
+ fprintf(stderr, "\n"); \
+ fflush(stderr);
+#else
+#define trace(x...)
+#define printable_string(x...)
+#endif
+
+const char* const tinyframe_state_string[] = {
+ "control",
+ "control_field",
+ "frame",
+ "done",
+};
+
+const char* const tinyframe_result_string[] = {
+ "ok",
+ "error",
+ "have_control",
+ "have_control_field",
+ "have_frame",
+ "stopped",
+ "finished",
+ "need_more",
+};
+
+static inline uint32_t _need32(const void* ptr)
+{
+ uint32_t v;
+ memcpy(&v, ptr, sizeof(v));
+ return be32toh(v);
+}
+
+static inline void _put32(void* ptr, uint32_t v)
+{
+ uint32_t be_v = htobe32(v);
+ memcpy(ptr, &be_v, sizeof(be_v));
+}
+
+static inline enum tinyframe_result __read_control(struct tinyframe_reader* handle, const uint8_t* data, size_t len)
+{
+ if (len < 12) {
+ trace("data len %zu < 12, need more", len);
+ return tinyframe_need_more;
+ }
+ handle->control.length = _need32(data); // "escape"
+ if (handle->control.length) {
+ trace("control length !zero, error, header: %s", printable_string(data, 12));
+ return tinyframe_error;
+ }
+ handle->control.length = _need32(data + 4); // length
+ if (handle->control.length > TINYFRAME_CONTROL_FRAME_LENGTH_MAX) {
+ trace("control length > max, error, header: %s", printable_string(data, 12));
+ return tinyframe_error;
+ }
+ handle->control.type = _need32(data + 8); // type
+
+ switch (handle->control.type) {
+ case TINYFRAME_CONTROL_ACCEPT:
+ case TINYFRAME_CONTROL_START:
+ case TINYFRAME_CONTROL_READY:
+ break;
+ case TINYFRAME_CONTROL_STOP:
+ handle->state = tinyframe_done;
+ handle->bytes_read = 12;
+ return tinyframe_stopped;
+ case TINYFRAME_CONTROL_FINISH:
+ handle->state = tinyframe_done;
+ handle->bytes_read = 12;
+ return tinyframe_finished;
+ default:
+ trace("control type %d invalid, error", handle->control.type);
+ return tinyframe_error;
+ }
+
+ // "escape" and length are not included in length, if not just type
+ // then we have control fields
+ if (handle->control.length > 4) {
+ handle->state = tinyframe_control_field;
+ handle->control_length = handle->control.length - 4; // - type length
+ handle->control_length_left = handle->control_length;
+ trace("control %d len %zu, next fields", handle->control.type, handle->control_length);
+ } else {
+ handle->state = tinyframe_frame;
+ trace("control %d len %zu, next frame", handle->control.type, handle->control_length);
+ }
+
+ handle->bytes_read = 12;
+ return tinyframe_have_control;
+}
+
+enum tinyframe_result tinyframe_read(struct tinyframe_reader* handle, const uint8_t* data, size_t len)
+{
+ assert(handle);
+ assert(data);
+
+ switch (handle->state) {
+ case tinyframe_control:
+ return __read_control(handle, data, len);
+
+ case tinyframe_control_field:
+ if (len < 8) {
+ trace("data len %zu < 8 for control field, need more", len);
+ return tinyframe_need_more;
+ }
+ handle->control_field.type = _need32(data);
+ switch (handle->control_field.type) {
+ case TINYFRAME_CONTROL_FIELD_CONTENT_TYPE:
+ break;
+ default:
+ trace("control field type %d invalid, error", handle->control_field.type);
+ return tinyframe_error;
+ }
+
+ handle->control_field.length = _need32(data + 4);
+ if (handle->control_field.length > TINYFRAME_CONTROL_FIELD_CONTENT_TYPE_LENGTH_MAX) {
+ trace("control field length > max, error");
+ return tinyframe_error;
+ }
+ if (len - 8 < handle->control_field.length) {
+ trace("data len %zu < control field length, need more", len - 8);
+ return tinyframe_need_more;
+ }
+
+ // did we over read control frame length?
+ if (handle->control_length_left < handle->control_field.length + 8) {
+ trace("control field goes beyond control, error");
+ return tinyframe_error;
+ }
+ handle->control_length_left -= handle->control_field.length + 8;
+ if (!handle->control_length_left) {
+ trace("no control fields left, next frame");
+ handle->state = tinyframe_frame;
+ }
+
+ handle->control_field.data = data + 8;
+ handle->bytes_read = 8 + handle->control_field.length;
+ trace("control field %d, data: %s", handle->control_field.type, printable_string(handle->control_field.data, handle->control_field.length));
+ return tinyframe_have_control_field;
+
+ case tinyframe_frame:
+ if (len < 4) {
+ trace("data len %zu < 4 for frame, need more", len);
+ return tinyframe_need_more;
+ }
+ handle->frame.length = _need32(data);
+ if (!handle->frame.length) {
+ handle->state = tinyframe_control;
+ return __read_control(handle, data, len);
+ }
+
+ if (len - 4 < handle->frame.length) {
+ trace("data len %zu < frame length, need more", len - 4);
+ return tinyframe_need_more;
+ }
+
+ handle->frame.data = data + 4;
+ handle->bytes_read = 4 + handle->frame.length;
+ trace("frame data [%zu]: %s...", handle->bytes_read, printable_string(data, handle->bytes_read > 20 ? 20 : handle->bytes_read));
+ return tinyframe_have_frame;
+
+ case tinyframe_done:
+ break;
+ }
+
+ return tinyframe_error;
+}
+
+enum tinyframe_result tinyframe_write_control(struct tinyframe_writer* handle, uint8_t* out, size_t len, uint32_t type, const struct tinyframe_control_field* fields, size_t num_fields)
+{
+ size_t out_len = 12;
+ size_t n;
+ uint8_t* outp;
+
+ assert(handle);
+ assert(out);
+ assert(!num_fields || (num_fields && fields));
+
+ for (n = 0; n < num_fields; n++) {
+ switch (fields[n].type) {
+ case TINYFRAME_CONTROL_FIELD_CONTENT_TYPE:
+ break;
+ default:
+ trace("field %zu type %d invalid, error", n, fields[n].type);
+ return tinyframe_error;
+ }
+ if (fields[n].length > TINYFRAME_CONTROL_FIELD_CONTENT_TYPE_LENGTH_MAX) {
+ trace("field %zu length > max, error", n);
+ return tinyframe_error;
+ }
+ out_len += 8 + fields[n].length;
+ }
+
+ if (len < out_len) {
+ trace("not enought space, need more");
+ return tinyframe_need_more;
+ }
+
+ _put32(out, 0); // "escape"
+ _put32(out + 4, out_len - 8); // length
+ // - 8 is because "escape" and length is not included in length
+ _put32(out + 8, type); // type
+
+ outp = out + 12;
+ for (n = 0; n < num_fields; n++) {
+ _put32(outp, fields[n].type);
+ _put32(outp + 4, fields[n].length);
+ if (fields[n].length) {
+ assert(fields[n].data);
+ memcpy(outp + 8, fields[n].data, fields[n].length);
+ }
+ outp += 8 + fields[n].length;
+ }
+
+ handle->bytes_wrote = out_len;
+ trace("control %u data: %s", type, printable_string(out, handle->bytes_wrote));
+ return tinyframe_ok;
+}
+
+enum tinyframe_result tinyframe_write_control_start(struct tinyframe_writer* handle, uint8_t* out, size_t len, const char* content_type, size_t content_type_len)
+{
+ assert(handle);
+ assert(out);
+ assert(content_type);
+
+ if (content_type_len > TINYFRAME_CONTROL_FIELD_CONTENT_TYPE_LENGTH_MAX) {
+ trace("field length > max, error");
+ return tinyframe_error;
+ }
+ if (len < 12 + 8 + content_type_len) {
+ trace("not enought space, need more");
+ return tinyframe_need_more;
+ }
+
+ _put32(out, 0); // "escape"
+ _put32(out + 4, 12 + 8 + content_type_len - 8); // length
+ // - 8 is because "escape" and length is not included in length
+ _put32(out + 8, TINYFRAME_CONTROL_START); // type
+ _put32(out + 12, TINYFRAME_CONTROL_FIELD_CONTENT_TYPE); // field type
+ _put32(out + 16, content_type_len); // field length
+ memcpy(out + 20, content_type, content_type_len); // field data
+
+ handle->bytes_wrote = 12 + 8 + content_type_len;
+ trace("control start data: %s", printable_string(out, handle->bytes_wrote));
+ return tinyframe_ok;
+}
+
+enum tinyframe_result tinyframe_write_frame(struct tinyframe_writer* handle, uint8_t* out, size_t len, const uint8_t* data, uint32_t data_len)
+{
+ assert(handle);
+ assert(out);
+ assert(data);
+
+ if (len < 4 + data_len) {
+ trace("not enought space, need more");
+ return tinyframe_need_more;
+ }
+
+ _put32(out, data_len); // length
+ memcpy(out + 4, data, data_len); // frame
+
+ handle->bytes_wrote = 4 + data_len;
+ trace("frame data: %s...", printable_string(out, handle->bytes_wrote > 20 ? 20 : handle->bytes_wrote));
+ return tinyframe_ok;
+}
+
+enum tinyframe_result tinyframe_write_control_stop(struct tinyframe_writer* handle, uint8_t* out, size_t len)
+{
+ assert(handle);
+ assert(out);
+
+ if (len < 12) {
+ trace("not enought space, need more");
+ return tinyframe_need_more;
+ }
+
+ _put32(out, 0); // "escape"
+ _put32(out + 4, 12 - 8); // length
+ // - 8 is because "escape" and length is not included in length
+ _put32(out + 8, TINYFRAME_CONTROL_STOP); // type
+
+ handle->bytes_wrote = 12;
+ trace("control stop data: %s", printable_string(out, handle->bytes_wrote));
+ return tinyframe_ok;
+}
+
+void tinyframe_set_header(uint8_t* frame, uint32_t frame_length)
+{
+ assert(frame);
+
+ _put32(frame, frame_length);
+}
+
+/*
+
+frame:
+- 32 length (if zero == control frame)
+- data
+
+control frame:
+- 32 bit "escape" (always zero)
+- 32 bit frame length
+- 32 bit control type
+
+control field: FSTRM_CONTROL_FIELD_CONTENT_TYPE:
+- 32 bit field type == FSTRM_CONTROL_FIELD_CONTENT_TYPE
+- 32 bit content type length
+- content type
+
+control start frame:
+- <control frame>
+- <control field: FSTRM_CONTROL_FIELD_CONTENT_TYPE>
+
+control accept & ready frame:
+- <control frame>
+- <control field: FSTRM_CONTROL_FIELD_CONTENT_TYPE>
+ ...
+ (additional control fields)
+
+control stop & finish:
+ nothing more
+
+*/
diff --git a/src/tinyframe/tinyframe.h b/src/tinyframe/tinyframe.h
new file mode 100644
index 0000000..e16806d
--- /dev/null
+++ b/src/tinyframe/tinyframe.h
@@ -0,0 +1,145 @@
+/*
+ * Author Jerry Lundström <jerry@dns-oarc.net>
+ * Copyright (c) 2020, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the tinyframe library.
+ *
+ * tinyframe library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * tinyframe library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with tinyframe library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <tinyframe/version.h>
+
+#include <unistd.h>
+#include <stdint.h>
+
+#ifndef __tinyframe_h_tinyframe
+#define __tinyframe_h_tinyframe 1
+
+#define TINYFRAME_CONTROL_FRAME_LENGTH_MAX 512
+#define TINYFRAME_CONTROL_FIELD_CONTENT_TYPE_LENGTH_MAX 256
+
+#define TINYFRAME_CONTROL_ACCEPT 0x01
+#define TINYFRAME_CONTROL_START 0x02
+#define TINYFRAME_CONTROL_STOP 0x03
+#define TINYFRAME_CONTROL_READY 0x04
+#define TINYFRAME_CONTROL_FINISH 0x05
+
+#define TINYFRAME_CONTROL_FIELD_CONTENT_TYPE 0x01
+
+struct tinyframe_control {
+ uint32_t length;
+ uint32_t type;
+};
+
+#define TINYFRAME_CONTROL_INITIALIZER \
+ { \
+ .length = 0, \
+ .type = 0, \
+ }
+
+struct tinyframe_control_field {
+ uint32_t type;
+ uint32_t length;
+ const uint8_t* data;
+};
+
+#define TINYFRAME_CONTROL_FIELD_INITIALIZER \
+ { \
+ .type = 0, \
+ .length = 0, \
+ .data = 0, \
+ }
+
+struct tinyframe {
+ uint32_t length;
+ const uint8_t* data;
+};
+
+#define TINYFRAME_INITIALIZER \
+ { \
+ .length = 0, \
+ .data = 0, \
+ }
+
+#define TINYFRAME_HEADER_SIZE sizeof(uint32_t)
+
+enum tinyframe_state {
+ tinyframe_control,
+ tinyframe_control_field,
+ tinyframe_frame,
+ tinyframe_done,
+};
+extern const char* const tinyframe_state_string[];
+
+struct tinyframe_reader {
+ enum tinyframe_state state;
+
+ size_t control_length, control_length_left;
+
+ struct tinyframe_control control;
+ struct tinyframe_control_field control_field;
+ struct tinyframe frame;
+
+ size_t bytes_read;
+};
+
+#define TINYFRAME_READER_INITIALIZER \
+ { \
+ .state = tinyframe_control, \
+ .control_length = 0, \
+ .control_length_left = 0, \
+ .control = TINYFRAME_CONTROL_INITIALIZER, \
+ .control_field = TINYFRAME_CONTROL_FIELD_INITIALIZER, \
+ .frame = TINYFRAME_INITIALIZER, \
+ .bytes_read = 0, \
+ }
+
+struct tinyframe_writer {
+ size_t bytes_wrote;
+};
+
+#define TINYFRAME_WRITER_INITIALIZER \
+ { \
+ .bytes_wrote = 0, \
+ }
+
+enum tinyframe_result {
+ tinyframe_ok = 0,
+ tinyframe_error = 1,
+ tinyframe_have_control = 2,
+ tinyframe_have_control_field = 3,
+ tinyframe_have_frame = 4,
+ tinyframe_stopped = 5,
+ tinyframe_finished = 6,
+ tinyframe_need_more = 7,
+};
+extern const char* const tinyframe_result_string[];
+
+enum tinyframe_result tinyframe_read(struct tinyframe_reader*, const uint8_t*, size_t);
+
+enum tinyframe_result tinyframe_write_control(struct tinyframe_writer*, uint8_t*, size_t, uint32_t, const struct tinyframe_control_field*, size_t);
+
+enum tinyframe_result tinyframe_write_control_start(struct tinyframe_writer*, uint8_t*, size_t, const char*, size_t);
+enum tinyframe_result tinyframe_write_frame(struct tinyframe_writer*, uint8_t*, size_t, const uint8_t*, uint32_t);
+enum tinyframe_result tinyframe_write_control_stop(struct tinyframe_writer*, uint8_t*, size_t);
+
+void tinyframe_set_header(uint8_t*, uint32_t);
+
+static inline size_t tinyframe_frame_size(size_t data_len)
+{
+ return 4 + data_len;
+}
+
+#endif
diff --git a/src/tinyframe/version.h.in b/src/tinyframe/version.h.in
new file mode 100644
index 0000000..c4357b8
--- /dev/null
+++ b/src/tinyframe/version.h.in
@@ -0,0 +1,31 @@
+/*
+ * Author Jerry Lundström <jerry@dns-oarc.net>
+ * Copyright (c) 2020, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the tinyframe library.
+ *
+ * tinyframe library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * tinyframe library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with tinyframe library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __tinyframe_h_version
+#define __tinyframe_h_version 1
+
+#define TINYFRAME_VERSION @TINYFRAME_VERSION_MAJOR@@TINYFRAME_VERSION_MINOR@@TINYFRAME_VERSION_PATCH@
+#define TINYFRAME_VERSION_MAJOR @TINYFRAME_VERSION_MAJOR@
+#define TINYFRAME_VERSION_MINOR @TINYFRAME_VERSION_MINOR@
+#define TINYFRAME_VERSION_PATCH @TINYFRAME_VERSION_PATCH@
+#define TINYFRAME_VERSION_STRING "@PACKAGE_VERSION@"
+
+#endif