From c1b2e29c07f3c3679ab797cdbaead04bd3ae10ac Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 30 Aug 2026 02:18:30 +0100 Subject: [PATCH] Add stream structure and utilities --- src/base/stream/stream.c | 40 ++++++++++++++++++++ src/base/stream/stream.h | 75 +++++++++++++++++++++++++++++++++++++ src/base/wapp_base.c | 1 + src/base/wapp_base.h | 1 + tests/stream/test_stream.c | 50 +++++++++++++++++++++++++ tests/stream/test_stream.cc | 50 +++++++++++++++++++++++++ tests/stream/test_stream.h | 12 ++++++ tests/wapptest.c | 4 ++ tests/wapptest.cc | 4 ++ 9 files changed, 237 insertions(+) create mode 100644 src/base/stream/stream.c create mode 100644 src/base/stream/stream.h create mode 100644 tests/stream/test_stream.c create mode 100644 tests/stream/test_stream.cc create mode 100644 tests/stream/test_stream.h diff --git a/src/base/stream/stream.c b/src/base/stream/stream.c new file mode 100644 index 0000000..bda4fd7 --- /dev/null +++ b/src/base/stream/stream.c @@ -0,0 +1,40 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "stream.h" +#include "../../common/aliases/aliases.h" +#include "../../common/assert/assert.h" + +void _streamValidate(const WpStream *stream, u64 item_size); + +b8 _streamAtEnd(const WpStream *stream, u64 item_size) { + _streamValidate(stream, item_size); + return stream->position >= stream->count; +} + +b8 _streamHasNext(const WpStream *stream, u64 item_size) { + _streamValidate(stream, item_size); + return stream->position + 1 < stream->count; +} + +void *_streamPeek(const WpStream *stream, u64 item_size) { + if (_streamHasNext(stream, item_size)) { + u64 index = (stream->position + 1) * stream->item_size; + return (void *)(&((u8 *)(stream->data))[index]); + } + + return NULL; +} + +void *_streamConsume(WpStream *stream, u64 item_size) { + if (!_streamAtEnd(stream, item_size)) { + u64 index = stream->position++ * stream->item_size; + return (void *)(&((u8 *)(stream->data))[index]); + } + + return NULL; +} + +void _streamValidate(const WpStream *stream, u64 item_size) { + wpDebugAssert(stream != NULL, "`stream` should not be NULL"); + wpRuntimeAssert(stream->item_size == item_size, "Invalid item type provided"); +} diff --git a/src/base/stream/stream.h b/src/base/stream/stream.h new file mode 100644 index 0000000..9a235bb --- /dev/null +++ b/src/base/stream/stream.h @@ -0,0 +1,75 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef STREAM_H +#define STREAM_H + +#include "../../common/aliases/aliases.h" +#include "../../common/platform/platform.h" + +#ifdef WP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WP_PLATFORM_CPP + +typedef struct { + void *data; + u64 count; + u64 position; + u64 item_size; +} WpStream; + +// NOTE (Abdelrahman): WpStream typedefs for readability +typedef WpStream WpVoidPtrStream; +typedef WpStream WpC8Stream; +typedef WpStream WpC16Stream; +typedef WpStream WpC32Stream; +typedef WpStream WpU8Stream; +typedef WpStream WpU16Stream; +typedef WpStream WpU32Stream; +typedef WpStream WpU64Stream; +typedef WpStream WpB8Stream; +typedef WpStream WpI8Stream; +typedef WpStream WpI16Stream; +typedef WpStream WpI32Stream; +typedef WpStream WpI64Stream; +typedef WpStream WpF32Stream; +typedef WpStream WpF64Stream; +typedef WpStream WpF128Stream; +typedef WpStream WpUptrStream; +typedef WpStream WpIptrStream; +typedef WpStream WpStr8Stream; + +#ifdef WP_PLATFORM_CPP +#define wpStream(TYPE, DATA, SIZE) ([&]() { \ + return WpStream{ \ + DATA, \ + SIZE / sizeof(TYPE), \ + 0, \ + sizeof(TYPE) \ + }; \ +}()) +#else +#define wpStream(TYPE, DATA, SIZE) ( \ + (WpStream){ \ + .data = DATA, \ + .count = SIZE / sizeof(TYPE), \ + .position = 0, \ + .item_size = sizeof(TYPE) \ + } \ +) +#endif + +#define wpStreamAtEnd(TYPE, STREAM) _streamAtEnd(STREAM, sizeof(TYPE)) +#define wpStreamHasNext(TYPE, STREAM) _streamHasNext(STREAM, sizeof(TYPE)) +#define wpStreamPeek(TYPE, STREAM) ((TYPE *)_streamPeek(STREAM, sizeof(TYPE))) +#define wpStreamConsume(TYPE, STREAM) ((TYPE *)_streamConsume(STREAM, sizeof(TYPE))) + +b8 _streamAtEnd(const WpStream *stream, u64 item_size); +b8 _streamHasNext(const WpStream *stream, u64 item_size); +void *_streamPeek(const WpStream *stream, u64 item_size); +void *_streamConsume(WpStream *stream, u64 item_size); + +#ifdef WP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WP_PLATFORM_CPP + +#endif // !STREAM_H diff --git a/src/base/wapp_base.c b/src/base/wapp_base.c index 8f22291..eeb3369 100644 --- a/src/base/wapp_base.c +++ b/src/base/wapp_base.c @@ -7,6 +7,7 @@ #include "array/array.c" #include "dbl_list/dbl_list.c" #include "queue/queue.c" +#include "stream/stream.c" #include "mem/allocator/mem_allocator.c" #include "mem/utils/mem_utils.c" #include "strings/str8/str8.c" diff --git a/src/base/wapp_base.h b/src/base/wapp_base.h index 4c48c1d..c7be92f 100644 --- a/src/base/wapp_base.h +++ b/src/base/wapp_base.h @@ -6,6 +6,7 @@ #include "array/array.h" #include "dbl_list/dbl_list.h" #include "queue/queue.h" +#include "stream/stream.h" #include "mem/allocator/mem_allocator.h" #include "mem/utils/mem_utils.h" #include "strings/str8/str8.h" diff --git a/tests/stream/test_stream.c b/tests/stream/test_stream.c new file mode 100644 index 0000000..1270e77 --- /dev/null +++ b/tests/stream/test_stream.c @@ -0,0 +1,50 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "test_stream.h" + +#define COUNT 3 + +WpTestFuncResult test_stream_at_end(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + for (u32 i = 0; i < COUNT; ++i) { + wpStreamConsume(c8, &stream); + result = result && (i == 2 ? wpStreamAtEnd(c8, &stream) : !wpStreamAtEnd(c8, &stream)); + } + + return wpTesterResult(result); +} + +WpTestFuncResult test_stream_peek(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + c8 *c = NULL; + while (!wpStreamAtEnd(c8, &stream)) { + c = wpStreamPeek(c8, &stream); + wpStreamConsume(c8, &stream); + result = result && (wpStreamAtEnd(c8, &stream) ? c == NULL : c != NULL); + } + + return wpTesterResult(result); +} + +WpTestFuncResult test_stream_consume(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + c8 *c = NULL; + for (u32 i = 0; i <= COUNT; ++i) { + c = wpStreamConsume(c8, &stream); + result = result && (i == COUNT ? c == NULL : c != NULL); + } + + return wpTesterResult(result); +} diff --git a/tests/stream/test_stream.cc b/tests/stream/test_stream.cc new file mode 100644 index 0000000..1270e77 --- /dev/null +++ b/tests/stream/test_stream.cc @@ -0,0 +1,50 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "test_stream.h" + +#define COUNT 3 + +WpTestFuncResult test_stream_at_end(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + for (u32 i = 0; i < COUNT; ++i) { + wpStreamConsume(c8, &stream); + result = result && (i == 2 ? wpStreamAtEnd(c8, &stream) : !wpStreamAtEnd(c8, &stream)); + } + + return wpTesterResult(result); +} + +WpTestFuncResult test_stream_peek(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + c8 *c = NULL; + while (!wpStreamAtEnd(c8, &stream)) { + c = wpStreamPeek(c8, &stream); + wpStreamConsume(c8, &stream); + result = result && (wpStreamAtEnd(c8, &stream) ? c == NULL : c != NULL); + } + + return wpTesterResult(result); +} + +WpTestFuncResult test_stream_consume(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + c8 *c = NULL; + for (u32 i = 0; i <= COUNT; ++i) { + c = wpStreamConsume(c8, &stream); + result = result && (i == COUNT ? c == NULL : c != NULL); + } + + return wpTesterResult(result); +} diff --git a/tests/stream/test_stream.h b/tests/stream/test_stream.h new file mode 100644 index 0000000..ac023e9 --- /dev/null +++ b/tests/stream/test_stream.h @@ -0,0 +1,12 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#ifndef TEST_STREAM_H +#define TEST_STREAM_H + +#include "wapp.h" + +WpTestFuncResult test_stream_at_end(void); +WpTestFuncResult test_stream_peek(void); +WpTestFuncResult test_stream_consume(void); + +#endif // !TEST_STREAM_H diff --git a/tests/wapptest.c b/tests/wapptest.c index d355bf1..1d89090 100644 --- a/tests/wapptest.c +++ b/tests/wapptest.c @@ -5,6 +5,7 @@ #include "test_str8_array.h" #include "test_i32_array.h" #include "test_queue.h" +#include "test_stream.h" #include "test_cpath.h" #include "test_file.h" #include "test_shell_commander.h" @@ -47,6 +48,9 @@ int main(void) { test_queue_push, test_queue_push_alloc, test_queue_pop, + test_stream_at_end, + test_stream_peek, + test_stream_consume, test_str8_lit, test_str8_lit_ro, test_str8_buf, diff --git a/tests/wapptest.cc b/tests/wapptest.cc index d355bf1..1d89090 100644 --- a/tests/wapptest.cc +++ b/tests/wapptest.cc @@ -5,6 +5,7 @@ #include "test_str8_array.h" #include "test_i32_array.h" #include "test_queue.h" +#include "test_stream.h" #include "test_cpath.h" #include "test_file.h" #include "test_shell_commander.h" @@ -47,6 +48,9 @@ int main(void) { test_queue_push, test_queue_push_alloc, test_queue_pop, + test_stream_at_end, + test_stream_peek, + test_stream_consume, test_str8_lit, test_str8_lit_ro, test_str8_buf,