From 7e9c1bfda2cd2ab5f7375a49e213dad34f6109a1 Mon Sep 17 00:00:00 2001 From: omicron Date: Wed, 16 Apr 2025 21:34:43 +0200 Subject: [PATCH] Add bytes type and tests bytes_t is a local (automatic) allocation array that carries the length and capacity with it. --- src/bytes.c | 6 ++ src/bytes.h | 60 ++++++++++++++++++ tests/bytes.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/main.c | 2 + 4 files changed, 232 insertions(+) create mode 100644 src/bytes.c create mode 100644 src/bytes.h create mode 100644 tests/bytes.c diff --git a/src/bytes.c b/src/bytes.c new file mode 100644 index 0000000..471f3d6 --- /dev/null +++ b/src/bytes.c @@ -0,0 +1,6 @@ +#include "bytes.h" +#include "error.h" + +error_t *const err_bytes_no_capacity = &(error_t){ + .message = "Not enough capacity in bytes buffer", +}; diff --git a/src/bytes.h b/src/bytes.h new file mode 100644 index 0000000..5041320 --- /dev/null +++ b/src/bytes.h @@ -0,0 +1,60 @@ +#ifndef INCLUDE_SRC_BYTES_H_ +#define INCLUDE_SRC_BYTES_H_ + +#include "error.h" +#include +#include +#include + +extern error_t *const err_bytes_no_capacity; + +typedef struct bytes { + size_t len; + size_t cap; + uint8_t buffer[]; +} bytes_t; + +#define LOCAL_BYTES_ANONYMOUS(N) \ + &(struct { \ + size_t len; \ + size_t cap; \ + uint8_t buffer[(N)]; \ + }) { \ + 0, (N), {} \ + } + +#define LOCAL_BYTES(N) (bytes_t *)LOCAL_BYTES_ANONYMOUS(N); + +static inline error_t *bytes_append_uint8(bytes_t *bytes, uint8_t value) { + if (bytes->len >= bytes->cap) + return err_bytes_no_capacity; + bytes->buffer[bytes->len++] = value; + return nullptr; +} + +static inline error_t *bytes_append_array(bytes_t *dst, size_t n, + uint8_t buffer[static n]) { + if (dst->len + n >= dst->cap) + return err_bytes_no_capacity; + memcpy(dst->buffer + dst->len, buffer, n); + dst->len += n; + return nullptr; +} + +static inline error_t *bytes_append_bytes(bytes_t *dst, bytes_t *src) { + return bytes_append_array(dst, src->len, src->buffer); +} + +static inline error_t *bytes_append_uint16(bytes_t *dst, uint16_t value) { + return bytes_append_array(dst, sizeof(value), (uint8_t *)&value); +} + +static inline error_t *bytes_append_uint32(bytes_t *dst, uint32_t value) { + return bytes_append_array(dst, sizeof(value), (uint8_t *)&value); +} + +static inline error_t *bytes_append_uint64(bytes_t *dst, uint64_t value) { + return bytes_append_array(dst, sizeof(value), (uint8_t *)&value); +} + +#endif // INCLUDE_SRC_BYTES_H_ diff --git a/tests/bytes.c b/tests/bytes.c new file mode 100644 index 0000000..3c98388 --- /dev/null +++ b/tests/bytes.c @@ -0,0 +1,164 @@ +#include "../src/bytes.h" +#include "munit.h" + +MunitResult test_bytes_initializer(const MunitParameter params[], void *data) { + (void)params; + (void)data; + bytes_t *bytes = LOCAL_BYTES(16); + munit_assert_size(bytes->len, ==, 0); + munit_assert_size(bytes->cap, ==, 16); + for (size_t i = 0; i < 16; ++i) + munit_assert_uint8(bytes->buffer[i], ==, 0); + return MUNIT_OK; +} + +MunitResult test_bytes_append_uint8(const MunitParameter params[], void *data) { + (void)params; + (void)data; + bytes_t *bytes = LOCAL_BYTES(16); + munit_assert_size(bytes->len, ==, 0); + munit_assert_size(bytes->cap, ==, 16); + for (size_t i = 0; i < 16; ++i) { + error_t *err = bytes_append_uint8(bytes, (uint8_t)i); + munit_assert_null(err); + munit_assert_uint8(bytes->buffer[i], ==, (uint8_t)i); + } + + error_t *err = bytes_append_uint8(bytes, 0xFF); + munit_assert_ptr(err, ==, err_bytes_no_capacity); + + return MUNIT_OK; +} + +MunitResult test_bytes_append_array(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + bytes_t *bytes = LOCAL_BYTES(16); + munit_assert_size(bytes->len, ==, 0); + munit_assert_size(bytes->cap, ==, 16); + + uint8_t test_array[] = {0x01, 0x02, 0x03, 0x04, 0x05}; + size_t array_len = sizeof(test_array) / sizeof(test_array[0]); + error_t *err = bytes_append_array(bytes, array_len, test_array); + munit_assert_null(err); + munit_assert_size(bytes->len, ==, array_len); + + for (size_t i = 0; i < array_len; ++i) { + munit_assert_uint8(bytes->buffer[i], ==, test_array[i]); + } + + uint8_t second_array[] = {0x06, 0x07, 0x08}; + size_t second_len = sizeof(second_array) / sizeof(second_array[0]); + err = bytes_append_array(bytes, second_len, second_array); + munit_assert_null(err); + munit_assert_size(bytes->len, ==, array_len + second_len); + for (size_t i = 0; i < second_len; ++i) { + munit_assert_uint8(bytes->buffer[array_len + i], ==, second_array[i]); + } + + uint8_t overflow_array[10] = {0}; // Array that would exceed capacity + err = bytes_append_array(bytes, sizeof(overflow_array), overflow_array); + munit_assert_ptr(err, ==, err_bytes_no_capacity); + munit_assert_size(bytes->len, ==, array_len + second_len); + + return MUNIT_OK; +} + +MunitResult test_bytes_append_bytes(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + bytes_t *src = LOCAL_BYTES(8); + bytes_t *dst = LOCAL_BYTES(16); + + // Fill source bytes with test data + for (uint8_t i = 0; i < 5; ++i) { + error_t *err = bytes_append_uint8(src, i + 1); + munit_assert_null(err); + } + munit_assert_size(src->len, ==, 5); + + // Append source to destination + error_t *err = bytes_append_bytes(dst, src); + munit_assert_null(err); + munit_assert_size(dst->len, ==, src->len); + + // Verify destination contents match source + for (size_t i = 0; i < src->len; ++i) { + munit_assert_uint8(dst->buffer[i], ==, src->buffer[i]); + } + + // Fill source with more data and append again + for (uint8_t i = 0; i < 3; ++i) { + err = bytes_append_uint8(src, i + 6); + munit_assert_null(err); + } + munit_assert_size(src->len, ==, 8); + + // Append updated source + err = bytes_append_bytes(dst, src); + munit_assert_null(err); + munit_assert_size(dst->len, ==, 13); // 5 + 8 + + // Test capacity boundary + src->len = 4; // manually set length to barely not fit + err = bytes_append_bytes(dst, src); + munit_assert_ptr(err, ==, err_bytes_no_capacity); + munit_assert_size(dst->len, ==, 13); // Length unchanged after error + + return MUNIT_OK; +} +MunitResult test_bytes_append_uint16(const MunitParameter params[], void *data) { + bytes_t *bytes = LOCAL_BYTES(16); + munit_assert_size(bytes->len, ==, 0); + munit_assert_size(bytes->cap, ==, 16); + + bytes_append_uint16(bytes, 0xFFAA); + munit_assert_size(bytes->len, ==, 2); + munit_assert_uint8(bytes->buffer[0], ==, 0xAA); + munit_assert_uint8(bytes->buffer[1], ==, 0xFF); + + return MUNIT_OK; +} +MunitResult test_bytes_append_uint32(const MunitParameter params[], void *data) { + bytes_t *bytes = LOCAL_BYTES(16); + munit_assert_size(bytes->len, ==, 0); + munit_assert_size(bytes->cap, ==, 16); + + bytes_append_uint32(bytes, 0xAABBCCDD); + munit_assert_size(bytes->len, ==, 4); + munit_assert_uint8(bytes->buffer[0], ==, 0xDD); + munit_assert_uint8(bytes->buffer[1], ==, 0xCC); + munit_assert_uint8(bytes->buffer[2], ==, 0xBB); + munit_assert_uint8(bytes->buffer[3], ==, 0xAA); + return MUNIT_OK; +} +MunitResult test_bytes_append_uint64(const MunitParameter params[], void *data) { + bytes_t *bytes = LOCAL_BYTES(16); + munit_assert_size(bytes->len, ==, 0); + munit_assert_size(bytes->cap, ==, 16); + + bytes_append_uint64(bytes, 0xAABBCCDDEEFF9988); + munit_assert_size(bytes->len, ==, 8); + munit_assert_uint8(bytes->buffer[0], ==, 0x88); + munit_assert_uint8(bytes->buffer[1], ==, 0x99); + munit_assert_uint8(bytes->buffer[2], ==, 0xFF); + munit_assert_uint8(bytes->buffer[3], ==, 0xEE); + munit_assert_uint8(bytes->buffer[4], ==, 0xDD); + munit_assert_uint8(bytes->buffer[5], ==, 0xCC); + munit_assert_uint8(bytes->buffer[6], ==, 0xBB); + munit_assert_uint8(bytes->buffer[7], ==, 0xAA); + return MUNIT_OK; +} + +MunitTest bytes_tests[] = { + {"/initializer", test_bytes_initializer, nullptr, nullptr, MUNIT_TEST_OPTION_NONE, nullptr}, + {"/append_uint8", test_bytes_append_uint8, nullptr, nullptr, MUNIT_TEST_OPTION_NONE, nullptr}, + {"/append_array", test_bytes_append_array, nullptr, nullptr, MUNIT_TEST_OPTION_NONE, nullptr}, + {"/append_bytes", test_bytes_append_bytes, nullptr, nullptr, MUNIT_TEST_OPTION_NONE, nullptr}, + {"/append_uint16", test_bytes_append_uint16, nullptr, nullptr, MUNIT_TEST_OPTION_NONE, nullptr}, + {"/append_uint32", test_bytes_append_uint32, nullptr, nullptr, MUNIT_TEST_OPTION_NONE, nullptr}, + {"/append_uint64", test_bytes_append_uint64, nullptr, nullptr, MUNIT_TEST_OPTION_NONE, nullptr}, + {nullptr, nullptr, nullptr, nullptr, MUNIT_TEST_OPTION_NONE, nullptr} +}; diff --git a/tests/main.c b/tests/main.c index cf7162f..be92b14 100644 --- a/tests/main.c +++ b/tests/main.c @@ -4,6 +4,7 @@ extern MunitTest ast_tests[]; extern MunitTest lexer_tests[]; extern MunitTest regression_tests[]; extern MunitTest symbols_tests[]; +extern MunitTest bytes_tests[]; int main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)]) { MunitSuite suites[] = { @@ -11,6 +12,7 @@ int main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)]) { {"/ast", ast_tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE}, {"/lexer", lexer_tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE}, {"/symbols", symbols_tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE}, + {"/bytes", bytes_tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE}, {nullptr, nullptr, nullptr, 0, MUNIT_SUITE_OPTION_NONE}, };