diff --git a/source/mijin/io/stream.cpp b/source/mijin/io/stream.cpp index 5f48d5b..e940369 100644 --- a/source/mijin/io/stream.cpp +++ b/source/mijin/io/stream.cpp @@ -63,6 +63,60 @@ StreamError Stream::writeString(std::string_view str) return writeSpan(str.begin(), str.end()); } +StreamError Stream::getTotalLength(std::size_t& outLength) +{ + const StreamFeatures features = getFeatures(); + if (!features.tell || !features.seek) { + return StreamError::NOT_SUPPORTED; + } + const std::size_t origPos = tell(); + if (StreamError error = seek(0, SeekMode::RELATIVE_TO_END); error != StreamError::SUCCESS) + { + return error; + } + outLength = tell(); + if (StreamError error = seek(static_cast(origPos)); error != StreamError::SUCCESS) + { + return error; + } + return StreamError::SUCCESS; +} + +StreamError Stream::readRest(TypelessBuffer& outBuffer) +{ + // first try to allocate everything at once + std::size_t length = 0; + if (StreamError lengthError = getTotalLength(length); lengthError == StreamError::SUCCESS) + { + MIJIN_ASSERT(getFeatures().tell, "How did you find the length if you cannot tell()?"); + length -= tell(); + outBuffer.resize(length); + if (StreamError error = readRaw(outBuffer.data(), length); error != StreamError::SUCCESS) + { + return error; + } + return StreamError::SUCCESS; + } + + // could not determine the size, read chunk-wise + static constexpr std::size_t CHUNK_SIZE = 4096; + std::array chunk = {}; + + while (!isAtEnd()) + { + std::size_t bytesRead = 0; + if (StreamError error = readRaw(chunk, true, &bytesRead); error != StreamError::SUCCESS) + { + return error; + } + + outBuffer.resize(outBuffer.byteSize() + bytesRead); + std::span bufferBytes = outBuffer.makeSpan(); + std::copy_n(chunk.begin(), bytesRead, bufferBytes.end() - static_cast(bytesRead)); + } + return StreamError::SUCCESS; +} + FileStream::~FileStream() { if (handle) { diff --git a/source/mijin/io/stream.hpp b/source/mijin/io/stream.hpp index 294cefa..ba549c2 100644 --- a/source/mijin/io/stream.hpp +++ b/source/mijin/io/stream.hpp @@ -7,8 +7,10 @@ #include #include #include +#include #include #include +#include "../container/typeless_buffer.hpp" namespace mijin { @@ -76,12 +78,26 @@ public: return readRaw(std::span(ptr, ptr + bytes), partial, outBytesRead); } + template + inline StreamError readRaw(TRange& range, bool partial = false, std::size_t* outBytesRead = nullptr) + { + const std::size_t bytes = std::distance(range.begin(), range.end()) * sizeof(std::ranges::range_value_t); + return readRaw(&*range.begin(), bytes, partial, outBytesRead); + } + inline StreamError writeRaw(const void* data, std::size_t bytes) { const std::uint8_t* ptr = static_cast(data); return writeRaw(std::span(ptr, ptr + bytes)); } + template + inline StreamError writeRaw(const TRange& range) + { + const std::size_t bytes = std::distance(range.begin(), range.end()) * sizeof(std::ranges::range_value_t); + return writeRaw(&*range.begin(), bytes); + } + template inline StreamError read(T& value) { @@ -123,6 +139,12 @@ public: StreamError readString(std::string& outString); StreamError writeString(std::string_view str); + + StreamError getTotalLength(std::size_t& outLength); + StreamError readRest(TypelessBuffer& outBuffer); + + template + StreamError readAsString(std::basic_string& outString); }; class FileStream : public Stream @@ -180,6 +202,42 @@ public: // public functions // +template +StreamError Stream::readAsString(std::basic_string& outString) +{ + static_assert(sizeof(TChar) == 1, "Can only read to 8-bit character types (char, unsigned char or char8_t"); + + // first try to allocate everything at once + std::size_t length = 0; + if (StreamError lengthError = getTotalLength(length); lengthError == StreamError::SUCCESS) + { + MIJIN_ASSERT(getFeatures().tell, "How did you find the length if you cannot tell()?"); + length -= tell(); + outString.resize(length); + if (StreamError error = readRaw(outString.data(), length); error != StreamError::SUCCESS) + { + return error; + } + return StreamError::SUCCESS; + } + + // could not determine the size, read chunk-wise + static constexpr std::size_t CHUNK_SIZE = 4096; + std::array chunk; + + outString.clear(); + while (!isAtEnd()) + { + std::size_t bytesRead = 0; + if (StreamError error = readRaw(chunk, true, &bytesRead); error != StreamError::SUCCESS) + { + return error; + } + outString.append(chunk.data(), chunk.data() + bytesRead); + } + return StreamError::SUCCESS; +} + } // namespace mijin #endif // !defined(MIJIN_IO_STREAM_HPP_INCLUDED)