diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2022-01-14 15:08:02 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2022-01-14 15:08:02 +0000 |
commit | 8d76eb12d5223d0fc3ea65d8d97f1feda2fcc340 (patch) | |
tree | 0fd0dd005a08843d538923c1a31d968a417dc4bf /src | |
parent | Initial commit. (diff) | |
download | tinyframe-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.am | 21 | ||||
-rw-r--r-- | src/test/Makefile.am | 29 | ||||
-rw-r--r-- | src/test/test.fstrm | bin | 0 -> 242 bytes | |||
-rw-r--r-- | src/test/test1.c | 125 | ||||
-rwxr-xr-x | src/test/test1.sh | 8 | ||||
-rw-r--r-- | src/test/test2.c | 141 | ||||
-rwxr-xr-x | src/test/test2.sh | 4 | ||||
-rw-r--r-- | src/test/test3.c | 93 | ||||
-rwxr-xr-x | src/test/test3.sh | 3 | ||||
-rw-r--r-- | src/tinyframe.c | 403 | ||||
-rw-r--r-- | src/tinyframe/tinyframe.h | 145 | ||||
-rw-r--r-- | src/tinyframe/version.h.in | 31 |
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 Binary files differnew file mode 100644 index 0000000..a3d164f --- /dev/null +++ b/src/test/test.fstrm 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 |