Add read_at support

This commit is contained in:
2026-01-01 15:19:36 +08:00
parent 22588fee9d
commit 12a4b29061

View File

@@ -6,6 +6,7 @@
#include <vector> #include <vector>
#include <string.h> #include <string.h>
#include "cstr_util.h" #include "cstr_util.h"
#include <mutex>
#if _WIN32 #if _WIN32
#include <fcntl.h> #include <fcntl.h>
#ifndef _SH_DENYWR #ifndef _SH_DENYWR
@@ -33,6 +34,15 @@ public:
virtual bool eof() = 0; virtual bool eof() = 0;
virtual bool error() = 0; virtual bool error() = 0;
virtual bool close() = 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) { bool readall(const uint8_t* buf, size_t size) {
size_t total_readed = 0; size_t total_readed = 0;
while (total_readed < size) { while (total_readed < size) {
@@ -159,9 +169,26 @@ public:
fp = nullptr; fp = nullptr;
return res; 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<std::mutex> 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: private:
FILE* fp = nullptr; FILE* fp = nullptr;
bool errored = false; bool errored = false;
std::mutex io_mutex;
}; };
class MemReadStream : public ReadStream { class MemReadStream : public ReadStream {
@@ -182,6 +209,17 @@ public:
return to_read; 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 { virtual bool seek(int64_t offset, int whence) override {
int64_t new_pos; int64_t new_pos;
switch (whence) { switch (whence) {
@@ -249,14 +287,8 @@ public:
size_t to_read = (size_t)remaining < size ? (size_t)remaining : size; size_t to_read = (size_t)remaining < size ? (size_t)remaining : size;
// Seek 到当前应该读取的位置 // Use read_at on the source to avoid modifying its global position.
if (!source->seek(start_pos + current_pos, SEEK_SET)) { size_t readed = source->read_at(buf, to_read, start_pos + current_pos);
errored = true;
return 0;
}
// 从源流读取数据
size_t readed = source->read(buf, to_read);
current_pos += readed; current_pos += readed;
if (source->error()) { if (source->error()) {
@@ -266,6 +298,20 @@ public:
return readed; 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 { virtual bool seek(int64_t offset, int whence) override {
if (!source) return false; if (!source) return false;