feat(common): Sd0::Compress, a default Sd0 and room for chunks that don't shrink

Sd0 gets a default constructor and Sd0::Compress(data), the raw sd0 bytes of
some data (what the client reads for UGC files it downloads without 3D
services). FromData compresses into a heap buffer big enough for a chunk that
grows when deflated (it used a 256 KiB stack buffer, so random data failed and
workers carried a large stack frame), and drops a half-written result.

Tests: chunks as the 1.10.64 client inflates them, incompressible data, empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-27 17:46:34 -05:00
parent 47a4c23a0d
commit 26ecfec7ec
4 changed files with 90 additions and 1 deletions

View File

@@ -98,17 +98,20 @@ void Sd0::FromData(const uint8_t* data, size_t bufferSize) {
if (bufferSize == 0) return;
m_Chunks.clear();
// Room for a chunk that doesn't compress (zlib makes it slightly bigger), on the heap: this runs on worker threads
std::vector<uint8_t> compressedChunk(ZCompression::GetMaxCompressedLength(MAX_UNCOMPRESSED_CHUNK_SIZE));
while (bufferSize > 0) {
const auto numToCopy = std::min(MAX_UNCOMPRESSED_CHUNK_SIZE, bufferSize);
const auto* startOffset = data + originalBufferSize - bufferSize;
bufferSize -= numToCopy;
std::array<uint8_t, MAX_UNCOMPRESSED_CHUNK_SIZE> compressedChunk;
const auto compressedSize = ZCompression::Compress(
startOffset, numToCopy,
compressedChunk.data(), compressedChunk.size());
if (compressedSize == -1) {
LOG("Failed to compress chunk, aborting");
// Part of the data isn't a usable buffer
m_Chunks.clear();
break;
}
@@ -128,6 +131,15 @@ void Sd0::FromData(const uint8_t* data, size_t bufferSize) {
}
std::string Sd0::Compress(std::string_view data) {
Sd0 sd0;
if (data.empty()) return std::string(SD0_HEADER, 5);
sd0.FromData(reinterpret_cast<const uint8_t*>(data.data()), data.size());
std::string out;
for (const auto& chunk : sd0.GetAsVector()) out.append(reinterpret_cast<const char*>(chunk.data()), chunk.size());
return out;
}
std::string Sd0::GetAsStringUncompressed() const {
std::string toReturn;
bool first = true;

View File

@@ -5,6 +5,8 @@
#define SD0_H
#include <fstream>
#include <string>
#include <string_view>
#include <vector>
// Sd0 is comprised of multiple zlib compressed buffers stored in a row.
@@ -21,9 +23,16 @@ public:
*/
static constexpr inline size_t MAX_UNCOMPRESSED_CHUNK_SIZE = 1024 * 256;
// Empty, for FromData
Sd0() = default;
// Read the input buffer into an internal chunk stream to be used later
Sd0(std::istream& buffer);
// `data` compressed into an sd0 buffer, returned in its raw compressed form (what the client reads, e.g. the UGC
// files it downloads without 3D services); only the header when `data` is empty, empty when it can't be compressed
[[nodiscard]] static std::string Compress(std::string_view data);
// Uncompresses the entire Sd0 buffer and returns it as a string
[[nodiscard]] std::string GetAsStringUncompressed() const;