feat: extend RegionCompact
This commit is contained in:
1 parent
5dd69bfdf9
commit
a7f26fa0ee
1 file changed
+101
@@ -157,3 +157,104 @@ int RegionCompactor::fragmentationPercent(const uint8_t* data, std::size_t size)
|
||||
return static_cast<int>((reclaimable * 100u) / measure.totalSectors);
|
||||
}
|
||||
|
||||
bool RegionCompactor::compact(const ByteBuffer& in, ByteBuffer& out,
|
||||
CompactionStats& stats) const {
|
||||
return compact(in.data(), in.size(), out, stats);
|
||||
}
|
||||
|
||||
bool RegionCompactor::compact(const uint8_t* data, std::size_t size, ByteBuffer& out,
|
||||
CompactionStats& stats) const {
|
||||
stats = CompactionStats{};
|
||||
LayoutMeasure measure = measureLayout(data, size);
|
||||
if (!measure.ok) {
|
||||
stats.failure = "buffer is not a readable save container";
|
||||
return false;
|
||||
}
|
||||
|
||||
SaveScanner scanner;
|
||||
IntegrityReport report = scanner.scan(data, size);
|
||||
if (report.countAtLeast(IssueSeverity::Recoverable) != 0) {
|
||||
stats.failure = "container reports " +
|
||||
std::to_string(report.countAtLeast(IssueSeverity::Recoverable)) +
|
||||
" unresolved integrity findings; repair it before compacting";
|
||||
return false;
|
||||
}
|
||||
|
||||
SaveVersion version = static_cast<SaveVersion>(measure.header.version);
|
||||
uint32_t sectorBytes = measure.header.sectorBytes;
|
||||
|
||||
stats.sectorBytes = sectorBytes;
|
||||
stats.sectorsBefore = measure.totalSectors;
|
||||
stats.headerSectors = measure.headerSectors;
|
||||
stats.referencedSectors = measure.referencedSectors;
|
||||
stats.holeSectors = measure.holeSectors;
|
||||
stats.excessSectors = measure.excessSectors;
|
||||
stats.slackBytes = measure.slackBytes;
|
||||
stats.bytesBefore = size;
|
||||
stats.fragmentationBefore = fragmentationPercent(data, size);
|
||||
|
||||
std::vector<ChunkFragment> fragments = survey(data, size);
|
||||
std::vector<std::size_t> order;
|
||||
for (std::size_t i = 0; i < fragments.size(); ++i) {
|
||||
++stats.operations;
|
||||
if (dropEmptyChunks && fragments[i].empty) {
|
||||
++stats.chunksDropped;
|
||||
++stats.operations;
|
||||
continue;
|
||||
}
|
||||
order.push_back(i);
|
||||
}
|
||||
std::sort(order.begin(), order.end(), [&](std::size_t a, std::size_t b) {
|
||||
return saveCoordLess(fragments[a].coord, fragments[b].coord);
|
||||
});
|
||||
|
||||
std::vector<Vec3i> coords;
|
||||
std::vector<ByteBuffer> payloads;
|
||||
coords.reserve(order.size());
|
||||
payloads.reserve(order.size());
|
||||
bool repack = repackWithStrongerCompression && version == currentSaveVersion;
|
||||
|
||||
for (std::size_t index : order) {
|
||||
const ChunkFragment& fragment = fragments[index];
|
||||
std::size_t at = static_cast<std::size_t>(fragment.sectorOffset) * sectorBytes;
|
||||
ByteBuffer payload(data + at, data + at + fragment.payloadBytes);
|
||||
if (repack) {
|
||||
SaveChunkRecord record;
|
||||
record.coord = fragment.coord;
|
||||
if (!decodeChunkPayload(version, payload.data(), payload.size(), record, nullptr)) {
|
||||
stats.failure = "a payload stopped decoding while repacking";
|
||||
return false;
|
||||
}
|
||||
ByteBuffer repacked = encodeChunkPayload(version, record, true);
|
||||
if (repacked.size() < payload.size()) {
|
||||
payload = std::move(repacked);
|
||||
++stats.chunksRepacked;
|
||||
++stats.operations;
|
||||
}
|
||||
}
|
||||
coords.push_back(fragment.coord);
|
||||
payloads.push_back(std::move(payload));
|
||||
}
|
||||
|
||||
std::vector<uint32_t> newOffsets;
|
||||
out = packSaveContainer(version, measure.header.regionX, measure.header.regionZ, sectorBytes,
|
||||
coords, payloads, &newOffsets);
|
||||
++stats.operations;
|
||||
|
||||
for (std::size_t i = 0; i < order.size(); ++i) {
|
||||
if (fragments[order[i]].sectorOffset != newOffsets[i]) {
|
||||
++stats.chunksMoved;
|
||||
++stats.operations;
|
||||
}
|
||||
}
|
||||
|
||||
stats.chunksKept = coords.size();
|
||||
stats.bytesAfter = out.size();
|
||||
stats.bytesReclaimed = size > out.size() ? size - out.size() : 0;
|
||||
stats.sectorsAfter = static_cast<uint32_t>(divideUp(out.size(), sectorBytes));
|
||||
stats.fragmentationAfter = fragmentationPercent(out.data(), out.size());
|
||||
stats.ok = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace vf
|
||||
Reference in new issue
Block a user