diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml index e8e56b5..9b847f0 100644 --- a/.github/workflows/presubmit.yml +++ b/.github/workflows/presubmit.yml @@ -8,7 +8,8 @@ on: - cron: '0 2 * * 0' # Weekly jobs: - sanitize: + sanitizers: + name: sanitizer (${{ matrix.sanitizer }}) runs-on: ubuntu-latest permissions: @@ -20,14 +21,14 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - env: - CC: /usr/bin/clang - CXX: /usr/bin/clang++ + strategy: + matrix: + sanitizer: [ "memory", "address", "safe-stack", "undefined" ] steps: - uses: actions/checkout@v3 - name: Build - run: COBS_SANITIZE=1 make -j + run: CC=clang CXX=clang++ COBS_SANITIZER=${{ matrix.sanitizer }} make -j arm-cm4: runs-on: ubuntu-latest @@ -46,7 +47,8 @@ jobs: - name: Build run: arm-none-eabi-gcc -mcpu=cortex-m4 -Os -Werror -Wall -Wextra -Wconversion -c cobs.c - linux-gcc: + linux: + name: linux (${{ matrix.compiler.name }}, ${{ matrix.architecture }}) runs-on: ubuntu-latest permissions: @@ -60,12 +62,15 @@ jobs: strategy: matrix: + compiler: + - { name: "clang", env: "CC=clang CXX=clang++" } + - { name: "gcc", env: "CC=gcc CXX=g++" } architecture: [32, 64] steps: - uses: actions/checkout@v3 - name: Build - run: COBS_LINUXARCH=${{ matrix.architecture }} make -j + run: ${{ matrix.compiler.env }} COBS_LINUXARCH=${{ matrix.architecture }} make -j macos: runs-on: macos-latest @@ -76,6 +81,7 @@ jobs: run: make -j win: + name: windows (msvc, ${{ matrix.architecture }}) runs-on: windows-latest strategy: @@ -89,3 +95,10 @@ jobs: run: | call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars${{ matrix.architecture }}.bat" call make-win.bat + + all-checks-pass: + needs: [sanitizers, linux, macos, win, arm-cm4] + runs-on: ubuntu-latest + steps: + - run: echo Done + diff --git a/.gitignore b/.gitignore index d739264..8dce5ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build +.vscode # Prerequisites *.d diff --git a/Makefile b/Makefile index 6fce519..1cbc016 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ SRCS := tests/cobs_encode_max_c.c \ tests/test_cobs_encode_inc.cc \ tests/test_cobs_encode_inplace.cc \ tests/test_cobs_decode.cc \ + tests/test_cobs_decode_inc.cc \ tests/test_cobs_decode_inplace.cc \ tests/test_paper_figures.cc \ tests/test_wikipedia.cc \ @@ -16,7 +17,7 @@ OS := $(shell uname) COMPILER_VERSION := $(shell $(CXX) --version) CFLAGS = --std=c99 -CXXFLAGS = --std=c++17 +CXXFLAGS = --std=c++20 CPPFLAGS += -MMD -MP -Os -g @@ -27,11 +28,6 @@ LDFLAGS += -m32 endif endif -ifeq ($(COBS_SANITIZE),1) -CPPFLAGS_SAN += -fsanitize=undefined,address -LDFLAGS_SAN += -fsanitize=undefined,address -endif - CPPFLAGS += -Wall -Werror -Wextra ifneq '' '$(findstring clang,$(COMPILER_VERSION))' @@ -40,7 +36,8 @@ CPPFLAGS += -Weverything \ -Wno-unsafe-buffer-usage \ -Wno-poison-system-directories \ -Wno-format-pedantic \ - -Wno-c++98-compat-bind-to-temporary-copy + -Wno-c++98-compat-bind-to-temporary-copy \ + -Wno-pre-c++20-compat-pedantic CFLAGS += -Wno-declaration-after-statement else CPPFLAGS += -Wconversion @@ -48,17 +45,22 @@ endif CPPFLAGS += -Wno-c++98-compat -Wno-padded +ifdef COBS_SANITIZER +CPPFLAGS += -fsanitize=$(COBS_SANITIZER) -fsanitize-ignorelist=sanitize-ignorelist.txt +LDFLAGS += -fsanitize=$(COBS_SANITIZER) -fsanitize-ignorelist=sanitize-ignorelist.txt +endif + $(BUILD_DIR)/cobs_unittests: $(OBJS) $(BUILD_DIR)/cobs.c.o Makefile - $(CXX) $(LDFLAGS) $(LDFLAGS_SAN) $(OBJS) $(BUILD_DIR)/cobs.c.o -o $@ + $(CXX) $(LDFLAGS) $(OBJS) $(BUILD_DIR)/cobs.c.o -o $@ $(BUILD_DIR)/cobs.c.o: cobs.c cobs.h Makefile - mkdir -p $(dir $@) && $(CC) $(CPPFLAGS) $(CFLAGS) $(CPPFLAGS_SAN) -c $< -o $@ + mkdir -p $(dir $@) && $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.c.o: %.c Makefile - mkdir -p $(dir $@) && $(CC) $(CPPFLAGS) $(CFLAGS) $(CPPFLAGS_SAN) -c $< -o $@ + mkdir -p $(dir $@) && $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/%.cc.o: %.cc Makefile - mkdir -p $(dir $@) && $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPPFLAGS_SAN) -c $< -o $@ + mkdir -p $(dir $@) && $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ $(BUILD_DIR)/cobs_unittests.timestamp: $(BUILD_DIR)/cobs_unittests $(BUILD_DIR)/cobs_unittests -m && touch $(BUILD_DIR)/cobs_unittests.timestamp diff --git a/cobs.c b/cobs.c index cb1bef6..55d0588 100644 --- a/cobs.c +++ b/cobs.c @@ -42,7 +42,7 @@ cobs_ret_t cobs_decode_inplace(void *buf, size_t const len) { cobs_byte_t *const src = (cobs_byte_t *)buf; size_t ofs, cur = 0; - while (cur < len && ((ofs = src[cur]) != COBS_FRAME_DELIMITER)) { + while ((cur < len) && ((ofs = src[cur]) != COBS_FRAME_DELIMITER)) { src[cur] = 0; for (size_t i = 1; i < ofs; ++i) { if (src[cur + i] == 0) { @@ -183,42 +183,102 @@ cobs_ret_t cobs_decode(void const *enc, return COBS_RET_ERR_BAD_ARG; } - cobs_byte_t const *const src = (cobs_byte_t const *)enc; - cobs_byte_t *const dst = (cobs_byte_t *)out_dec; + cobs_decode_inc_ctx_t ctx; + cobs_ret_t r = cobs_decode_inc_begin(&ctx); + if (r != COBS_RET_SUCCESS) { + return r; + } - if ((src[0] == COBS_FRAME_DELIMITER) || (src[enc_len - 1] != COBS_FRAME_DELIMITER)) { - return COBS_RET_ERR_BAD_PAYLOAD; + size_t src_len; + bool decode_complete; + if ((r = cobs_decode_inc(&ctx, + &(cobs_decode_inc_args_t){ .enc_src = enc, + .dec_dst = out_dec, + .enc_src_max = enc_len, + .dec_dst_max = dec_max }, + &src_len, + out_dec_len, + &decode_complete)) != COBS_RET_SUCCESS) { + return r; } + return decode_complete ? COBS_RET_SUCCESS : COBS_RET_ERR_EXHAUSTED; +} +cobs_ret_t cobs_decode_inc_begin(cobs_decode_inc_ctx_t *ctx) { + if (!ctx) { + return COBS_RET_ERR_BAD_ARG; + } + ctx->state = COBS_DECODE_READ_CODE; + return COBS_RET_SUCCESS; +} + +cobs_ret_t cobs_decode_inc(cobs_decode_inc_ctx_t *ctx, + cobs_decode_inc_args_t const *args, + size_t *out_enc_src_len, + size_t *out_dec_dst_len, + bool *out_decode_complete) { + if (!ctx || !args || !out_enc_src_len || !out_dec_dst_len || !out_decode_complete || + !args->dec_dst || !args->enc_src) { + return COBS_RET_ERR_BAD_ARG; + } + + bool decode_complete = false; size_t src_idx = 0, dst_idx = 0; - while (src_idx < (enc_len - 1)) { - unsigned const code = src[src_idx++]; - if (!code) { - return COBS_RET_ERR_BAD_PAYLOAD; - } - if ((src_idx + code) > enc_len) { - return COBS_RET_ERR_BAD_PAYLOAD; - } + size_t const src_max = args->enc_src_max; + size_t const dst_max = args->dec_dst_max; + cobs_byte_t const *src_b = (cobs_byte_t const *)args->enc_src; + cobs_byte_t *dst_b = (cobs_byte_t *)args->dec_dst; + unsigned block = ctx->block, code = ctx->code; + enum cobs_decode_inc_state state = ctx->state; - if ((dst_idx + code - 1) > dec_max) { - return COBS_RET_ERR_EXHAUSTED; - } - for (size_t i = 0; i < code - 1; ++i) { - if (src[src_idx] == 0) { - return COBS_RET_ERR_BAD_PAYLOAD; - } - dst[dst_idx++] = src[src_idx++]; - } + while (src_idx < src_max) { + switch (state) { + case COBS_DECODE_READ_CODE: { + block = code = src_b[src_idx++]; + state = COBS_DECODE_RUN; + } break; - if ((src_idx < (enc_len - 1)) && (code < 0xFF)) { - if (dst_idx >= dec_max) { - return COBS_RET_ERR_EXHAUSTED; - } - dst[dst_idx++] = 0; + case COBS_DECODE_FINISH_RUN: { + if (!src_b[src_idx]) { + decode_complete = true; + goto done; + } + + if (code != 0xFF) { + if (dst_idx >= dst_max) { + goto done; + } + dst_b[dst_idx++] = 0; + } + state = COBS_DECODE_READ_CODE; + } break; + + case COBS_DECODE_RUN: { + while (block - 1) { + if ((src_idx >= src_max) || (dst_idx >= dst_max)) { + goto done; + } + + --block; + cobs_byte_t const b = src_b[src_idx++]; + if (!b) { + return COBS_RET_ERR_BAD_PAYLOAD; + } + + dst_b[dst_idx++] = b; + } + state = COBS_DECODE_FINISH_RUN; + } break; } } - *out_dec_len = dst_idx; +done: + ctx->state = state; + ctx->code = (uint8_t)code; + ctx->block = (uint8_t)block; + *out_dec_dst_len = dst_idx; + *out_enc_src_len = src_idx; + *out_decode_complete = decode_complete; return COBS_RET_SUCCESS; } diff --git a/cobs.h b/cobs.h index 383e345..af679fa 100644 --- a/cobs.h +++ b/cobs.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -171,6 +172,31 @@ cobs_ret_t cobs_encode_inc(cobs_enc_ctx_t *ctx, void const *dec_src, size_t dec_ // If null pointers are provided, the function returns COBS_RET_ERR_BAD_ARG. cobs_ret_t cobs_encode_inc_end(cobs_enc_ctx_t *ctx, size_t *out_enc_len); +// Incremental decoding API + +typedef struct cobs_decode_inc_ctx { + enum cobs_decode_inc_state { + COBS_DECODE_READ_CODE, + COBS_DECODE_RUN, + COBS_DECODE_FINISH_RUN + } state; + uint8_t block, code; +} cobs_decode_inc_ctx_t; + +typedef struct cobs_decode_inc_args { + void const *enc_src; // pointer to current position of encoded payload + void *dec_dst; // pointer to decoded output buffer. + size_t enc_src_max; // length of the |src| input buffer. + size_t dec_dst_max; // length of the |dst| output buffer. +} cobs_decode_inc_args_t; + +cobs_ret_t cobs_decode_inc_begin(cobs_decode_inc_ctx_t *ctx); +cobs_ret_t cobs_decode_inc(cobs_decode_inc_ctx_t *ctx, + cobs_decode_inc_args_t const *args, + size_t *out_enc_src_len, // how many bytes of src were read + size_t *out_dec_dst_len, // how many bytes written to dst + bool *out_decode_complete); + #ifdef __cplusplus } #endif diff --git a/make-win.bat b/make-win.bat index d0281f6..32befe5 100644 --- a/make-win.bat +++ b/make-win.bat @@ -1,7 +1,8 @@ -cl.exe /W4 /WX /MP /EHsc ^ +cl.exe /W4 /WX /MP /EHsc /std:c++20 ^ cobs.c ^ tests/cobs_encode_max_c.c ^ tests/test_cobs_decode.cc ^ + tests/test_cobs_decode_inc.cc ^ tests/test_cobs_decode_inplace.cc ^ tests/test_cobs_encode_max.cc ^ tests/test_cobs_encode.cc ^ diff --git a/sanitize-ignorelist.txt b/sanitize-ignorelist.txt new file mode 100644 index 0000000..3d599fb --- /dev/null +++ b/sanitize-ignorelist.txt @@ -0,0 +1,5 @@ +[memory] +src:*/stl_tree.h +src:*/ostream +src:*/iomanip +src:*/basic_string.h diff --git a/tests/test_cobs_decode_inc.cc b/tests/test_cobs_decode_inc.cc new file mode 100644 index 0000000..dc8db1b --- /dev/null +++ b/tests/test_cobs_decode_inc.cc @@ -0,0 +1,120 @@ +#include "../cobs.h" +#include "byte_vec.h" +#include "doctest.h" + +#include +#include +#include + +TEST_CASE("cobs_decode_inc_begin") { + REQUIRE(cobs_decode_inc_begin(nullptr) == COBS_RET_ERR_BAD_ARG); + + cobs_decode_inc_ctx_t c; + c.state = cobs_decode_inc_ctx_t::cobs_decode_inc_state( + cobs_decode_inc_ctx_t::COBS_DECODE_READ_CODE + 1); + REQUIRE(cobs_decode_inc_begin(&c) == COBS_RET_SUCCESS); + REQUIRE(c.state == cobs_decode_inc_ctx_t::COBS_DECODE_READ_CODE); +} + +TEST_CASE("cobs_decode_inc") { + cobs_decode_inc_ctx_t ctx; + REQUIRE(cobs_decode_inc_begin(&ctx) == COBS_RET_SUCCESS); + + byte_vec_t enc(1024), dec(enc.size() * 2); + cobs_decode_inc_args_t args{ .enc_src = enc.data(), + .dec_dst = dec.data(), + .enc_src_max = enc.size(), + .dec_dst_max = dec.size() }; + size_t enc_len{ 0u }, dec_len{ 0u }; + bool done{ false }; + + SUBCASE("bad args") { + REQUIRE(cobs_decode_inc(nullptr, &args, &enc_len, &dec_len, &done) == + COBS_RET_ERR_BAD_ARG); + REQUIRE(cobs_decode_inc(&ctx, nullptr, &enc_len, &dec_len, &done) == + COBS_RET_ERR_BAD_ARG); + REQUIRE(cobs_decode_inc(&ctx, &args, nullptr, &dec_len, &done) == + COBS_RET_ERR_BAD_ARG); + REQUIRE(cobs_decode_inc(&ctx, &args, &enc_len, nullptr, &done) == + COBS_RET_ERR_BAD_ARG); + REQUIRE(cobs_decode_inc(&ctx, &args, &enc_len, &dec_len, nullptr) == + COBS_RET_ERR_BAD_ARG); + + SUBCASE("args.src") { + args.enc_src = nullptr; + REQUIRE(cobs_decode_inc(&ctx, &args, &enc_len, &dec_len, &done) == + COBS_RET_ERR_BAD_ARG); + } + SUBCASE("args.dst") { + args.dec_dst = nullptr; + REQUIRE(cobs_decode_inc(&ctx, &args, &enc_len, &dec_len, &done) == + COBS_RET_ERR_BAD_ARG); + } + } + + // fill the buffer with patterns and 0x00 runs + dec_len = 900; + memset(dec.data(), 0xAA, dec_len); + memset(&dec[10], 0x00, 3); + memset(&dec[99], 0x00, 5); + memset(&dec[413], 0x00, 9); + std::iota(&dec[500], &dec[500 + 300], byte_t{ 0u }); + + // encode the test buffer into |enc| + REQUIRE(cobs_encode(dec.data(), dec_len, enc.data(), enc.size(), &enc_len) == + COBS_RET_SUCCESS); + std::fill(dec.begin(), dec.end(), byte_t{ 0u }); + REQUIRE(enc_len >= dec_len); + REQUIRE(enc_len <= COBS_ENCODE_MAX(dec_len)); + + // Do a full decode into a reference buffer for comparison later + byte_vec_t oneshot(enc.size()); + { + size_t oneshot_len{ 0u }; + REQUIRE( + cobs_decode(enc.data(), enc_len, oneshot.data(), oneshot.size(), &oneshot_len) == + COBS_RET_SUCCESS); + oneshot.resize(oneshot_len); + } + + size_t cur_dec{ 0 }, cur_enc{ 0 }; + + auto const decode_inc{ [&](size_t const enc_size, size_t const dec_size) { + while (!done) { + args.enc_src = &enc[cur_enc]; + args.dec_dst = &dec[cur_dec]; + args.enc_src_max = std::min(enc_size, enc_len - cur_enc); + args.dec_dst_max = std::min(dec_size, oneshot.size() - cur_dec); + + size_t this_enc_len{ 0u }, this_dec_len{ 0u }; + REQUIRE(cobs_decode_inc(&ctx, &args, &this_enc_len, &this_dec_len, &done) == + COBS_RET_SUCCESS); + cur_dec += this_dec_len; + cur_enc += this_enc_len; + } + + REQUIRE(cur_dec == oneshot.size()); + dec.resize(cur_dec); + REQUIRE(dec == oneshot); + } }; + + SUBCASE("1 byte enc, full dec") { + decode_inc(1, oneshot.size()); + } + + SUBCASE("full enc, 1 byte dec") { + decode_inc(enc_len, 1); + } + + SUBCASE("1 byte enc, 1 byte dec") { + decode_inc(1, 1); + } + + SUBCASE("relative primes") { + decode_inc(19, 29); + } + + SUBCASE("full") { + decode_inc(enc_len, oneshot.size()); + } +} diff --git a/tests/test_cobs_decode_inplace.cc b/tests/test_cobs_decode_inplace.cc index 61c457b..b690dbc 100644 --- a/tests/test_cobs_decode_inplace.cc +++ b/tests/test_cobs_decode_inplace.cc @@ -114,12 +114,13 @@ TEST_CASE("Inplace decoding") { namespace { void verify_decode_inplace(unsigned char *inplace, size_t payload_len) { byte_vec_t external(std::max(payload_len, size_t(1))); - size_t external_len; - REQUIRE(cobs_decode(inplace, - payload_len + 2, - external.data(), - external.size(), - &external_len) == COBS_RET_SUCCESS); + size_t external_len{ 0u }; + REQUIRE_MESSAGE(cobs_decode(inplace, + payload_len + 2, + external.data(), + external.size(), + &external_len) == COBS_RET_SUCCESS, + payload_len); REQUIRE(external_len == payload_len); REQUIRE(cobs_decode_inplace(inplace, payload_len + 2) == COBS_RET_SUCCESS); @@ -127,59 +128,60 @@ void verify_decode_inplace(unsigned char *inplace, size_t payload_len) { byte_vec_t(external.data(), external.data() + external_len)); } -void fill_encode_inplace(byte_t *inplace, unsigned payload_len, byte_t f) { +void fill_encode_inplace(byte_t *inplace, size_t payload_len, byte_t f) { inplace[0] = COBS_INPLACE_SENTINEL_VALUE; memset(inplace + 1, f, payload_len); inplace[payload_len + 1] = COBS_INPLACE_SENTINEL_VALUE; - REQUIRE(cobs_encode_inplace(inplace, payload_len + 2) == COBS_RET_SUCCESS); + REQUIRE_MESSAGE(cobs_encode_inplace(inplace, payload_len + 2) == COBS_RET_SUCCESS, + payload_len); } } // namespace TEST_CASE("Decode: Inplace == External") { - unsigned char inplace[COBS_INPLACE_SAFE_BUFFER_SIZE]; + std::array inplace; SUBCASE("Fill with zeros") { - for (auto i = 0u; i < sizeof(inplace) - 2; ++i) { - fill_encode_inplace(inplace, i, 0x00); - verify_decode_inplace(inplace, i); + for (auto i{ 0u }; i < inplace.size() - 2; ++i) { + fill_encode_inplace(inplace.data(), i, 0x00); + verify_decode_inplace(inplace.data(), i); } } SUBCASE("Fill with nonzeros") { - for (auto i = 0u; i < sizeof(inplace) - 2; ++i) { - fill_encode_inplace(inplace, i, 0x01); - verify_decode_inplace(inplace, i); + for (auto i{ 0u }; i < inplace.size() - 2; ++i) { + fill_encode_inplace(inplace.data(), i, 0x01); + verify_decode_inplace(inplace.data(), i); } } SUBCASE("Fill with 0xFF") { - for (auto i = 0u; i < sizeof(inplace) - 2; ++i) { - fill_encode_inplace(inplace, i, 0xFF); - verify_decode_inplace(inplace, i); + for (auto i{ 0u }; i < inplace.size() - 2; ++i) { + fill_encode_inplace(inplace.data(), i, 0xFF); + verify_decode_inplace(inplace.data(), i); } } SUBCASE("Fill with zero/one pattern") { - for (auto i = 0u; i < sizeof(inplace) - 2; ++i) { + for (auto i{ 0u }; i < inplace.size() - 2; ++i) { inplace[0] = COBS_INPLACE_SENTINEL_VALUE; - for (auto j = 1u; j < i; ++j) { + for (auto j{ 1u }; j < i; ++j) { inplace[j] = j & 1; } inplace[i + 1] = COBS_INPLACE_SENTINEL_VALUE; - REQUIRE(cobs_encode_inplace(inplace, i + 2) == COBS_RET_SUCCESS); - verify_decode_inplace(inplace, i); + REQUIRE(cobs_encode_inplace(inplace.data(), i + 2) == COBS_RET_SUCCESS); + verify_decode_inplace(inplace.data(), i); } } SUBCASE("Fill with one/zero pattern") { - for (auto i = 0u; i < sizeof(inplace) - 2; ++i) { + for (auto i{ 0u }; i < inplace.size() - 2; ++i) { inplace[0] = COBS_INPLACE_SENTINEL_VALUE; for (auto j = 1u; j < i; ++j) { inplace[j] = (j & 1) ^ 1; } inplace[i + 1] = COBS_INPLACE_SENTINEL_VALUE; - REQUIRE(cobs_encode_inplace(inplace, i + 2) == COBS_RET_SUCCESS); - verify_decode_inplace(inplace, i); + REQUIRE(cobs_encode_inplace(inplace.data(), i + 2) == COBS_RET_SUCCESS); + verify_decode_inplace(inplace.data(), i); } } } diff --git a/tests/test_cobs_encode.cc b/tests/test_cobs_encode.cc index eefa20b..a9e47d2 100644 --- a/tests/test_cobs_encode.cc +++ b/tests/test_cobs_encode.cc @@ -26,7 +26,7 @@ TEST_CASE("Encoding validation") { TEST_CASE("Simple encodings") { byte_t dec[16], enc[16]; - size_t enc_len; + size_t enc_len{ 0u }; SUBCASE("Empty") { REQUIRE(cobs_encode(&dec, 0, enc, sizeof(enc), &enc_len) == COBS_RET_SUCCESS); @@ -94,7 +94,7 @@ TEST_CASE("Simple encodings") { namespace { byte_vec_t encode(byte_vec_t const &decoded) { byte_vec_t enc(COBS_ENCODE_MAX(static_cast(decoded.size()))); - size_t enc_len; + size_t enc_len{ 0u }; REQUIRE(cobs_encode(decoded.data(), decoded.size(), enc.data(), enc.size(), &enc_len) == COBS_RET_SUCCESS); return byte_vec_t(enc.data(), enc.data() + enc_len); diff --git a/tests/test_cobs_encode_inc.cc b/tests/test_cobs_encode_inc.cc index f739bbe..f23e4fc 100644 --- a/tests/test_cobs_encode_inc.cc +++ b/tests/test_cobs_encode_inc.cc @@ -34,7 +34,12 @@ TEST_CASE("cobs_encode_inc_begin") { } TEST_CASE("cobs_encode_inc") { - cobs_enc_ctx_t ctx; + cobs_enc_ctx_t ctx{ .dst = nullptr, + .dst_max = 0, + .cur = 0, + .code_idx = 0, + .code = 0, + .need_advance = 0 }; size_t const enc_max{ 1024 }; byte_vec_t enc_buf(enc_max); size_t const dec_max{ 1024 }; @@ -166,7 +171,7 @@ TEST_CASE("cobs_encode_inc_end") { namespace { byte_vec_t encode_single(byte_vec_t const &dec) { byte_vec_t enc(COBS_ENCODE_MAX(dec.size())); - size_t enc_len; + size_t enc_len{ 0u }; REQUIRE(cobs_encode(dec.data(), dec.size(), enc.data(), enc.size(), &enc_len) == COBS_RET_SUCCESS); enc.resize(enc_len); @@ -186,7 +191,7 @@ byte_vec_t encode_incremental(byte_vec_t const &decoded, size_t chunk_size) { cur += encode_size; } - size_t len; + size_t len{ 0u }; REQUIRE(cobs_encode_inc_end(&ctx, &len) == COBS_RET_SUCCESS); encoded.resize(len); return encoded; @@ -295,7 +300,7 @@ TEST_CASE("Single/multi-encode equivalences") { REQUIRE(cobs_encode_inc(&ctx, h, sizeof(h)) == COBS_RET_SUCCESS); REQUIRE(cobs_encode_inc(&ctx, dec_buf.data(), dec_buf.size()) == COBS_RET_SUCCESS); - size_t len; + size_t len{ 0u }; REQUIRE(cobs_encode_inc_end(&ctx, &len) == COBS_RET_SUCCESS); enc_buf.resize(len); diff --git a/tests/test_paper_figures.cc b/tests/test_paper_figures.cc index 111d86f..813b633 100644 --- a/tests/test_paper_figures.cc +++ b/tests/test_paper_figures.cc @@ -9,7 +9,7 @@ TEST_CASE("COBS paper examples") { input[input.size() - 1] = 0x00; byte_vec_t output(684); - size_t output_len; + size_t output_len{ 0u }; REQUIRE(cobs_encode(input.data(), input.size(), @@ -34,7 +34,7 @@ TEST_CASE("COBS paper examples") { 0x01, 0x05, 0x40, 0x06, 0x4F, 0x37, 0x00 }; std::array encoded; - size_t encoded_len; + size_t encoded_len{ 0u }; REQUIRE(cobs_encode(input.data(), input.size(), diff --git a/tests/test_wikipedia.cc b/tests/test_wikipedia.cc index 2eeb024..2a0a38a 100644 --- a/tests/test_wikipedia.cc +++ b/tests/test_wikipedia.cc @@ -4,15 +4,12 @@ #include -static constexpr byte_t CSV{ COBS_INPLACE_SENTINEL_VALUE }; - namespace { + void round_trip_inplace(byte_vec_t const &decoded, byte_vec_t const &encoded) { - byte_vec_t decoded_inplace{ CSV }; - decoded_inplace.insert(std::end(decoded_inplace), - std::begin(decoded), - std::end(decoded)); - decoded_inplace.push_back(CSV); + byte_vec_t decoded_inplace{ COBS_INPLACE_SENTINEL_VALUE }; + decoded_inplace.insert(std::end(decoded_inplace), decoded.begin(), decoded.end()); + decoded_inplace.push_back(COBS_INPLACE_SENTINEL_VALUE); byte_vec_t x(decoded_inplace); @@ -24,7 +21,7 @@ void round_trip_inplace(byte_vec_t const &decoded, byte_vec_t const &encoded) { void round_trip(byte_vec_t const &decoded, byte_vec_t const &encoded) { std::array enc_actual, dec_actual; - size_t enc_actual_len, dec_actual_len; + size_t enc_actual_len{ 0u }, dec_actual_len{ 0u }; REQUIRE(cobs_encode(decoded.data(), decoded.size(), @@ -32,6 +29,7 @@ void round_trip(byte_vec_t const &decoded, byte_vec_t const &encoded) { enc_actual.size(), &enc_actual_len) == COBS_RET_SUCCESS); + REQUIRE(enc_actual_len == encoded.size()); REQUIRE(encoded == byte_vec_t(enc_actual.data(), enc_actual.data() + enc_actual_len)); REQUIRE(cobs_decode(enc_actual.data(), @@ -40,6 +38,7 @@ void round_trip(byte_vec_t const &decoded, byte_vec_t const &encoded) { dec_actual.size(), &dec_actual_len) == COBS_RET_SUCCESS); + REQUIRE(dec_actual_len == decoded.size()); REQUIRE(decoded == byte_vec_t(dec_actual.data(), dec_actual.data() + dec_actual_len)); // Additionaly, in-place decode atop enc_actual using cobs_decode. @@ -50,12 +49,12 @@ void round_trip(byte_vec_t const &decoded, byte_vec_t const &encoded) { enc_actual.size(), &dec_actual_len) == COBS_RET_SUCCESS); + REQUIRE(dec_actual_len == decoded.size()); REQUIRE(decoded == byte_vec_t(enc_actual.data(), enc_actual.data() + dec_actual_len)); } } // namespace // https://wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing#Encoding_examples - TEST_CASE("Wikipedia round-trip examples") { SUBCASE("Example 1") { const byte_vec_t decoded{ 0x00 }; @@ -72,34 +71,41 @@ TEST_CASE("Wikipedia round-trip examples") { } SUBCASE("Example 3") { + const byte_vec_t decoded{ 0x00, 0x11, 0x00 }; + const byte_vec_t encoded{ 0x01, 0x02, 0x11, 0x01, 0x00 }; + round_trip_inplace(decoded, encoded); + round_trip(decoded, encoded); + } + + SUBCASE("Example 4") { const byte_vec_t decoded{ 0x11, 0x22, 0x00, 0x33 }; const byte_vec_t encoded{ 0x03, 0x11, 0x22, 0x02, 0x33, 0x00 }; round_trip_inplace(decoded, encoded); round_trip(decoded, encoded); } - SUBCASE("Example 4") { + SUBCASE("Example 5") { const byte_vec_t decoded{ 0x11, 0x22, 0x33, 0x44 }; const byte_vec_t encoded{ 0x05, 0x11, 0x22, 0x33, 0x44, 0x00 }; round_trip_inplace(decoded, encoded); round_trip(decoded, encoded); } - SUBCASE("Example 5") { + SUBCASE("Example 6") { const byte_vec_t decoded{ 0x11, 0x00, 0x00, 0x00 }; const byte_vec_t encoded{ 0x02, 0x11, 0x01, 0x01, 0x01, 0x00 }; round_trip_inplace(decoded, encoded); round_trip(decoded, encoded); } - SUBCASE("Example 6") { + SUBCASE("Example 7") { // 01 02 03 ... FD FE byte_vec_t decoded(254); - std::iota(std::begin(decoded), std::end(decoded), byte_t{ 0x01 }); + std::iota(decoded.begin(), decoded.end(), byte_t{ 0x01 }); // FF 01 02 03 ... FD FE 00 byte_vec_t encoded(255); - std::iota(std::begin(encoded), std::end(encoded), byte_t{ 0x00 }); + std::iota(encoded.begin(), encoded.end(), byte_t{ 0x00 }); encoded[0] = 0xFF; encoded.push_back(0x00); @@ -107,10 +113,10 @@ TEST_CASE("Wikipedia round-trip examples") { round_trip(decoded, encoded); } - SUBCASE("Example 7") { + SUBCASE("Example 8") { // 00 01 02 ... FC FD FE byte_vec_t decoded(255); - std::iota(std::begin(decoded), std::end(decoded), byte_t{ 0x00 }); + std::iota(decoded.begin(), decoded.end(), byte_t{ 0x00 }); // 01 FF 01 02 ... FC FD FE 00 byte_vec_t encoded{ 0x01, 0xFF }; @@ -123,39 +129,39 @@ TEST_CASE("Wikipedia round-trip examples") { round_trip(decoded, encoded); } - SUBCASE("Example 8") { + SUBCASE("Example 9") { // 01 02 03 ... FD FE FF byte_vec_t decoded(255); - std::iota(std::begin(decoded), std::end(decoded), byte_t{ 0x01 }); + std::iota(decoded.begin(), decoded.end(), byte_t{ 0x01 }); // FF 01 02 03 ... FD FE 02 FF 00 byte_vec_t encoded(255); - std::iota(std::begin(encoded), std::end(encoded), byte_t{ 0x00 }); + std::iota(encoded.begin(), encoded.end(), byte_t{ 0x00 }); encoded[0] = 0xFF; - encoded.insert(std::end(encoded), { 0x02, 0xFF, 0x00 }); + encoded.insert(encoded.end(), { 0x02, 0xFF, 0x00 }); round_trip(decoded, encoded); } - SUBCASE("Example 9") { + SUBCASE("Example 10") { // 02 03 04 ... FE FF 00 byte_vec_t decoded(255); - std::iota(std::begin(decoded), std::end(decoded), byte_t{ 0x02 }); + std::iota(decoded.begin(), decoded.end(), byte_t{ 0x02 }); decoded[decoded.size() - 1] = 0x00; // FF 02 03 04 ... FE FF 01 01 00 byte_vec_t encoded(255); - std::iota(std::begin(encoded), std::end(encoded), byte_t{ 0x01 }); + std::iota(encoded.begin(), encoded.end(), byte_t{ 0x01 }); encoded[0] = 0xFF; encoded.insert(std::end(encoded), { 0x01, 0x01, 0x00 }); round_trip(decoded, encoded); } - SUBCASE("Example 10") { + SUBCASE("Example 11") { // 03 04 05 ... FF 00 01 byte_vec_t decoded(253); - std::iota(std::begin(decoded), std::end(decoded), byte_t{ 0x03 }); + std::iota(decoded.begin(), decoded.end(), byte_t{ 0x03 }); decoded.insert(decoded.end(), { 0x00, 0x01 }); // FE 03 04 05 ... FF 02 01 00