refactor: rewrite streamed_io.

This commit is contained in:
2025-08-18 18:35:05 +08:00
parent 989ed8e60c
commit fc42ade509
6 changed files with 124 additions and 29 deletions

View File

@@ -39,15 +39,15 @@ namespace di::data_format::_pl::v1_12_0 {
void MagicBlobImpl::read(const fs::path& path) {
StreamedIO::read(path);
m_stored_seed = eat<uint64_t>();
m_stored_seed = get<uint64_t>();
m_query_seed = hash_qseed(m_stored_seed);
rva_t n_rva{};
while (next() != EOF) {
auto flags = eat_varint<uint64_t>();
auto rva = eat_varint<rva_t>();
auto hash = eat<hash_t>();
while (!is_eof()) {
auto flags = get_varint<uint64_t>();
auto rva = get_varint<rva_t>();
auto hash = get<hash_t>();
// What is stored in the original format is not the RVA itself, but the
// difference with the previous entry (in MagicBlob, RVA is sorted from

View File

@@ -6,8 +6,7 @@ namespace di::data_format::_pl::v1_12_0 {
class MagicBlobImpl : public MagicBlob {
public:
using for_each_callback_t =
std::function<void(hash_t, shared_entry_t const&)>;
MagicBlobImpl() = default;
void read(const fs::path& path) override;

View File

@@ -3,13 +3,39 @@
namespace di::io {
void StreamedIO::read(const fs::path& path) {
m_file_stream.open(path, std::ios::binary);
if (!m_file_stream) throw UnableToOpenException(path);
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file) {
throw UnableToOpenException(path);
}
auto size = static_cast<size_t>(file.tellg());
file.seekg(0, std::ios::beg);
m_buffer.resize(size);
if (!file.read(m_buffer.data(), size)) {
throw IOException(
"Failed to read {} bytes from {}.",
size,
path.string()
);
}
m_position = 0; // reset
}
void StreamedIO::write(const fs::path& path) const {
std::fstream ofs(path, std::ios::binary);
ofs << m_file_stream.rdbuf();
std::ofstream file(path, std::ios::binary | std::ios::trunc);
if (!file) {
throw UnableToOpenException(path);
}
if (!file.write(m_buffer.data(), m_buffer.size())) {
throw IOException(
"Failed to write {} bytes to {}.",
m_buffer.size(),
path.string()
);
}
}
} // namespace di::io

View File

@@ -6,38 +6,103 @@ namespace di::io {
class StreamedIO : public io::IOBase {
public:
explicit StreamedIO(std::string data) : m_buffer(std::move(data)) {}
explicit StreamedIO(const fs::path& path) { read(path); }
StreamedIO() = default;
void read(const fs::path& path) override;
void write(const fs::path& path) const override;
constexpr auto next() { return m_file_stream.peek(); }
// Avoid confusion with the function that opens a file, so use eat
// instead of read. Maybe come up with another name for write as well.
//
// TODO: write<T>() method.
template <typename T>
constexpr T eat() {
template <TriviallyCopyable T>
constexpr T get() {
if (m_position + sizeof(T) > m_buffer.size()) {
throw IOException("Read attempt is out of buffer bounds.");
}
T value;
m_file_stream.read((char*)&value, sizeof(T));
return value;
// To avoid violating the strict aliasing rule, so we use memcpy.
std::memcpy(&value, m_buffer.data() + m_position, sizeof(T));
m_position += sizeof(T);
if constexpr (std::endian::native != std::endian::little) {
return std::byteswap(value);
} else {
return value;
}
}
template <std::unsigned_integral T>
constexpr T eat_varint() {
T res = 0;
int shift = 0;
constexpr T get_varint() {
T res = 0;
int shift = 0;
const int max_shift = sizeof(T) * 8;
while (true) {
auto byte = m_file_stream.get();
res |= static_cast<T>(byte & 0x7F) << shift;
if ((byte & 0x80) == 0) break;
if (m_position >= m_buffer.size()) {
throw IOException("Incomplete VarInt at end of buffer.");
}
if (shift >= max_shift) {
throw IOException("VarInt is too long for the requested type.");
}
auto byte = static_cast<std::byte>(m_buffer[m_position++]);
res |= static_cast<T>(byte & std::byte{0x7F}) << shift;
if ((byte & std::byte{0x80}) == std::byte{0}) {
break;
}
shift += 7;
}
return res;
}
template <TriviallyCopyable T>
constexpr void put(T value) {
if constexpr (std::endian::native != std::endian::little) {
value = std::byteswap(value);
}
auto data = reinterpret_cast<const char*>(&value);
if (m_position + sizeof(T) > m_buffer.size()) {
m_buffer.append(data, sizeof(T));
} else {
std::memcpy(m_buffer.data() + m_position, data, sizeof(T));
}
m_position += sizeof(T);
}
template <std::unsigned_integral T>
constexpr void put_varint(T value) {
while (value >= 0x80) {
std::byte to_write = static_cast<std::byte>((value & 0x7F) | 0x80);
put(static_cast<char>(to_write));
value >>= 7;
}
put(static_cast<char>(value));
}
constexpr void seek(size_t pos) {
if (pos > m_buffer.size()) {
throw IOException("Seek position is out of buffer bounds.");
}
m_position = pos;
}
size_t size() const { return m_buffer.size(); }
bool is_eof() const { return m_position >= m_buffer.size(); }
constexpr size_t position() const { return m_position; }
std::string const& data() const { return m_buffer; }
private:
std::ifstream m_file_stream;
std::string m_buffer;
size_t m_position;
};
} // namespace di::io

View File

@@ -13,6 +13,9 @@ constexpr auto underlying_value(T v) {
return static_cast<std::underlying_type_t<decltype(v)>>(v);
}
template <typename T>
concept TriviallyCopyable = std::is_trivially_copyable_v<T>;
#if __cpp_constexpr >= 202211L // complete c++23 constexpr
#define DI_CONSTEXPR constexpr
#else

View File

@@ -16,6 +16,8 @@ using namespace llvm;
#include <functional>
#include <memory>
#include <bit>
#include <cstring>
#include <string>
#include <string_view>