diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cce839..eb5bab9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.4.0] - 2026-08-30 + +### Added + +- `wpStreamHasCount`, `wpStreamPeekWithOffset` and `wpStreamConsumeCount` utilities + +### Fixed + +- Logic for peeking at stream contents + ## [2.3.0] - 2026-08-29 ### Added diff --git a/VERSION b/VERSION index 276cbf9..197c4d5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3.0 +2.4.0 diff --git a/src/base/stream/stream.c b/src/base/stream/stream.c index bda4fd7..c63636b 100644 --- a/src/base/stream/stream.c +++ b/src/base/stream/stream.c @@ -6,28 +6,31 @@ void _streamValidate(const WpStream *stream, u64 item_size); -b8 _streamAtEnd(const WpStream *stream, u64 item_size) { - _streamValidate(stream, item_size); +b8 wpStreamAtEnd(const WpStream *stream) { return stream->position >= stream->count; } -b8 _streamHasNext(const WpStream *stream, u64 item_size) { - _streamValidate(stream, item_size); - return stream->position + 1 < stream->count; +b8 _streamHasCount(const WpStream *stream, u64 count) { + return stream->position + count <= stream->count; } -void *_streamPeek(const WpStream *stream, u64 item_size) { - if (_streamHasNext(stream, item_size)) { - u64 index = (stream->position + 1) * stream->item_size; +void *_streamPeekWithOffset(const WpStream *stream, u64 offset, u64 item_size) { + _streamValidate(stream, item_size); + + if (_streamHasCount(stream, offset + 1)) { + u64 index = (stream->position + offset) * 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; +void *_streamConsumeCount(WpStream *stream, u64 count, u64 item_size) { + _streamValidate(stream, item_size); + + if (_streamHasCount(stream, count)) { + u64 index = stream->position * stream->item_size; + stream->position += count; return (void *)(&((u8 *)(stream->data))[index]); } diff --git a/src/base/stream/stream.h b/src/base/stream/stream.h index 9a235bb..7b030a6 100644 --- a/src/base/stream/stream.h +++ b/src/base/stream/stream.h @@ -58,15 +58,20 @@ typedef WpStream WpStr8Stream; ) #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))) +#define wpStreamHasNext(STREAM) _streamHasCount(STREAM, 1) +#define wpStreamHasCount(STREAM, COUNT) _streamHasCount(STREAM, COUNT) +#define wpStreamPeek(TYPE, STREAM) ((TYPE *)_streamPeekWithOffset(STREAM, 0, sizeof(TYPE))) +#define wpStreamPeekWithOffset(TYPE, STREAM, OFFSET) \ + ((TYPE *)_streamPeekWithOffset(STREAM, OFFSET, sizeof(TYPE))) +#define wpStreamConsume(TYPE, STREAM) ((TYPE *)_streamConsumeCount(STREAM, 1, sizeof(TYPE))) +#define wpStreamConsumeCount(TYPE, STREAM, COUNT) \ + ((TYPE *)_streamConsumeCount(STREAM, COUNT, 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); +b8 wpStreamAtEnd(const WpStream *stream); + +b8 _streamHasCount(const WpStream *stream, u64 count); +void *_streamPeekWithOffset(const WpStream *stream, u64 offset, u64 item_size); +void *_streamConsumeCount(WpStream *stream, u64 count, u64 item_size); #ifdef WP_PLATFORM_CPP END_C_LINKAGE diff --git a/src/log/log.h b/src/log/log.h index 3a4dbb0..8015023 100644 --- a/src/log/log.h +++ b/src/log/log.h @@ -6,6 +6,10 @@ #include "../os/file/file.h" #include "../base/strings/str8/str8.h" +#ifdef WP_PLATFORM_CPP +BEGIN_C_LINKAGE +#endif // !WP_PLATFORM_CPP + typedef enum { WP_LOG_LEVEL_FATAL, WP_LOG_LEVEL_CRITICAL, @@ -31,4 +35,8 @@ void wpLogError(const WpLogger *logger, WpStr8 msg); void wpLogCritical(const WpLogger *logger, WpStr8 msg); void wpLogFatal(const WpLogger *logger, WpStr8 msg); +#ifdef WP_PLATFORM_CPP +END_C_LINKAGE +#endif // !WP_PLATFORM_CPP + #endif // !LOG_H diff --git a/tests/stream/test_stream.c b/tests/stream/test_stream.c index 1270e77..7a43a00 100644 --- a/tests/stream/test_stream.c +++ b/tests/stream/test_stream.c @@ -1,6 +1,7 @@ // vim:fileencoding=utf-8:foldmethod=marker #include "test_stream.h" +#include #define COUNT 3 @@ -12,7 +13,7 @@ WpTestFuncResult test_stream_at_end(void) { for (u32 i = 0; i < COUNT; ++i) { wpStreamConsume(c8, &stream); - result = result && (i == 2 ? wpStreamAtEnd(c8, &stream) : !wpStreamAtEnd(c8, &stream)); + result = result && (i == 2 ? wpStreamAtEnd(&stream) : !wpStreamAtEnd(&stream)); } return wpTesterResult(result); @@ -25,15 +26,31 @@ WpTestFuncResult test_stream_peek(void) { WpC8Stream stream = wpStream(c8, characters, COUNT); c8 *c = NULL; - while (!wpStreamAtEnd(c8, &stream)) { + for (u32 i = 0; i <= COUNT; ++i) { c = wpStreamPeek(c8, &stream); wpStreamConsume(c8, &stream); - result = result && (wpStreamAtEnd(c8, &stream) ? c == NULL : c != NULL); + result = result && (i == 3 ? c == NULL : c != NULL); } return wpTesterResult(result); } +WpTestFuncResult test_stream_peek_with_offset(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + u64 offset = 2; + c8 *c = wpStreamPeekWithOffset(c8, &stream, offset); + result = result && *c == characters[offset]; + + c = wpStreamPeekWithOffset(c8, &stream, 5); + result = result && c == NULL; + + return wpTesterResult(result); +} + WpTestFuncResult test_stream_consume(void) { b8 result = true; @@ -48,3 +65,18 @@ WpTestFuncResult test_stream_consume(void) { return wpTesterResult(result); } + +WpTestFuncResult test_stream_consume_count(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + c8 *c = wpStreamConsumeCount(c8, &stream, COUNT); + result = result && c != NULL && wpStreamAtEnd(&stream); + + c = wpStreamConsumeCount(c8, &stream, COUNT); + result = result && c == NULL; + + return wpTesterResult(result); +} diff --git a/tests/stream/test_stream.cc b/tests/stream/test_stream.cc index 1270e77..7a43a00 100644 --- a/tests/stream/test_stream.cc +++ b/tests/stream/test_stream.cc @@ -1,6 +1,7 @@ // vim:fileencoding=utf-8:foldmethod=marker #include "test_stream.h" +#include #define COUNT 3 @@ -12,7 +13,7 @@ WpTestFuncResult test_stream_at_end(void) { for (u32 i = 0; i < COUNT; ++i) { wpStreamConsume(c8, &stream); - result = result && (i == 2 ? wpStreamAtEnd(c8, &stream) : !wpStreamAtEnd(c8, &stream)); + result = result && (i == 2 ? wpStreamAtEnd(&stream) : !wpStreamAtEnd(&stream)); } return wpTesterResult(result); @@ -25,15 +26,31 @@ WpTestFuncResult test_stream_peek(void) { WpC8Stream stream = wpStream(c8, characters, COUNT); c8 *c = NULL; - while (!wpStreamAtEnd(c8, &stream)) { + for (u32 i = 0; i <= COUNT; ++i) { c = wpStreamPeek(c8, &stream); wpStreamConsume(c8, &stream); - result = result && (wpStreamAtEnd(c8, &stream) ? c == NULL : c != NULL); + result = result && (i == 3 ? c == NULL : c != NULL); } return wpTesterResult(result); } +WpTestFuncResult test_stream_peek_with_offset(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + u64 offset = 2; + c8 *c = wpStreamPeekWithOffset(c8, &stream, offset); + result = result && *c == characters[offset]; + + c = wpStreamPeekWithOffset(c8, &stream, 5); + result = result && c == NULL; + + return wpTesterResult(result); +} + WpTestFuncResult test_stream_consume(void) { b8 result = true; @@ -48,3 +65,18 @@ WpTestFuncResult test_stream_consume(void) { return wpTesterResult(result); } + +WpTestFuncResult test_stream_consume_count(void) { + b8 result = true; + + c8 characters[COUNT] = {'a', 'b', 'c'}; + WpC8Stream stream = wpStream(c8, characters, COUNT); + + c8 *c = wpStreamConsumeCount(c8, &stream, COUNT); + result = result && c != NULL && wpStreamAtEnd(&stream); + + c = wpStreamConsumeCount(c8, &stream, COUNT); + result = result && c == NULL; + + return wpTesterResult(result); +} diff --git a/tests/stream/test_stream.h b/tests/stream/test_stream.h index ac023e9..9d9fb63 100644 --- a/tests/stream/test_stream.h +++ b/tests/stream/test_stream.h @@ -7,6 +7,8 @@ WpTestFuncResult test_stream_at_end(void); WpTestFuncResult test_stream_peek(void); +WpTestFuncResult test_stream_peek_with_offset(void); WpTestFuncResult test_stream_consume(void); +WpTestFuncResult test_stream_consume_count(void); #endif // !TEST_STREAM_H diff --git a/tests/wapptest.c b/tests/wapptest.c index 1d89090..38ad6ef 100644 --- a/tests/wapptest.c +++ b/tests/wapptest.c @@ -50,7 +50,9 @@ int main(void) { test_queue_pop, test_stream_at_end, test_stream_peek, + test_stream_peek_with_offset, test_stream_consume, + test_stream_consume_count, test_str8_lit, test_str8_lit_ro, test_str8_buf, diff --git a/tests/wapptest.cc b/tests/wapptest.cc index 1d89090..38ad6ef 100644 --- a/tests/wapptest.cc +++ b/tests/wapptest.cc @@ -50,7 +50,9 @@ int main(void) { test_queue_pop, test_stream_at_end, test_stream_peek, + test_stream_peek_with_offset, test_stream_consume, + test_stream_consume_count, test_str8_lit, test_str8_lit_ro, test_str8_buf,