diff --git a/Utilities/File.h b/Utilities/File.h index e85050c0c6..b308b3e915 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace fs { @@ -469,4 +470,103 @@ namespace fs // Error code returned extern thread_local error g_tls_error; + + template + struct container_stream final : file_base + { + // T can be a reference, but this is not recommended + using value_type = typename std::remove_reference_t::value_type; + + T obj; + u64 pos; + + container_stream(T&& obj) + : obj(std::forward(obj)) + , pos(0) + { + } + + ~container_stream() override + { + } + + stat_t stat() override + { + fmt::raw_error("fs::container_stream<>::stat(): not supported"); + } + + bool trunc(u64 length) override + { + obj.resize(length); + return true; + } + + u64 read(void* buffer, u64 size) override + { + const u64 end = obj.size(); + + if (pos < end) + { + // Get readable size + if (const u64 max = std::min(size, end - pos)) + { + std::copy(obj.cbegin() + pos, obj.cbegin() + pos + max, static_cast(buffer)); + pos = pos + max; + return max; + } + } + + return 0; + } + + u64 write(const void* buffer, u64 size) override + { + const u64 old_size = obj.size(); + + if (old_size + size < old_size) + { + fmt::raw_error("fs::container_stream<>::write(): overflow"); + } + + if (pos > old_size) + { + // Fill gap if necessary (default-initialized) + obj.resize(pos); + } + + const auto src = static_cast(buffer); + + // Overwrite existing part + const u64 overlap = std::min(obj.size() - pos, size); + std::copy(src, src + overlap, obj.begin() + pos); + + // Append new data + obj.insert(obj.end(), src + overlap, src + size); + pos += size; + + return size; + } + + u64 seek(s64 offset, seek_mode whence) override + { + return + whence == fs::seek_set ? pos = offset : + whence == fs::seek_cur ? pos = offset + pos : + whence == fs::seek_end ? pos = offset + size() : + (fmt::raw_error("fs::container_stream<>::seek(): invalid whence"), 0); + } + + u64 size() override + { + return obj.size(); + } + }; + + template + file make_stream(T&& container = T{}) + { + file result; + result.reset(std::make_unique>(std::forward(container))); + return result; + } }