mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-04-02 05:27:41 -05:00
feat: Added remote SSH file provider
This commit is contained in:
2
lib/external/libwolv
vendored
2
lib/external/libwolv
vendored
Submodule lib/external/libwolv updated: a8f68e7222...b7e3530aea
@@ -45,6 +45,7 @@ set(LIBIMHEX_SOURCES
|
||||
source/test/tests.cpp
|
||||
|
||||
source/providers/provider.cpp
|
||||
source/providers/cached_provider.cpp
|
||||
source/providers/memory_provider.cpp
|
||||
source/providers/undo/stack.cpp
|
||||
|
||||
|
||||
59
lib/libimhex/include/hex/providers/cached_provider.hpp
Normal file
59
lib/libimhex/include/hex/providers/cached_provider.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
/**
|
||||
* @brief A base class for providers that want to cache data in memory.
|
||||
* Thread-safe for concurrent reads/writes. Reads are cached in memory.
|
||||
* Subclasses must implement readFromSource and writeToSource.
|
||||
*/
|
||||
class CachedProvider : public Provider {
|
||||
public:
|
||||
CachedProvider(size_t cacheBlockSize = 4096, size_t maxBlocks = 1024);
|
||||
~CachedProvider() override;
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
|
||||
void readRaw(u64 offset, void *buffer, size_t size) override;
|
||||
void writeRaw(u64 offset, const void *buffer, size_t size) override;
|
||||
void resizeRaw(u64 newSize) override;
|
||||
|
||||
u64 getActualSize() const override;
|
||||
|
||||
protected:
|
||||
virtual void readFromSource(uint64_t offset, void* buffer, size_t size) = 0;
|
||||
virtual void writeToSource(uint64_t offset, const void* buffer, size_t size) = 0;
|
||||
virtual void resizeSource(uint64_t newSize) { std::ignore = newSize; }
|
||||
virtual u64 getSourceSize() const = 0;
|
||||
|
||||
void clearCache();
|
||||
|
||||
struct Block {
|
||||
uint64_t index;
|
||||
std::vector<uint8_t> data;
|
||||
bool dirty = false;
|
||||
};
|
||||
|
||||
size_t m_cacheBlockSize;
|
||||
size_t m_maxBlocks;
|
||||
mutable std::shared_mutex m_cacheMutex;
|
||||
std::vector<std::optional<Block>> m_cache;
|
||||
mutable u64 m_cachedSize = 0;
|
||||
|
||||
constexpr u64 calcBlockIndex(u64 offset) const { return offset / m_cacheBlockSize; }
|
||||
constexpr size_t calcBlockOffset(u64 offset) const { return offset % m_cacheBlockSize; }
|
||||
|
||||
void evictIfNeeded();
|
||||
};
|
||||
|
||||
}
|
||||
130
lib/libimhex/source/providers/cached_provider.cpp
Normal file
130
lib/libimhex/source/providers/cached_provider.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include "hex/providers/cached_provider.hpp"
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
CachedProvider::CachedProvider(size_t cacheBlockSize, size_t maxBlocks)
|
||||
: m_cacheBlockSize(cacheBlockSize), m_maxBlocks(maxBlocks), m_cache(maxBlocks) {}
|
||||
|
||||
CachedProvider::~CachedProvider() {
|
||||
clearCache();
|
||||
}
|
||||
|
||||
bool CachedProvider::open() {
|
||||
clearCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CachedProvider::close() {
|
||||
clearCache();
|
||||
}
|
||||
|
||||
void CachedProvider::readRaw(u64 offset, void* buffer, size_t size) {
|
||||
if (!isAvailable() || !isReadable())
|
||||
return;
|
||||
|
||||
auto out = static_cast<u8 *>(buffer);
|
||||
while (size > 0) {
|
||||
const auto blockIndex = calcBlockIndex(offset);
|
||||
const auto blockOffset = calcBlockOffset(offset);
|
||||
const auto toRead = std::min(m_cacheBlockSize - blockOffset, size);
|
||||
const auto cacheSlot = blockIndex % m_maxBlocks;
|
||||
|
||||
{
|
||||
std::shared_lock lock(m_cacheMutex);
|
||||
const auto &slot = m_cache[cacheSlot];
|
||||
if (slot && slot->index == blockIndex) {
|
||||
std::copy_n(slot->data.begin() + blockOffset, toRead, out);
|
||||
|
||||
out += toRead;
|
||||
offset += toRead;
|
||||
size -= toRead;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> blockData(m_cacheBlockSize);
|
||||
readFromSource(blockIndex * m_cacheBlockSize, blockData.data(), m_cacheBlockSize);
|
||||
|
||||
{
|
||||
std::unique_lock lock(m_cacheMutex);
|
||||
m_cache[cacheSlot] = Block{blockIndex, std::move(blockData), false};
|
||||
std::copy_n(m_cache[cacheSlot]->data.begin() + blockOffset, toRead, out);
|
||||
}
|
||||
|
||||
out += toRead;
|
||||
offset += toRead;
|
||||
size -= toRead;
|
||||
}
|
||||
}
|
||||
|
||||
void CachedProvider::writeRaw(u64 offset, const void* buffer, size_t size) {
|
||||
if (!isAvailable() || !isWritable())
|
||||
return;
|
||||
|
||||
auto in = static_cast<const u8 *>(buffer);
|
||||
while (size > 0) {
|
||||
const auto blockIndex = calcBlockIndex(offset);
|
||||
const auto blockOffset = calcBlockOffset(offset);
|
||||
const auto toWrite = std::min(m_cacheBlockSize - blockOffset, size);
|
||||
const auto cacheSlot = blockIndex % m_maxBlocks;
|
||||
|
||||
{
|
||||
std::unique_lock lock(m_cacheMutex);
|
||||
auto& slot = m_cache[cacheSlot];
|
||||
if (!slot || slot->index != blockIndex) {
|
||||
std::vector<uint8_t> blockData(m_cacheBlockSize);
|
||||
readFromSource(blockIndex * m_cacheBlockSize, blockData.data(), m_cacheBlockSize);
|
||||
slot = Block { blockIndex, std::move(blockData), false };
|
||||
}
|
||||
|
||||
std::copy_n(in, toWrite, slot->data.begin() + blockOffset);
|
||||
slot->dirty = true;
|
||||
}
|
||||
|
||||
writeToSource(offset, in, toWrite);
|
||||
|
||||
in += toWrite;
|
||||
offset += toWrite;
|
||||
size -= toWrite;
|
||||
}
|
||||
}
|
||||
|
||||
void CachedProvider::resizeRaw(u64 newSize) {
|
||||
clearCache();
|
||||
|
||||
resizeSource(newSize);
|
||||
}
|
||||
|
||||
|
||||
u64 CachedProvider::getActualSize() const {
|
||||
if (!isAvailable())
|
||||
return 0;
|
||||
|
||||
if (m_cachedSize == 0) {
|
||||
std::unique_lock lock(m_cacheMutex);
|
||||
m_cachedSize = getSourceSize();
|
||||
}
|
||||
|
||||
return m_cachedSize;
|
||||
}
|
||||
|
||||
|
||||
void CachedProvider::clearCache() {
|
||||
std::unique_lock lock(m_cacheMutex);
|
||||
|
||||
for (auto& slot : m_cache)
|
||||
slot.reset();
|
||||
|
||||
m_cachedSize = 0;
|
||||
}
|
||||
|
||||
void CachedProvider::evictIfNeeded() {
|
||||
if (m_cache.size() < m_maxBlocks)
|
||||
return;
|
||||
|
||||
m_cache.erase(m_cache.begin());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user