From 12a4b29061cc8d9763149b46a62d0da2ed59cd72 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 1 Jan 2026 15:19:36 +0800 Subject: [PATCH] Add read_at support --- stream.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/stream.h b/stream.h index 622b180..bda6c33 100644 --- a/stream.h +++ b/stream.h @@ -6,6 +6,7 @@ #include #include #include "cstr_util.h" +#include #if _WIN32 #include #ifndef _SH_DENYWR @@ -33,6 +34,15 @@ public: virtual bool eof() = 0; virtual bool error() = 0; virtual bool close() = 0; + + // Read at absolute offset. Default implementation seeks, reads and returns. + // Offset is absolute in the underlying stream coordinate. + virtual size_t read_at(uint8_t* buf, size_t size, int64_t offset) { + if (!seekable()) return 0; + if (!seek(offset, SEEK_SET)) return 0; + return read(buf, size); + } + bool readall(const uint8_t* buf, size_t size) { size_t total_readed = 0; while (total_readed < size) { @@ -159,9 +169,26 @@ public: fp = nullptr; return res; } + + // Read at absolute offset without modifying stream position for other users. + virtual size_t read_at(uint8_t* buf, size_t size, int64_t offset) override { + if (!fp) return 0; + std::lock_guard guard(io_mutex); + if (fileop::fseek(fp, offset, SEEK_SET) != 0) { + errored = true; + return 0; + } + size_t readed = fread(buf, 1, size, fp); + if (readed != size && ferror(fp)) { + errored = true; + } + return readed; + } + private: FILE* fp = nullptr; bool errored = false; + std::mutex io_mutex; }; class MemReadStream : public ReadStream { @@ -182,6 +209,17 @@ public: return to_read; } + // Read at absolute offset within the memory buffer. + virtual size_t read_at(uint8_t* buf, size_t size, int64_t offset) override { + if (offset < 0) return 0; + size_t uoffset = (size_t)offset; + if (uoffset >= data.size()) return 0; + size_t remaining = data.size() - uoffset; + size_t to_read = remaining < size ? remaining : size; + memcpy(buf, data.data() + uoffset, to_read); + return to_read; + } + virtual bool seek(int64_t offset, int whence) override { int64_t new_pos; switch (whence) { @@ -249,14 +287,8 @@ public: size_t to_read = (size_t)remaining < size ? (size_t)remaining : size; - // Seek 到当前应该读取的位置 - if (!source->seek(start_pos + current_pos, SEEK_SET)) { - errored = true; - return 0; - } - - // 从源流读取数据 - size_t readed = source->read(buf, to_read); + // Use read_at on the source to avoid modifying its global position. + size_t readed = source->read_at(buf, to_read, start_pos + current_pos); current_pos += readed; if (source->error()) { @@ -266,6 +298,20 @@ public: return readed; } + // Attempt to read at absolute offset relative to this region by delegating to source->read_at. + virtual size_t read_at(uint8_t* buf, size_t size, int64_t offset) override { + if (errored || !source) return 0; + if (offset < 0) return 0; + int64_t rel = offset; + if (rel < 0 || rel >= (end_pos - start_pos)) return 0; + int64_t remaining = end_pos - start_pos - rel; + if (remaining <= 0) return 0; + size_t to_read = (size_t)remaining < size ? (size_t)remaining : size; + size_t readed = source->read_at(buf, to_read, start_pos + rel); + if (source->error()) errored = true; + return readed; + } + virtual bool seek(int64_t offset, int whence) override { if (!source) return false;