diff --git a/.gitignore b/.gitignore index 6f31401..2928dd2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build/ .vscode/ +.meson-subproject-wrap-hash.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 5779a57..6b01b6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,6 +155,7 @@ set(SOURCE_FILE_HEADERS hash_map.h reg_util.h hash_lib.h + stream.h ) if (NOT HAVE_STRPTIME) diff --git a/meson.build b/meson.build index e472270..a842318 100644 --- a/meson.build +++ b/meson.build @@ -157,6 +157,7 @@ source_file_headers = files([ 'hash_map.h', 'reg_util.h', 'hash_lib.h', + 'stream.h', ]) if conf.get('HAVE_STRPTIME') == 0 diff --git a/stream.h b/stream.h new file mode 100644 index 0000000..64d02aa --- /dev/null +++ b/stream.h @@ -0,0 +1,298 @@ +#ifndef _UTILS_STREAM_H +#define _UTILS_STREAM_H +#include +#include +#include "fileop.h" +#include +#include "cstr_util.h" + +class ReadStream { +public: + virtual ~ReadStream() {} + virtual size_t read(uint8_t* buf, size_t size) = 0; + virtual bool seek(int64_t offset, int whence) { + return false; + } + virtual int64_t tell() { + return -1; + } + virtual bool seekable() = 0; + virtual bool eof() = 0; + virtual bool error() = 0; + virtual bool close() = 0; + bool readall(const uint8_t* buf, size_t size) { + size_t total_readed = 0; + while (total_readed < size) { + size_t r = read((uint8_t*)buf + total_readed, size - total_readed); + if (r == 0) break; + total_readed += r; + } + return total_readed == size && !error(); + } + bool readall(std::vector& buf) { + return readall(buf.data(), buf.size()); + } + template + bool readall(uint8_t(&data)[T]) { + return readall(data, T); + } + bool readu8(uint8_t& value) { + uint8_t buf[1]; + if (!readall(buf)) return false; + value = buf[0]; + return true; + } + bool readu16(uint16_t& value) { + uint8_t buf[2]; + if (!readall(buf)) return false; + value = cstr_read_uint16(buf, 0); + return true; + } + bool readu32(uint32_t& value) { + uint8_t buf[4]; + if (!readall(buf)) return false; + value = cstr_read_uint32(buf, 0); + return true; + } + bool readu64(uint64_t& value) { + uint8_t buf[8]; + if (!readall(buf)) return false; + value = cstr_read_uint64(buf, 0); + return true; + } + bool skip(uint64_t bytes) { + if (seekable()) { + if (!seek(bytes, SEEK_CUR)) { + return false; + } + } else { + uint64_t skipped = 0; + uint8_t buffer[4096]; + while (skipped < bytes) { + size_t to_read = (bytes - skipped) < sizeof(buffer) ? (bytes - skipped) : sizeof(buffer); + size_t r = read(buffer, to_read); + if (r == 0) break; + skipped += r; + } + } + return !error(); + } +}; + +class FileReadStream : public ReadStream { +public: + virtual ~FileReadStream() { + if (fp) { + fileop::fclose(fp); + fp = nullptr; + } + } + /** + * @brief Open a file for reading + * @param filename File name (on Windows, UTF-8 encoding is supported) + */ + FileReadStream(const char* filename) { + fp = fileop::fopen(filename, "rb"); + if (!fp) { + errored = true; + } + } + virtual size_t read(uint8_t* buf, size_t size) { + if (!fp) return 0; + size_t readed = fread(buf, 1, size, fp); + if (readed != size && ferror(fp)) { + errored = true; + } + return readed; + } + virtual bool seek(int64_t offset, int whence) { + if (!fp) return false; + if (fileop::fseek(fp, offset, whence) != 0) { + return false; + } + return true; + } + virtual int64_t tell() { + if (!fp) return -1; + return fileop::ftell(fp); + } + virtual bool seekable() { + return fp != nullptr; + } + virtual bool eof() { + if (!fp) return true; + return feof(fp) != 0; + } + virtual bool error() { + return errored; + } + virtual bool close() { + if (!fp) return true; + bool res = fileop::fclose(fp); + fp = nullptr; + return res; + } +private: + FILE* fp = nullptr; + bool errored = false; +}; + +class MemReadStream : public ReadStream { +public: + MemReadStream(const std::vector& data) : data(data), pos(0) {} + + MemReadStream(std::vector&& data) : data(std::move(data)), pos(0) {} + + virtual size_t read(uint8_t* buf, size_t size) override { + if (pos >= data.size()) return 0; + + size_t remaining = data.size() - pos; + size_t to_read = remaining < size ? remaining : size; + + memcpy(buf, data.data() + pos, to_read); + pos += to_read; + + return to_read; + } + + virtual bool seek(int64_t offset, int whence) override { + int64_t new_pos; + switch (whence) { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos = pos + offset; + break; + case SEEK_END: + new_pos = data.size() + offset; + break; + default: + return false; + } + + if (new_pos < 0 || new_pos > (int64_t)data.size()) { + return false; + } + + pos = (size_t)new_pos; + return true; + } + + virtual int64_t tell() override { + return (int64_t)pos; + } + + virtual bool seekable() override { + return true; + } + + virtual bool eof() override { + return pos >= data.size(); + } + + virtual bool error() override { + return false; + } + + virtual bool close() override { + return true; + } + +private: + const std::vector data; + size_t pos = 0; +}; + +class ReadStreamRegion : public ReadStream { +public: + ReadStreamRegion(ReadStream* source, int64_t start, int64_t end) + : source(source), start_pos(start), end_pos(end), current_pos(0) { + if (!source || !source->seekable()) { + errored = true; + } + } + + virtual size_t read(uint8_t* buf, size_t size) override { + if (errored || !source) return 0; + + // 计算实际可读取的大小 + int64_t remaining = end_pos - start_pos - current_pos; + if (remaining <= 0) return 0; + + 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); + current_pos += readed; + + if (source->error()) { + errored = true; + } + + return readed; + } + + virtual bool seek(int64_t offset, int whence) override { + if (!source) return false; + + int64_t new_pos; + switch (whence) { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos = current_pos + offset; + break; + case SEEK_END: + new_pos = (end_pos - start_pos) + offset; + break; + default: + return false; + } + + // 限制在有效范围内 + if (new_pos < 0 || new_pos > end_pos - start_pos) { + return false; + } + + current_pos = new_pos; + return true; + } + + virtual int64_t tell() override { + return current_pos; + } + + virtual bool seekable() override { + return source && source->seekable(); + } + + virtual bool eof() override { + if (!source) return true; + return current_pos >= (end_pos - start_pos); + } + + virtual bool error() override { + return errored || (source && source->error()); + } + + virtual bool close() override { + // 不关闭源流,因为它可能被其他地方使用 + return true; + } + +private: + ReadStream* source; + int64_t start_pos; + int64_t end_pos; + int64_t current_pos; + bool errored = false; +}; +#endif