feat: add LiveChunk and insertLive

This commit is contained in:
dualfroz committed 2026-09-10 12:48:42 +02:00
1 parent 08362822f0
commit aa4bd109fe
1 file changed
+156
+156
View File
@@ -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();