This commit is contained in:
2025-08-25 17:13:47 +06:00
parent c06683cd57
commit 36346c9798
5 changed files with 249 additions and 338 deletions

View File

@@ -156,7 +156,7 @@ std::pair<std::string, size_t> CacheDatabase::getAllHash() {
return {out, readed};
}
void CacheDatabase::updateTimeFor(HASH hash) {
void CacheDatabase::updateTimeFor(Hash_t hash) {
sqlite3_bind_blob(STMT_UPDATE_TIME, 1, (const void*) hash.data(), 32, SQLITE_STATIC);
sqlite3_bind_int(STMT_UPDATE_TIME, 2, time(nullptr));
if(sqlite3_step(STMT_UPDATE_TIME) != SQLITE_DONE) {
@@ -167,7 +167,7 @@ void CacheDatabase::updateTimeFor(HASH hash) {
sqlite3_reset(STMT_UPDATE_TIME);
}
void CacheDatabase::insert(HASH hash, size_t size) {
void CacheDatabase::insert(Hash_t hash, size_t size) {
assert(size < (size_t(1) << 31)-1 && size > 0);
sqlite3_bind_blob(STMT_INSERT, 1, (const void*) hash.data(), 32, SQLITE_STATIC);
@@ -181,8 +181,8 @@ void CacheDatabase::insert(HASH hash, size_t size) {
sqlite3_reset(STMT_INSERT);
}
std::vector<CacheDatabase::HASH> CacheDatabase::findExcessHashes(size_t bytesToFree, int timeBefore = time(nullptr)-604800) {
std::vector<HASH> out;
std::vector<Hash_t> CacheDatabase::findExcessHashes(size_t bytesToFree, int timeBefore = time(nullptr)-604800) {
std::vector<Hash_t> out;
size_t removed = 0;
sqlite3_bind_int(STMT_OLD, 1, timeBefore);
@@ -197,7 +197,7 @@ std::vector<CacheDatabase::HASH> CacheDatabase::findExcessHashes(size_t bytesToF
const uint8_t *hash = (const uint8_t*) sqlite3_column_blob(STMT_OLD, 0);
removed += sqlite3_column_int(STMT_OLD, 1);
HASH obj;
Hash_t obj;
for(int iter = 0; iter < 32; iter++)
obj[iter] = hash[iter];
@@ -221,7 +221,7 @@ std::vector<CacheDatabase::HASH> CacheDatabase::findExcessHashes(size_t bytesToF
}
const uint8_t *hash = (const uint8_t*) sqlite3_column_blob(STMT_TO_FREE, 0);
HASH obj;
Hash_t obj;
for(int iter = 0; iter < 32; iter++)
obj[iter] = hash[iter];
@@ -232,7 +232,7 @@ std::vector<CacheDatabase::HASH> CacheDatabase::findExcessHashes(size_t bytesToF
return out;
}
void CacheDatabase::remove(HASH hash) {
void CacheDatabase::remove(Hash_t hash) {
sqlite3_bind_blob(STMT_REMOVE, 1, (const void*) hash.data(), 32, SQLITE_STATIC);
if(sqlite3_step(STMT_REMOVE) != SQLITE_DONE) {
sqlite3_reset(STMT_REMOVE);
@@ -242,7 +242,7 @@ void CacheDatabase::remove(HASH hash) {
sqlite3_reset(STMT_REMOVE);
}
std::string CacheDatabase::hashToString(HASH hash) {
std::string CacheDatabase::hashToString(Hash_t hash) {
std::string text;
text.reserve(64);
@@ -263,125 +263,41 @@ std::string CacheDatabase::hashToString(HASH hash) {
return text;
}
int CacheDatabase::hexCharToInt(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
throw std::invalid_argument("Invalid hexadecimal character");
// int CacheDatabase::hexCharToInt(char c) {
// if (c >= '0' && c <= '9') return c - '0';
// if (c >= 'a' && c <= 'f') return c - 'a' + 10;
// throw std::invalid_argument("Invalid hexadecimal character");
// }
// Hash_t CacheDatabase::stringToHash(const std::string_view view) {
// if (view.size() != 64)
// throw std::invalid_argument("Hex string must be exactly 64 characters long");
// Hash_t hash;
// for (size_t i = 0; i < 32; ++i) {
// size_t offset = 62 - i * 2;
// int high = hexCharToInt(view[offset]);
// int low = hexCharToInt(view[offset + 1]);
// hash[i] = (high << 4) | low;
// }
// return hash;
// }
coro<> ResourceHandler::asyncDestructor() {
assert(NeedShutdown); // Нормальный shutdown должен быть вызван
co_await IAsyncDestructible::asyncDestructor();
}
CacheDatabase::HASH CacheDatabase::stringToHash(const std::string_view view) {
if (view.size() != 64)
throw std::invalid_argument("Hex string must be exactly 64 characters long");
HASH hash;
for (size_t i = 0; i < 32; ++i) {
size_t offset = 62 - i * 2;
int high = hexCharToInt(view[offset]);
int low = hexCharToInt(view[offset + 1]);
hash[i] = (high << 4) | low;
}
return hash;
}
CacheHandler::CacheHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
size_t maxCacheDirectorySize, size_t maxLifeTime)
: IAsyncDestructible(ioc), Path(cachePath), DB(Path),
MaxCacheDirectorySize(maxCacheDirectorySize), MaxLifeTime(maxLifeTime)
{
}
CacheHandler::~CacheHandler() = default;
std::pair<std::string, size_t> CacheHandler::getAll() {
return DB.getAllHash();
}
size_t CacheHandler::getCacheSize() {
return DB.getCacheSize();
}
coro<> CacheHandlerBasic::asyncDestructor() {
NeedShutdown = true;
co_await CacheHandler::asyncDestructor();
}
void CacheHandlerBasic::readThread(AsyncUseControl::Lock lock) {
LOG.info() << "Поток чтения запущен";
while(!NeedShutdown) {
if(ReadQueue.get_read().empty())
goto wait;
else {
auto lock = ReadQueue.lock();
if(lock->empty())
goto wait;
CacheDatabase::HASH hash = lock->front();
lock->pop();
lock.unlock();
std::string name = CacheDatabase::hashToString(hash);
fs::path path = Path / name.substr(0, 2) / name.substr(2, 2) / name.substr(4);
std::shared_ptr<std::string> data;
{
auto lock_wc = WriteCache.lock();
auto iter = lock_wc->begin();
while(iter != lock_wc->end()) {
if(iter->first == hash) {
// Копируем
data = std::make_shared<std::string>(*iter->second);
break;
}
}
}
if(!data) {
data = std::make_shared<std::string>();
try {
std::ifstream fd(path, std::ios::binary | std::ios::ate);
if (!fd.is_open())
MAKE_ERROR("!is_open(): " << fd.exceptions());
if (fd.fail())
MAKE_ERROR("fail(): " << fd.exceptions());
std::ifstream::pos_type size = fd.tellg();
fd.seekg(0, std::ios::beg);
data->resize(size);
fd.read(data->data(), size);
if (!fd.good())
MAKE_ERROR("!good(): " << fd.exceptions());
} catch(const std::exception &exc) {
LOG.error() << "Не удалось считать ресурс " << path.c_str() << ": " << exc.what();
}
}
ReadedQueue.lock()->emplace_back(hash, std::move(data));
continue;
}
wait:
TOS::Time::sleep3(20);
}
LOG.info() << "Поток чтения остановлен";
lock.unlock();
}
void CacheHandlerBasic::readWriteThread(AsyncUseControl::Lock lock) {
void ResourceHandler::readWriteThread(AsyncUseControl::Lock lock) {
LOG.info() << "Поток чтения/записи запущен";
while(!NeedShutdown || !WriteQueue.get_read().empty()) {
if(!ReadQueue.get_read().empty()) {
auto lock = ReadQueue.lock();
if(!lock->empty()) {
CacheDatabase::HASH hash = lock->front();
Hash_t hash = lock->front();
lock->pop();
lock.unlock();
@@ -448,11 +364,11 @@ void CacheHandlerBasic::readWriteThread(AsyncUseControl::Lock lock) {
if(ssize_t free = ssize_t(MaxCacheDirectorySize)-DB.getCacheSize(); free < task.Data->size()) {
// Недостаточно места, сколько необходимо освободить с запасом
ssize_t need = task.Data->size()-free + 64*1024*1024;
std::vector<CacheDatabase::HASH> hashes = DB.findExcessHashes(need, time(nullptr)-MaxLifeTime);
std::vector<Hash_t> hashes = DB.findExcessHashes(need, time(nullptr)-MaxLifeTime);
LOG.warn() << "Удаление устаревшего кеша в количестве " << hashes.size() << "...";
for(CacheDatabase::HASH hash : hashes) {
for(Hash_t hash : hashes) {
std::string name = CacheDatabase::hashToString(hash);
fs::path path = Path / name.substr(0, 2) / name.substr(2, 2) / name.substr(4);
DB.remove(hash);
@@ -496,80 +412,46 @@ void CacheHandlerBasic::readWriteThread(AsyncUseControl::Lock lock) {
lock.unlock();
}
CacheHandlerBasic::CacheHandlerBasic(boost::asio::io_context &ioc, const fs::path &cachePath,
ResourceHandler::ResourceHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
size_t maxCacheDirectorySize, size_t maxLifeTime)
: CacheHandler(ioc, cachePath, maxCacheDirectorySize, maxLifeTime),
ReadThread(&CacheHandlerBasic::readThread, this, AUC.use()),
ReadWriteThread(&CacheHandlerBasic::readWriteThread, this, AUC.use())
: IAsyncDestructible(ioc),
OffThread(&ResourceHandler::readWriteThread, this, AUC.use())
{
LOG.info() << "Инициализировано хранилище кеша: " << cachePath.c_str();
}
CacheHandlerBasic::~CacheHandlerBasic() {
ReadThread.join();
ReadWriteThread.join();
LOG.info() << "ДеИнициализировано хранилище кеша: " << Path.c_str();
}
void CacheHandlerBasic::pushWrite(std::string &&data, CacheDatabase::HASH hash) {
std::shared_ptr<std::string> dat = std::make_shared<std::string>(std::move(data));
WriteCache.lock()->push_back({hash, dat});
WriteQueue.lock()->push({hash, dat});
}
void CacheHandlerBasic::pushRead(CacheDatabase::HASH hash) {
ReadQueue.lock()->push(hash);
}
std::vector<std::pair<CacheDatabase::HASH, std::string>> CacheHandlerBasic::pullReads() {
std::vector<DataTask> data;
{
auto lock = ReadedQueue.lock();
data = std::move(*lock);
}
std::vector<std::pair<CacheDatabase::HASH, std::string>> out;
out.reserve(data.size());
for(auto &value : data) {
out.emplace_back(value.Hash, std::move(*value.Data));
}
return out;
}
void CacheHandlerBasic::updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) {
MaxLifeTime = maxLifeTime;
// void ResourceHandler::updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) {
// MaxLifeTime = maxLifeTime;
if(MaxCacheDirectorySize != maxCacheDirectorySize) {
MaxCacheDirectorySize = maxCacheDirectorySize;
// if(MaxCacheDirectorySize != maxCacheDirectorySize) {
// MaxCacheDirectorySize = maxCacheDirectorySize;
size_t size = DB.getCacheSize();
if(size > maxCacheDirectorySize) {
size_t needToFree = size-maxCacheDirectorySize+64*1024*1024;
try {
LOG.info() << "Начата вычистка кеша на сумму " << needToFree/1024/1024 << " Мб";
std::vector<CacheDatabase::HASH> hashes = DB.findExcessHashes(needToFree, time(nullptr)-MaxLifeTime);
LOG.warn() << "Удаление кеша в количестве " << hashes.size() << "...";
// size_t size = DB.getCacheSize();
// if(size > maxCacheDirectorySize) {
// size_t needToFree = size-maxCacheDirectorySize+64*1024*1024;
// try {
// LOG.info() << "Начата вычистка кеша на сумму " << needToFree/1024/1024 << " Мб";
// std::vector<Hash_t> hashes = DB.findExcessHashes(needToFree, time(nullptr)-MaxLifeTime);
// LOG.warn() << "Удаление кеша в количестве " << hashes.size() << "...";
for(CacheDatabase::HASH hash : hashes) {
std::string name = CacheDatabase::hashToString(hash);
fs::path path = Path / name.substr(0, 2) / name.substr(2, 2) / name.substr(4);
DB.remove(hash);
fs::remove(path);
// for(Hash_t hash : hashes) {
// std::string name = CacheDatabase::hashToString(hash);
// fs::path path = Path / name.substr(0, 2) / name.substr(2, 2) / name.substr(4);
// DB.remove(hash);
// fs::remove(path);
fs::path up1 = path.parent_path();
LOG.info() << "В директории " << up1.c_str() << " не осталось файлов, удаляем...";
size_t count = std::distance(fs::directory_iterator(up1), fs::directory_iterator());
if(count == 0)
fs::remove(up1);
}
} catch(const std::exception &exc) {
LOG.error() << "Не удалось очистить кеш до новой границы: " << exc.what();
}
}
}
}
// fs::path up1 = path.parent_path();
// LOG.info() << "В директории " << up1.c_str() << " не осталось файлов, удаляем...";
// size_t count = std::distance(fs::directory_iterator(up1), fs::directory_iterator());
// if(count == 0)
// fs::remove(up1);
// }
// } catch(const std::exception &exc) {
// LOG.error() << "Не удалось очистить кеш до новой границы: " << exc.what();
// }
// }
// }
// }
}

View File

@@ -1,7 +1,11 @@
#pragma once
#include "Common/Abstract.hpp"
#include <array>
#include <boost/lockfree/spsc_queue.hpp>
#include <cassert>
#include <chrono>
#include <functional>
#include <memory>
#include <queue>
#include <string>
@@ -10,6 +14,8 @@
#include <TOSAsync.hpp>
#include <filesystem>
#include <string_view>
#include <thread>
#include <unordered_map>
namespace LV::Client {
@@ -38,7 +44,7 @@ public:
CacheDatabase(const CacheDatabase&) = delete;
CacheDatabase(CacheDatabase&&) = delete;
CacheDatabase& operator=(const CacheDatabase&) = delete;
CacheDatabase& operator=(CacheDatabase&&) = delete;
CacheDatabase& operator=(CacheDatabase&&) = delete;
/*
Выдаёт размер занимаемый всем хранимым кешем
@@ -50,134 +56,141 @@ public:
/*
Создаёт линейный массив в котором подряд указаны все хэш суммы в бинарном виде и возвращает их количество
*/
std::pair<std::string, size_t> getAllHash();
using HASH = std::array<uint8_t, 32>;
// std::pair<std::string, size_t> getAllHash();
/*
Обновляет время использования кеша
*/
void updateTimeFor(HASH hash);
void updateTimeFor(Hash_t hash);
/*
Добавляет запись
*/
void insert(HASH hash, size_t size);
void insert(Hash_t hash, size_t size);
/*
Выдаёт хэши на удаление по размеру в сумме больше bytesToFree.
Сначала удаляется старьё, потом по приоритету дата использования + размер
*/
std::vector<HASH> findExcessHashes(size_t bytesToFree, int timeBefore);
std::vector<Hash_t> findExcessHashes(size_t bytesToFree, int timeBefore);
/*
Удаление записи
*/
void remove(HASH hash);
void remove(Hash_t hash);
static std::string hashToString(HASH hash);
static std::string hashToString(Hash_t hash);
static int hexCharToInt(char c);
static HASH stringToHash(const std::string_view view);
static Hash_t stringToHash(const std::string_view view);
};
/*
Читает и пишет ресурсы на диск
В приоритете чтение
Менеджер предоставления ресурсов. Управляет ресурс паками
и хранением кешированных ресурсов сервера.
Интерфейс однопоточный.
Кодировки только на стороне сервера, на клиенте уже готовые данные
NOT ThreadSafe
Обработка файлов в отдельном потоке
*/
class CacheHandler : public IAsyncDestructible {
protected:
const fs::path Path;
CacheDatabase DB;
size_t MaxCacheDirectorySize;
size_t MaxLifeTime;
class ResourceHandler : public IAsyncDestructible {
public:
using Ptr = std::shared_ptr<CacheHandler>;
using Ptr = std::shared_ptr<ResourceHandler>;
protected:
CacheHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
size_t maxCacheDirectorySize, size_t maxLifeTime);
public:
virtual ~CacheHandler();
// Добавить задачу на запись
virtual void pushWrite(std::string &&data, CacheDatabase::HASH hash) = 0;
// Добавить задачу на чтение
virtual void pushRead(CacheDatabase::HASH hash) = 0;
// Получить считанные данные
virtual std::vector<std::pair<CacheDatabase::HASH, std::string>> pullReads() = 0;
// Получить список доступных ресурсов
std::pair<std::string, size_t> getAll();
// Размер всего хранимого кеша
size_t getCacheSize();
// Обновить параметры хранилища
virtual void updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) = 0;
};
class CacheHandlerBasic : public CacheHandler {
Logger LOG = "CacheHandlerBasic";
struct DataTask {
CacheDatabase::HASH Hash;
std::shared_ptr<std::string> Data;
struct ResourceKey {
Hash_t Hash;
EnumAssets Type;
std::string Domain, Key;
};
// Очередь задач на чтение
SpinlockObject<std::queue<CacheDatabase::HASH>> ReadQueue;
// Кэш данных, которые ещё не записались
SpinlockObject<std::vector<std::pair<CacheDatabase::HASH, std::shared_ptr<std::string>>>> WriteCache;
// Очередь записи данных на диск
SpinlockObject<std::queue<DataTask>> WriteQueue;
// Список полностью считанных файлов
SpinlockObject<std::vector<DataTask>> ReadedQueue;
bool NeedShutdown = false;
std::thread ReadThread, ReadWriteThread;
public:
using Ptr = std::shared_ptr<CacheHandlerBasic>;
private:
virtual coro<> asyncDestructor() override;
void readThread(AsyncUseControl::Lock lock);
void readWriteThread(AsyncUseControl::Lock lock);
protected:
CacheHandlerBasic(boost::asio::io_context &ioc, const fs::path& cachePath,
size_t maxCacheDirectorySize, size_t maxLifeTime);
ResourceHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
size_t maxCacheDatabaseSize, size_t maxLifeTime);
public:
virtual ~CacheHandlerBasic();
static std::shared_ptr<CacheHandlerBasic> Create(asio::io_context &ioc, const fs::path& cachePath,
virtual ~ResourceHandler();
static std::shared_ptr<ResourceHandler> Create(asio::io_context &ioc, const fs::path& cachePath,
size_t maxCacheDirectorySize = 8*1024*1024*1024ULL, size_t maxLifeTime = 7*24*60*60) {
return createShared(ioc, new CacheHandlerBasic(ioc, cachePath, maxCacheDirectorySize, maxLifeTime));
return createShared(ioc, new ResourceHandler(ioc, cachePath, maxCacheDirectorySize, maxLifeTime));
}
virtual void pushWrite(std::string &&data, CacheDatabase::HASH hash) override;
virtual void pushRead(CacheDatabase::HASH hash) override;
virtual std::vector<std::pair<CacheDatabase::HASH, std::string>> pullReads() override;
virtual void updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) override;
// Добавить новый полученный с сервера ресурс
void pushResources(std::vector<Resource> resources) {
WriteQueue.lock()->push_range(resources);
}
// Добавить задачи на чтение
void pushReads(std::vector<ResourceKey> keys) {
ReadQueue.lock()->push_range(keys);
}
// Получить считанные данные
std::vector<std::pair<Hash_t, std::optional<Resource>>> pullReads() {
return std::move(*ReadyQueue.lock());
}
// Размер всего хранимого кеша
size_t getCacheSize() {
return DatabaseSize;
}
// Обновить параметры хранилища кеша
void updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) {
auto lock = Changes.lock();
lock->MaxLifeTime = maxLifeTime;
lock->MaxCacheDatabaseSize = maxCacheDirectorySize;
lock->MaxChange = true;
}
// Установка путей до папок assets
void setResourcePacks(std::vector<fs::path> packsAssets) {
auto lock = Changes.lock();
lock->Assets = std::move(packsAssets);
lock->AssetsChange = true;
}
// Запуск процедуры проверки хешей всего хранимого кеша
void runFullDatabaseRecheck(std::move_only_function<void(std::string result)>&& func) {
auto lock = Changes.lock();
lock->OnRecheckEnd = std::move(func);
lock->FullRecheck = true;
}
// Уведомление о завершении работы
void prepareShutdown() {
NeedShutdown = true;
}
// После этого вызова уже нельзя будет обращатся ко внешним ресурсам
void shutdown() {
OffThread.join();
}
private:
Logger LOG = "Client>ResourceHandler";
const fs::path CachePath;
volatile size_t DatabaseSize = 0;
// Очередь задач на чтение
TOS::SpinlockObject<std::queue<ResourceKey>> ReadQueue;
// Очередь на запись ресурсов
TOS::SpinlockObject<std::queue<Resource>> WriteQueue;
// Очередь на выдачу результатов чтения
TOS::SpinlockObject<std::vector<std::pair<Hash_t, std::optional<Resource>>>> ReadyQueue;
struct Changes_t {
std::vector<fs::path> Assets;
volatile bool AssetsChange = false;
size_t MaxCacheDatabaseSize, MaxLifeTime;
volatile bool MaxChange = false;
std::optional<std::move_only_function<void(std::string)>> OnRecheckEnd;
volatile bool FullRecheck = false;
};
TOS::SpinlockObject<Changes_t> Changes;
bool NeedShutdown = false;
std::thread OffThread;
void readWriteThread(AsyncUseControl::Lock lock);
};
#ifdef LUAVOX_HAVE_LIBURING
class CacheHandlerUring : public CacheHandler {
};
#endif
}

View File

@@ -1,7 +1,10 @@
#include "Abstract.hpp"
#include "Common/Net.hpp"
#include "TOSLib.hpp"
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include "boost/json.hpp"
#include "sha2.hpp"
#include <algorithm>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
@@ -16,6 +19,8 @@
namespace LV {
namespace fs = std::filesystem;
CompressedVoxels compressVoxels_byte(const std::vector<VoxelCube>& voxels) {
std::u8string compressed;
@@ -1970,4 +1975,49 @@ std::u8string PreparedModel::dump() const {
return result.complite();
}
struct Resource::InlineMMap {
boost::interprocess::file_mapping MMap;
boost::interprocess::mapped_region Region;
Hash_t Hash;
InlineMMap(fs::path path)
: MMap(path.c_str(), boost::interprocess::read_only),
Region(MMap, boost::interprocess::read_only)
{
Hash = sha2::sha256((const uint8_t*) Region.get_address(), Region.get_size());
}
const std::byte* data() const { return (const std::byte*) Region.get_address(); }
size_t size() const { return Region.get_size(); }
};
struct Resource::InlinePtr {
std::vector<uint8_t> Data;
Hash_t Hash;
InlinePtr(const uint8_t* data, size_t size) {
Data.resize(size);
std::copy(data, data+size, Data.data());
Hash = sha2::sha256(data, size);
}
const std::byte* data() const { return (const std::byte*) Data.data(); }
size_t size() const { return Data.size(); }
};
Resource::Resource(fs::path path)
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlineMMap(path)))
{}
Resource::Resource(const uint8_t* data, size_t size)
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlinePtr(data, size)))
{}
const std::byte* Resource::data() const { assert(In); return std::visit<const std::byte*>([](auto& obj){ return obj.data(); }, *In); }
size_t Resource::size() const { assert(In); return std::visit<size_t>([](auto& obj){ return obj.size(); }, *In); }
Hash_t Resource::hash() const { assert(In); return std::visit<Hash_t>([](auto& obj){ return obj.Hash; }, *In); }
auto Resource::operator<=>(const Resource&) const = default;
}

View File

@@ -691,6 +691,29 @@ inline std::pair<std::string, std::string> parseDomainKey(const std::string& val
}
}
struct Resource {
private:
struct InlineMMap;
struct InlinePtr;
std::shared_ptr<std::variant<InlineMMap, InlinePtr>> In;
public:
Resource(std::filesystem::path path);
Resource(const uint8_t* data, size_t size);
Resource(const Resource&) = default;
Resource(Resource&&) = default;
Resource& operator=(const Resource&) = default;
Resource& operator=(Resource&&) = default;
auto operator<=>(const Resource&) const;
const std::byte* data() const;
size_t size() const;
Hash_t hash() const;
};
}

View File

@@ -3,11 +3,8 @@
#include "Common/Abstract.hpp"
#include "TOSLib.hpp"
#include "Common/Net.hpp"
#include "assets.hpp"
#include "sha2.hpp"
#include <bitset>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <filesystem>
#include <optional>
#include <unordered_map>
@@ -18,60 +15,6 @@ namespace LV::Server {
namespace fs = std::filesystem;
struct Resource {
private:
struct InlineMMap {
boost::interprocess::file_mapping MMap;
boost::interprocess::mapped_region Region;
Hash_t Hash;
InlineMMap(fs::path path)
: MMap(path.c_str(), boost::interprocess::read_only),
Region(MMap, boost::interprocess::read_only)
{
Hash = sha2::sha256((const uint8_t*) Region.get_address(), Region.get_size());
}
const std::byte* data() const { return (const std::byte*) Region.get_address(); }
size_t size() const { return Region.get_size(); }
};
struct InlinePtr {
std::vector<uint8_t> Data;
Hash_t Hash;
InlinePtr(const uint8_t* data, size_t size) {
Data.resize(size);
std::copy(data, data+size, Data.data());
Hash = sha2::sha256(data, size);
}
const std::byte* data() const { return (const std::byte*) Data.data(); }
size_t size() const { return Data.size(); }
};
std::shared_ptr<std::variant<InlineMMap, InlinePtr>> In;
public:
Resource(fs::path path)
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlineMMap(path)))
{}
Resource(const uint8_t* data, size_t size)
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlinePtr(data, size)))
{}
Resource(const Resource&) = default;
Resource(Resource&&) = default;
Resource& operator=(const Resource&) = default;
Resource& operator=(Resource&&) = default;
bool operator<=>(const Resource&) const = default;
const std::byte* data() const { return std::visit<const std::byte*>([](auto& obj){ return obj.data(); }, *In); }
size_t size() const { return std::visit<size_t>([](auto& obj){ return obj.size(); }, *In); }
Hash_t hash() const { return std::visit<Hash_t>([](auto& obj){ return obj.Hash; }, *In); }
};
/*
Используется для расчёта коллизии,
если это необходимо.