feat: add LiveChunk and insertLive
This commit is contained in:
1 parent
08362822f0
commit
aa4bd109fe
1 file changed
+156
@@ -0,0 +1,156 @@
|
||||
#include "vf/WorldBackup.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace vf {
|
||||
|
||||
namespace {
|
||||
|
||||
struct LiveChunk {
|
||||
Vec3i coord;
|
||||
uint32_t checksum = 0;
|
||||
ByteBuffer payload;
|
||||
};
|
||||
|
||||
std::size_t findLive(const std::vector<LiveChunk>& live, const Vec3i& coord) {
|
||||
for (std::size_t i = 0; i < live.size(); ++i)
|
||||
if (live[i].coord == coord) return i;
|
||||
return live.size();
|
||||
}
|
||||
|
||||
void insertLive(std::vector<LiveChunk>& live, const Vec3i& coord, uint32_t checksum,
|
||||
const ByteBuffer& payload) {
|
||||
std::size_t at = findLive(live, coord);
|
||||
if (at != live.size()) {
|
||||
live[at].checksum = checksum;
|
||||
live[at].payload = payload;
|
||||
return;
|
||||
}
|
||||
LiveChunk fresh;
|
||||
fresh.coord = coord;
|
||||
fresh.checksum = checksum;
|
||||
fresh.payload = payload;
|
||||
live.push_back(std::move(fresh));
|
||||
}
|
||||
|
||||
void eraseLive(std::vector<LiveChunk>& live, const Vec3i& coord) {
|
||||
std::size_t at = findLive(live, coord);
|
||||
if (at != live.size()) live.erase(live.begin() + static_cast<std::ptrdiff_t>(at));
|
||||
}
|
||||
|
||||
// Pulls every chunk of a container out as (coordinate, checksum, payload
|
||||
// bytes), in canonical order.
|
||||
bool sliceContainer(const ByteBuffer& save, SaveHeader& header, std::vector<LiveChunk>& out) {
|
||||
if (!readSaveHeader(save.data(), save.size(), header)) return false;
|
||||
if (header.magic != saveMagic || !isKnownSaveVersion(header.version)) return false;
|
||||
if (header.sectorBytes == 0) return false;
|
||||
std::vector<SaveTableEntry> entries;
|
||||
if (!readSaveTable(save.data(), save.size(), header, entries)) return false;
|
||||
|
||||
out.clear();
|
||||
out.reserve(entries.size());
|
||||
for (const SaveTableEntry& entry : entries) {
|
||||
std::size_t at = static_cast<std::size_t>(entry.sectorOffset) * header.sectorBytes;
|
||||
if (at + entry.payloadBytes > save.size()) return false;
|
||||
LiveChunk chunk;
|
||||
chunk.coord = entry.coord;
|
||||
chunk.payload.assign(save.begin() + static_cast<std::ptrdiff_t>(at),
|
||||
save.begin() + static_cast<std::ptrdiff_t>(at + entry.payloadBytes));
|
||||
chunk.checksum = fnv1a32(chunk.payload.data(), chunk.payload.size());
|
||||
out.push_back(std::move(chunk));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string idText(uint32_t id) { return "#" + std::to_string(id); }
|
||||
|
||||
} // namespace
|
||||
|
||||
const char* backupKindName(BackupKind kind) {
|
||||
switch (kind) {
|
||||
case BackupKind::Full: return "full";
|
||||
case BackupKind::Incremental: return "incremental";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
std::size_t BackupEntry::storedBytes() const {
|
||||
std::size_t total = 0;
|
||||
for (const BackupChunk& chunk : stored) total += chunk.payload.size();
|
||||
return total;
|
||||
}
|
||||
|
||||
const BackupChunk* BackupEntry::findStored(const Vec3i& coord) const {
|
||||
for (const BackupChunk& chunk : stored)
|
||||
if (chunk.coord == coord) return &chunk;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const BackupChunk* BackupEntry::findState(const Vec3i& coord) const {
|
||||
for (const BackupChunk& chunk : state)
|
||||
if (chunk.coord == coord) return &chunk;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const BackupEntry* BackupIndex::find(uint32_t id) const {
|
||||
for (const BackupEntry& entry : entries)
|
||||
if (entry.id == id) return &entry;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BackupEntry* BackupIndex::find(uint32_t id) {
|
||||
for (BackupEntry& entry : entries)
|
||||
if (entry.id == id) return &entry;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t BackupIndex::latestId() const {
|
||||
uint32_t best = 0;
|
||||
for (const BackupEntry& entry : entries)
|
||||
if (entry.id > best) best = entry.id;
|
||||
return best;
|
||||
}
|
||||
|
||||
std::size_t BackupIndex::storedBytes() const {
|
||||
std::size_t total = 0;
|
||||
for (const BackupEntry& entry : entries) total += entry.storedBytes();
|
||||
return total;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> BackupIndex::chainTo(uint32_t id, bool& complete,
|
||||
std::string& failure) const {
|
||||
std::vector<uint32_t> walk;
|
||||
complete = false;
|
||||
failure.clear();
|
||||
|
||||
const BackupEntry* entry = find(id);
|
||||
if (entry == nullptr) {
|
||||
failure = "backup " + idText(id) + " is not in the index";
|
||||
return walk;
|
||||
}
|
||||
|
||||
std::size_t guard = entries.size() + 1;
|
||||
while (guard-- > 0) {
|
||||
walk.push_back(entry->id);
|
||||
if (entry->kind == BackupKind::Full) {
|
||||
std::reverse(walk.begin(), walk.end());
|
||||
complete = true;
|
||||
return walk;
|
||||
}
|
||||
const BackupEntry* parent = find(entry->parentId);
|
||||
if (parent == nullptr) {
|
||||
failure = "chain broken: incremental " + idText(entry->id) + " expects parent " +
|
||||
idText(entry->parentId) + ", which is not in the index";
|
||||
walk.clear();
|
||||
return walk;
|
||||
}
|
||||
entry = parent;
|
||||
}
|
||||
failure = "chain broken: the parent links from " + idText(id) + " never reach a full backup";
|
||||
walk.clear();
|
||||
return walk;
|
||||
}
|
||||
|
||||
uint32_t BackupStore::capture(BackupKind kind, const ByteBuffer& save, uint64_t tick) {
|
||||
failure.clear();
|
||||
|
||||
Reference in new issue
Block a user