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);
|
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