refactor: rewrite streamed_io.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user