diff --git a/lib/inc/centurion/core/memory.hpp b/lib/inc/centurion/core/memory.hpp new file mode 100644 index 000000000..41f3f6613 --- /dev/null +++ b/lib/inc/centurion/core/memory.hpp @@ -0,0 +1,20 @@ +// Copyright (C) 2019-2024 Albin Johansson (MIT License) + +#pragma once + +#include + +#include + +namespace cen::inline v8_0_0 { + +struct sdl_free_deleter +{ + // SDL_free + void operator()(void* ptr) const noexcept; +}; + +template +using unique_sdl_ptr = std::unique_ptr; + +} // namespace cen::inline v8_0_0 diff --git a/lib/inc/centurion/io/iostream.hpp b/lib/inc/centurion/io/iostream.hpp new file mode 100644 index 000000000..4c40853e0 --- /dev/null +++ b/lib/inc/centurion/io/iostream.hpp @@ -0,0 +1,229 @@ +// Copyright (C) 2019-2024 Albin Johansson (MIT License) + +#pragma once + +#include +#include +#include +#include + +#include + +#include "centurion/core/error.hpp" +#include "centurion/core/memory.hpp" + +namespace cen::inline v8_0_0 { + +enum class seek_mode : int +{ + start = SDL_IO_SEEK_SET, + current = SDL_IO_SEEK_CUR, + end = SDL_IO_SEEK_END, +}; + +class iostream_base +{ + public: + using size_type = std::size_t; + using difference_type = std::int64_t; + + // SDL_SeekIO + auto seek(seek_mode mode, difference_type offset = 0) noexcept + -> std::optional; + + // SDL_TellIO + [[nodiscard]] + auto tell() const noexcept -> std::optional; + + // SDL_ReadIO + auto read(void* data, size_type size) noexcept -> size_type; + + // SDL_ReadU8 + [[nodiscard]] + auto read_u8() noexcept -> std::optional; + + // SDL_ReadU16LE + [[nodiscard]] + auto read_u16_le() noexcept -> std::optional; + + // SDL_ReadU16BE + [[nodiscard]] + auto read_u16_be() noexcept -> std::optional; + + // SDL_Reads16LE + [[nodiscard]] + auto read_s16_le() noexcept -> std::optional; + + // SDL_Reads16BE + [[nodiscard]] + auto read_s16_be() noexcept -> std::optional; + + // SDL_ReadU32LE + [[nodiscard]] + auto read_u32_le() noexcept -> std::optional; + + // SDL_ReadU32BE + [[nodiscard]] + auto read_u32_be() noexcept -> std::optional; + + // SDL_Reads32LE + [[nodiscard]] + auto read_s32_le() noexcept -> std::optional; + + // SDL_Reads32BE + [[nodiscard]] + auto read_s32_be() noexcept -> std::optional; + + // SDL_ReadU64LE + [[nodiscard]] + auto read_u64_le() noexcept -> std::optional; + + // SDL_ReadU64BE + [[nodiscard]] + auto read_u64_be() noexcept -> std::optional; + + // SDL_Reads64LE + [[nodiscard]] + auto read_s64_le() noexcept -> std::optional; + + // SDL_Reads64BE + [[nodiscard]] + auto read_s64_be() noexcept -> std::optional; + + // SDL_WriteIO + auto write(const void* data, size_type size) noexcept -> size_type; + + // SDL_WriteU8 + auto write_u8(std::uint8_t value) noexcept -> result; + + // SDL_WriteU16LE + auto write_u16_le(std::uint16_t value) noexcept -> result; + + // SDL_WriteU16BE + auto write_u16_be(std::uint16_t value) noexcept -> result; + + // SDL_WriteS16LE + auto write_s16_le(std::int16_t value) noexcept -> result; + + // SDL_WriteS16BE + auto write_s16_be(std::int16_t value) noexcept -> result; + + // SDL_WriteU32LE + auto write_u32_le(std::uint32_t value) noexcept -> result; + + // SDL_WriteU32BE + auto write_u32_be(std::uint32_t value) noexcept -> result; + + // SDL_WriteS32LE + auto write_s32_le(std::int32_t value) noexcept -> result; + + // SDL_WriteS32BE + auto write_s32_be(std::int32_t value) noexcept -> result; + + // SDL_WriteU64LE + auto write_u64_le(std::uint64_t value) noexcept -> result; + + // SDL_WriteU64BE + auto write_u64_be(std::uint64_t value) noexcept -> result; + + // SDL_WriteS64LE + auto write_s64_le(std::int64_t value) noexcept -> result; + + // SDL_WriteS64BE + auto write_s64_be(std::int64_t value) noexcept -> result; + + // SDL_GetIOSize + [[nodiscard]] + auto size() const noexcept -> std::optional; + + // SDL_GetIOStatus + [[nodiscard]] + auto status() const noexcept -> SDL_IOStatus; + + // SDL_GetIOProperties + [[nodiscard]] + auto get_properties() const noexcept -> std::optional; + + [[nodiscard]] + auto get() noexcept -> SDL_IOStream*; + + [[nodiscard]] + auto get() const noexcept -> const SDL_IOStream*; + + [[nodiscard]] + virtual auto unsafe_get() const noexcept -> SDL_IOStream* = 0; +}; + +class iostream final : public iostream_base +{ + public: + // SDL_IOFromFile + [[nodiscard]] + static auto from_file(const char* file, + const char* mode) noexcept -> std::optional; + + // SDL_IOFromMem + [[nodiscard]] + static auto from_memory(void* data, + size_type size) noexcept -> std::optional; + + // SDL_IOFromConstMem + [[nodiscard]] + static auto from_const_memory(const void* data, size_type size) noexcept + -> std::optional; + + // SDL_IOFromDynamicMem + [[nodiscard]] + static auto from_dynamic_memory() noexcept -> std::optional; + + [[nodiscard]] + auto unsafe_get() const noexcept -> SDL_IOStream* override; + + private: + struct iostream_deleter + { + void operator()(SDL_IOStream* stream) const noexcept; + }; + + std::unique_ptr m_stream; + + explicit iostream(SDL_IOStream* ptr) noexcept; +}; + +class iostream_ref final : public iostream_base +{ + public: + explicit iostream_ref(SDL_IOStream* ptr = nullptr) noexcept; + + /* implicit */ iostream_ref(iostream& other) noexcept; + + [[nodiscard]] + auto is_null() const noexcept -> bool; + + [[nodiscard]] + explicit operator bool() const noexcept; + + [[nodiscard]] + auto unsafe_get() const noexcept -> SDL_IOStream* override; + + private: + SDL_IOStream* m_stream; +}; + +using iostream_cref = const iostream_ref&; + +struct file_data final +{ + unique_sdl_ptr data; + std::size_t size; +}; + +// SDL_LoadFile +[[nodiscard]] +auto load_file(const char* file) noexcept -> std::optional; + +// SDL_LoadFile_IO +[[nodiscard]] +auto load_file(iostream_ref stream) noexcept -> std::optional; + +} // namespace cen::inline v8_0_0 diff --git a/lib/src/centurion/core/memory.cpp b/lib/src/centurion/core/memory.cpp new file mode 100644 index 000000000..e7ac23cde --- /dev/null +++ b/lib/src/centurion/core/memory.cpp @@ -0,0 +1,12 @@ +// Copyright (C) 2019-2024 Albin Johansson (MIT License) + +#include "centurion/core/memory.hpp" + +namespace cen::inline v8_0_0 { + +void sdl_free_deleter::operator()(void* ptr) const noexcept +{ + SDL_free(ptr); +} + +} // namespace cen::inline v8_0_0 diff --git a/lib/src/centurion/io/iostream.cpp b/lib/src/centurion/io/iostream.cpp new file mode 100644 index 000000000..d4e8dfe46 --- /dev/null +++ b/lib/src/centurion/io/iostream.cpp @@ -0,0 +1,538 @@ +// Copyright (C) 2019-2024 Albin Johansson (MIT License) + +#include "centurion/io/iostream.hpp" + +#include "centurion/core/error.hpp" +#include "centurion/detail/enum.hpp" + +namespace cen::inline v8_0_0 { + +auto iostream_base::seek(const seek_mode mode, + const difference_type offset) noexcept + -> std::optional +{ + const auto final_offset = + SDL_SeekIO(get(), offset, detail::to_underlying(mode)); + + if (final_offset < 0) { + emit_sdl_error(); + return std::nullopt; + } + + return final_offset; +} + +auto iostream_base::tell() const noexcept -> std::optional +{ + const auto offset = SDL_TellIO(unsafe_get()); + + if (offset == -1) { + emit_sdl_error(); + return std::nullopt; + } + + return offset; +} + +auto iostream_base::read(void* const data, + const size_type size) noexcept -> size_type +{ + return SDL_ReadIO(get(), data, size); +} + +auto iostream_base::read_u8() noexcept -> std::optional +{ + std::uint8_t value {}; + const auto succeeded = SDL_ReadU8(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_u16_le() noexcept -> std::optional +{ + std::uint16_t value {}; + const auto succeeded = SDL_ReadU16LE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_u16_be() noexcept -> std::optional +{ + std::uint16_t value {}; + const auto succeeded = SDL_ReadU16BE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_s16_le() noexcept -> std::optional +{ + std::int16_t value {}; + const auto succeeded = SDL_ReadS16LE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_s16_be() noexcept -> std::optional +{ + std::int16_t value {}; + const auto succeeded = SDL_ReadS16BE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_u32_le() noexcept -> std::optional +{ + std::uint32_t value {}; + const auto succeeded = SDL_ReadU32LE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_u32_be() noexcept -> std::optional +{ + std::uint32_t value {}; + const auto succeeded = SDL_ReadU32BE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_s32_le() noexcept -> std::optional +{ + std::int32_t value {}; + const auto succeeded = SDL_ReadS32LE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_s32_be() noexcept -> std::optional +{ + std::int32_t value {}; + const auto succeeded = SDL_ReadS32BE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_u64_le() noexcept -> std::optional +{ + std::uint64_t value {}; + const auto succeeded = SDL_ReadU64LE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_u64_be() noexcept -> std::optional +{ + std::uint64_t value {}; + const auto succeeded = SDL_ReadU64BE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_s64_le() noexcept -> std::optional +{ + std::int64_t value {}; + const auto succeeded = SDL_ReadS64LE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::read_s64_be() noexcept -> std::optional +{ + std::int64_t value {}; + const auto succeeded = SDL_ReadS64BE(get(), &value); + + if (!succeeded) { + emit_sdl_error(); + return std::nullopt; + } + + return value; +} + +auto iostream_base::write(const void* const data, + const size_type size) noexcept -> size_type +{ + const auto written_bytes = SDL_WriteIO(get(), data, size); + + if (written_bytes < size) { + emit_sdl_error(); + } + + return written_bytes; +} + +auto iostream_base::write_u8(const std::uint8_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteU8(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_u16_le(const std::uint16_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteU16LE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_u16_be(const std::uint16_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteU16BE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_s16_le(const std::int16_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteS16LE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_s16_be(const std::int16_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteS16BE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_u32_le(const std::uint32_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteU32LE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_u32_be(const std::uint32_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteU32BE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_s32_le(const std::int32_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteS32LE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_s32_be(const std::int32_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteS32BE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_u64_le(const std::uint64_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteU64LE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_u64_be(const std::uint64_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteU64BE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_s64_le(const std::int64_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteS64LE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::write_s64_be(const std::int64_t value) noexcept -> result +{ + const auto succeeded = SDL_WriteS64BE(get(), value); + + if (!succeeded) { + emit_sdl_error(); + return result::failure; + } + + return result::success; +} + +auto iostream_base::size() const noexcept -> std::optional +{ + const auto io_size = SDL_GetIOSize(unsafe_get()); + + if (io_size < 0) { + emit_sdl_error(); + return std::nullopt; + } + + return io_size; +} + +auto iostream_base::status() const noexcept -> SDL_IOStatus +{ + return SDL_GetIOStatus(unsafe_get()); +} + +auto iostream_base::get_properties() const noexcept + -> std::optional +{ + const auto id = SDL_GetIOProperties(unsafe_get()); + + if (id == 0) { + emit_sdl_error(); + return std::nullopt; + } + + return id; +} + +auto iostream_base::get() noexcept -> SDL_IOStream* +{ + return unsafe_get(); +} + +auto iostream_base::get() const noexcept -> const SDL_IOStream* +{ + return unsafe_get(); +} + +void iostream::iostream_deleter::operator()(SDL_IOStream* stream) const noexcept +{ + const auto ec = SDL_CloseIO(stream); + if (ec != 0) { + emit_sdl_error(); + } +} + +iostream::iostream(SDL_IOStream* ptr) noexcept + : m_stream {ptr} +{} + +auto iostream::from_file(const char* file, + const char* mode) noexcept -> std::optional +{ + auto* const ptr = SDL_IOFromFile(file, mode); + + if (!ptr) { + emit_sdl_error(); + return std::nullopt; + } + + return iostream {ptr}; +} + +auto iostream::from_memory(void* data, const size_type size) noexcept + -> std::optional +{ + auto* const ptr = SDL_IOFromMem(data, size); + + if (!ptr) { + emit_sdl_error(); + return std::nullopt; + } + + return iostream {ptr}; +} + +auto iostream::from_const_memory(const void* data, + const size_type size) noexcept + -> std::optional +{ + auto* const ptr = SDL_IOFromConstMem(data, size); + + if (!ptr) { + emit_sdl_error(); + return std::nullopt; + } + + return iostream {ptr}; +} + +auto iostream::from_dynamic_memory() noexcept -> std::optional +{ + auto* const ptr = SDL_IOFromDynamicMem(); + + if (!ptr) { + emit_sdl_error(); + return std::nullopt; + } + + return iostream {ptr}; +} + +auto iostream::unsafe_get() const noexcept -> SDL_IOStream* +{ + return m_stream.get(); +} + +iostream_ref::iostream_ref(SDL_IOStream* ptr) noexcept + : m_stream {ptr} +{} + +iostream_ref::iostream_ref(iostream& other) noexcept + : iostream_ref {other.get()} +{} + +auto iostream_ref::is_null() const noexcept -> bool +{ + return m_stream == nullptr; +} + +iostream_ref::operator bool() const noexcept +{ + return !is_null(); +} + +auto iostream_ref::unsafe_get() const noexcept -> SDL_IOStream* +{ + return m_stream; +} + +auto load_file(const char* file) noexcept -> std::optional +{ + file_data result {}; + result.data.reset(SDL_LoadFile(file, &result.size)); + + if (!result.data) { + emit_sdl_error(); + return std::nullopt; + } + + return result; +} + +auto load_file(iostream_ref stream) noexcept -> std::optional +{ + file_data result {}; + result.data.reset(SDL_LoadFile_IO(stream.get(), &result.size, SDL_FALSE)); + + if (!result.data) { + emit_sdl_error(); + return std::nullopt; + } + + return result; +} + +} // namespace cen::inline v8_0_0 diff --git a/test/demo/CMakeLists.txt b/test/demo/CMakeLists.txt index 01d2eb778..9120bb56c 100644 --- a/test/demo/CMakeLists.txt +++ b/test/demo/CMakeLists.txt @@ -1,3 +1,4 @@ project(centurion-test-demo CXX) add_subdirectory(basic) +add_subdirectory(iostream) diff --git a/test/demo/iostream/CMakeLists.txt b/test/demo/iostream/CMakeLists.txt new file mode 100644 index 000000000..ffd354aad --- /dev/null +++ b/test/demo/iostream/CMakeLists.txt @@ -0,0 +1,9 @@ +project(centurion-test-demo-iostream CXX) + +add_executable(centurion_iostream_demo) + +target_sources(centurion_iostream_demo PRIVATE "main.cpp") + +cen_prepare_target_options(centurion_iostream_demo) + +target_link_libraries(centurion_iostream_demo PUBLIC centurion::centurion) diff --git a/test/demo/iostream/main.cpp b/test/demo/iostream/main.cpp new file mode 100644 index 000000000..5021260bb --- /dev/null +++ b/test/demo/iostream/main.cpp @@ -0,0 +1,85 @@ +// Copyright (C) 2019-2024 Albin Johansson (MIT License) + +#include +#include + +#include + +#include "centurion/core/init.hpp" +#include "centurion/io/iostream.hpp" + +namespace demo { + +[[nodiscard]] +auto get_status_name(const SDL_IOStatus status) noexcept -> const char* +{ + switch (status) { + case SDL_IO_STATUS_READY: return "READY"; + case SDL_IO_STATUS_ERROR: return "ERROR"; + case SDL_IO_STATUS_EOF: return "EOF"; + case SDL_IO_STATUS_NOT_READY: return "NOT_READY"; + case SDL_IO_STATUS_READONLY: return "READONLY"; + case SDL_IO_STATUS_WRITEONLY: return "WRITEONLY"; + default: return "?"; + } +} + +void show_stream_info(cen::iostream_ref stream) noexcept +{ + const auto size = stream.size().value(); + const auto offset = stream.tell().value(); + + SDL_Log(" stream.status() == %s", get_status_name(stream.status())); + SDL_Log(" stream.size() == %" PRId64, size); + SDL_Log(" stream.tell() == %" PRId64, offset); + + char buffer[128] {}; + stream.read(buffer, sizeof buffer); + stream.seek(cen::seek_mode::start, offset); + + SDL_Log(" stream data after cursor == \"%s\"", buffer); +} + +} // namespace demo + +auto main(int, char*[]) -> int +{ + const auto sdl [[maybe_unused]] = cen::sdl_library::init(0).value(); + + SDL_SetLogOutputFunction( + [](void*, int, SDL_LogPriority, const char* msg) { + std::printf("%s\n", msg); + }, + nullptr); + + auto stream = cen::iostream::from_dynamic_memory().value(); + + SDL_Log("==> Created stream from dynamic memory"); + SDL_Log(" stream.get_properties() == %i", stream.get_properties().value()); + demo::show_stream_info(stream); + + { + const char data[] = "Hello, iostream!"; + const auto written_bytes = stream.write(data, sizeof data); + SDL_Log("==> Wrote %zu bytes", written_bytes); + } + + demo::show_stream_info(stream); + + { + const auto cursor_pos = stream.seek(cen::seek_mode::start).value(); + SDL_Log("==> Reset stream cursor to %" PRId64, cursor_pos); + } + + demo::show_stream_info(stream); + + { + char read_buffer[64] {}; + const auto read_bytes = stream.read(read_buffer, sizeof read_buffer); + SDL_Log("==> Read %zu bytes", read_bytes); + } + + demo::show_stream_info(stream); + + return 0; +} diff --git a/test/unit/centurion/io/iostream_test.cpp b/test/unit/centurion/io/iostream_test.cpp new file mode 100644 index 000000000..4e990cf89 --- /dev/null +++ b/test/unit/centurion/io/iostream_test.cpp @@ -0,0 +1,428 @@ +// Copyright (C) 2019-2024 Albin Johansson (MIT License) + +#include "centurion/io/iostream.hpp" + +#include +#include + +#include +#include + +namespace cen::test { + +class IOStreamTest : public testing::Test +{ + public: + static void SetUpTestSuite() + { + std::ofstream file {"assets/foobar.txt", std::ios::out | std::ios::trunc}; + file << "foobar"; + } +}; + +// cen::iostream::from_file +// cen::iostream::read +TEST_F(IOStreamTest, FromFile) +{ + auto stream = iostream::from_file("assets/foobar.txt", "r"); + ASSERT_TRUE(stream.has_value()); + + char buffer[6] {}; + EXPECT_EQ(stream->read(buffer, sizeof buffer), 6); + EXPECT_EQ(stream->tell(), 6); + EXPECT_EQ(stream->status(), SDL_IO_STATUS_READY); + + EXPECT_EQ(stream->read(buffer, sizeof buffer), 0); + EXPECT_EQ(stream->tell(), 6); + EXPECT_EQ(stream->status(), SDL_IO_STATUS_EOF); + + EXPECT_EQ(buffer[0], 'f'); + EXPECT_EQ(buffer[1], 'o'); + EXPECT_EQ(buffer[2], 'o'); + EXPECT_EQ(buffer[3], 'b'); + EXPECT_EQ(buffer[4], 'a'); + EXPECT_EQ(buffer[5], 'r'); +} + +// cen::iostream::from_memory +// cen::iostream::write +TEST_F(IOStreamTest, FromMemory) +{ + char backing_buffer[256] = {}; + + auto stream = iostream::from_memory(backing_buffer, sizeof backing_buffer); + ASSERT_TRUE(stream.has_value()); + + const char data[] = "abc"; + EXPECT_EQ(stream->write(data, sizeof data), 4); + EXPECT_EQ(stream->tell(), 4); + + EXPECT_EQ(backing_buffer[0], 'a'); + EXPECT_EQ(backing_buffer[1], 'b'); + EXPECT_EQ(backing_buffer[2], 'c'); + EXPECT_EQ(backing_buffer[3], '\0'); +} + +// cen::iostream::from_const_memory +TEST_F(IOStreamTest, FromConstMemory) +{ + const std::uint8_t data[] = {1, 2, 3, 4, 5, 6}; + + auto stream = iostream::from_const_memory(data, sizeof data); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_u8(0xFF), result::failure); + EXPECT_EQ(stream->size(), 6); + + EXPECT_EQ(stream->read_u8(), 1); + EXPECT_EQ(stream->read_u8(), 2); + EXPECT_EQ(stream->read_u8(), 3); + EXPECT_EQ(stream->read_u8(), 4); + EXPECT_EQ(stream->read_u8(), 5); + EXPECT_EQ(stream->read_u8(), 6); +} + +// cen::iostream::from_dynamic_memory +TEST_F(IOStreamTest, FromDynamicMemory) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + const char original_data[] = {'1', '2', '3'}; + EXPECT_EQ(stream->write(original_data, sizeof original_data), 3); + EXPECT_EQ(stream->tell(), 3); + EXPECT_EQ(stream->status(), SDL_IO_STATUS_READY); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->tell(), 0); + EXPECT_EQ(stream->status(), SDL_IO_STATUS_READY); + + char read_data[3] = {}; + EXPECT_EQ(stream->read(read_data, sizeof read_data), 3); + EXPECT_EQ(stream->tell(), 3); + EXPECT_EQ(stream->status(), SDL_IO_STATUS_READY); + + EXPECT_THAT(read_data, testing::ContainerEq(original_data)); +} + +// cen::iostream_base::seek +// cen::iostream_base::tell +TEST_F(IOStreamTest, Seek) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->tell(), 0); + EXPECT_EQ(stream->write_u8(1), result::success); + EXPECT_EQ(stream->write_u8(2), result::success); + EXPECT_EQ(stream->write_u8(3), result::success); + EXPECT_EQ(stream->tell(), 3); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->tell(), 0); + EXPECT_EQ(stream->read_u8(), 1); + + EXPECT_EQ(stream->seek(seek_mode::start, 1), 1); + EXPECT_EQ(stream->tell(), 1); + EXPECT_EQ(stream->read_u8(), 2); + + EXPECT_EQ(stream->seek(seek_mode::end), 3); + EXPECT_EQ(stream->tell(), 3); + EXPECT_EQ(stream->read_u8(), std::nullopt); + + EXPECT_EQ(stream->seek(seek_mode::end, -1), 2); + EXPECT_EQ(stream->tell(), 2); + EXPECT_EQ(stream->read_u8(), 3); + + EXPECT_EQ(stream->seek(seek_mode::current, -2), 1); + EXPECT_EQ(stream->tell(), 1); + EXPECT_EQ(stream->read_u8(), 2); +} + +// cen::iostream_base::write +TEST_F(IOStreamTest, Write) +{ + auto stream = iostream::from_file("assets/iostream_write.txt", "w+"); + ASSERT_TRUE(stream.has_value()); + + const char data[] = "This is a test of the write function"; + EXPECT_EQ(stream->write(data, sizeof data), sizeof data); + EXPECT_EQ(stream->status(), SDL_IO_STATUS_READY); +} + +// cen::iostream_base::write_u8 +// cen::iostream_base::read_u8 +TEST_F(IOStreamTest, WriteReadU8) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_u8(0xF1), result::success); + EXPECT_EQ(stream->status(), SDL_IO_STATUS_READY); + EXPECT_EQ(stream->size(), 1); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + + EXPECT_EQ(stream->read_u8(), 0xF1); + EXPECT_EQ(stream->status(), SDL_IO_STATUS_READY); + EXPECT_EQ(stream->size(), 1); +} + +// cen::iostream_base::write_u16_le +// cen::iostream_base::read_u16_le +TEST_F(IOStreamTest, WriteReadU16LE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_u16_le(0xDEAD), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u16_le(), 0xDEAD); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0xAD); + EXPECT_EQ(stream->read_u8(), 0xDE); +} + +// cen::iostream_base::write_u16_be +// cen::iostream_base::read_u16_be +TEST_F(IOStreamTest, WriteReadU16BE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_u16_be(0xDEAD), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u16_be(), 0xDEAD); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0xDE); + EXPECT_EQ(stream->read_u8(), 0xAD); +} + +// cen::iostream_base::write_s16_le +// cen::iostream_base::read_s16_le +TEST_F(IOStreamTest, WriteReadS16LE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_s16_le(0x1234), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_s16_le(), 0x1234); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0x34); + EXPECT_EQ(stream->read_u8(), 0x12); +} + +// cen::iostream_base::write_s16_be +// cen::iostream_base::read_s16_be +TEST_F(IOStreamTest, WriteReadS16BE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_s16_be(0x1234), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_s16_be(), 0x1234); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0x12); + EXPECT_EQ(stream->read_u8(), 0x34); +} + +// cen::iostream_base::write_u32_le +// cen::iostream_base::read_u32_le +TEST_F(IOStreamTest, WriteReadU32LE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_u32_le(0xDEADBEEF), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u32_le(), 0xDEADBEEF); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0xEF); + EXPECT_EQ(stream->read_u8(), 0xBE); + EXPECT_EQ(stream->read_u8(), 0xAD); + EXPECT_EQ(stream->read_u8(), 0xDE); +} + +// cen::iostream_base::write_u32_be +// cen::iostream_base::read_u32_be +TEST_F(IOStreamTest, WriteReadU32BE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_u32_be(0xDEADBEEF), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u32_be(), 0xDEADBEEF); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0xDE); + EXPECT_EQ(stream->read_u8(), 0xAD); + EXPECT_EQ(stream->read_u8(), 0xBE); + EXPECT_EQ(stream->read_u8(), 0xEF); +} + +// cen::iostream_base::write_s32_le +// cen::iostream_base::read_s32_le +TEST_F(IOStreamTest, WriteReadS32LE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_s32_le(0x11223344), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_s32_le(), 0x11223344); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0x44); + EXPECT_EQ(stream->read_u8(), 0x33); + EXPECT_EQ(stream->read_u8(), 0x22); + EXPECT_EQ(stream->read_u8(), 0x11); +} + +// cen::iostream_base::write_s32_be +// cen::iostream_base::read_s32_be +TEST_F(IOStreamTest, WriteReadS32BE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_s32_be(0x11223344), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_s32_be(), 0x11223344); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0x11); + EXPECT_EQ(stream->read_u8(), 0x22); + EXPECT_EQ(stream->read_u8(), 0x33); + EXPECT_EQ(stream->read_u8(), 0x44); +} + +// cen::iostream_base::write_u64_le +// cen::iostream_base::read_u64_le +TEST_F(IOStreamTest, WriteReadU64LE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_u64_le(0x1122334455667788), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u64_le(), 0x1122334455667788); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0x88); + EXPECT_EQ(stream->read_u8(), 0x77); + EXPECT_EQ(stream->read_u8(), 0x66); + EXPECT_EQ(stream->read_u8(), 0x55); + EXPECT_EQ(stream->read_u8(), 0x44); + EXPECT_EQ(stream->read_u8(), 0x33); + EXPECT_EQ(stream->read_u8(), 0x22); + EXPECT_EQ(stream->read_u8(), 0x11); +} + +// cen::iostream_base::write_u64_be +// cen::iostream_base::read_u64_be +TEST_F(IOStreamTest, WriteReadU64BE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_u64_be(0x1122334455667788), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u64_be(), 0x1122334455667788); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0x11); + EXPECT_EQ(stream->read_u8(), 0x22); + EXPECT_EQ(stream->read_u8(), 0x33); + EXPECT_EQ(stream->read_u8(), 0x44); + EXPECT_EQ(stream->read_u8(), 0x55); + EXPECT_EQ(stream->read_u8(), 0x66); + EXPECT_EQ(stream->read_u8(), 0x77); + EXPECT_EQ(stream->read_u8(), 0x88); +} + +// cen::iostream_base::write_s64_le +// cen::iostream_base::read_s64_le +TEST_F(IOStreamTest, WriteReadS64LE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_s64_le(0x1122334455667788), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_s64_le(), 0x1122334455667788); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0x88); + EXPECT_EQ(stream->read_u8(), 0x77); + EXPECT_EQ(stream->read_u8(), 0x66); + EXPECT_EQ(stream->read_u8(), 0x55); + EXPECT_EQ(stream->read_u8(), 0x44); + EXPECT_EQ(stream->read_u8(), 0x33); + EXPECT_EQ(stream->read_u8(), 0x22); + EXPECT_EQ(stream->read_u8(), 0x11); +} + +// cen::iostream_base::write_s64_be +// cen::iostream_base::read_s64_be +TEST_F(IOStreamTest, WriteReadS64BE) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + EXPECT_EQ(stream->write_s64_be(0x1122334455667788), result::success); + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_s64_be(), 0x1122334455667788); + + EXPECT_EQ(stream->seek(seek_mode::start), 0); + EXPECT_EQ(stream->read_u8(), 0x11); + EXPECT_EQ(stream->read_u8(), 0x22); + EXPECT_EQ(stream->read_u8(), 0x33); + EXPECT_EQ(stream->read_u8(), 0x44); + EXPECT_EQ(stream->read_u8(), 0x55); + EXPECT_EQ(stream->read_u8(), 0x66); + EXPECT_EQ(stream->read_u8(), 0x77); + EXPECT_EQ(stream->read_u8(), 0x88); +} + +// cen::iostream_base::get_properties +TEST_F(IOStreamTest, GetProperties) +{ + auto stream = iostream::from_dynamic_memory(); + ASSERT_TRUE(stream.has_value()); + + const auto id = stream->get_properties(); + EXPECT_TRUE(id.has_value()); +} + +// cen::load_file(const char*) +TEST_F(IOStreamTest, LoadFile) +{ + const auto content = load_file("assets/foobar.txt"); + ASSERT_TRUE(content.has_value()); + + EXPECT_NE(content->data, nullptr); + EXPECT_EQ(content->size, 6); +} + +// cen::load_file(iostream_ref) +TEST_F(IOStreamTest, LoadFileFromIOStream) +{ + auto stream = iostream::from_file("assets/foobar.txt", "r"); + ASSERT_TRUE(stream.has_value()); + + const auto content = load_file(*stream); + ASSERT_TRUE(content.has_value()); + + EXPECT_NE(content->data, nullptr); + EXPECT_EQ(content->size, 6); +} + +} // namespace cen::test