2021-02-07 09:22:50 -08:00
|
|
|
#include "compressed_data.h"
|
|
|
|
#include "../thirdparty/lz4/lz4.h"
|
|
|
|
#include "../util/profiling.h"
|
|
|
|
#include "../util/serialization.h"
|
|
|
|
|
|
|
|
#include <core/io/file_access_memory.h>
|
2021-12-13 13:38:10 -08:00
|
|
|
#include <core/variant/variant.h>
|
2021-02-16 13:30:34 -08:00
|
|
|
#include <limits>
|
2021-02-07 09:22:50 -08:00
|
|
|
|
2022-01-02 17:50:58 -08:00
|
|
|
namespace zylann::voxel::CompressedData {
|
2021-02-07 09:22:50 -08:00
|
|
|
|
2021-05-31 08:48:49 -07:00
|
|
|
bool decompress(Span<const uint8_t> src, std::vector<uint8_t> &dst) {
|
2021-02-07 09:22:50 -08:00
|
|
|
VOXEL_PROFILE_SCOPE();
|
|
|
|
|
2021-04-10 09:38:07 -07:00
|
|
|
// TODO Apparently big-endian is dead
|
|
|
|
// I chose it originally to match "network byte order",
|
|
|
|
// but as I read comments about it there seem to be no reason to continue using it. Needs a version increment.
|
2022-01-02 17:50:58 -08:00
|
|
|
MemoryReader f(src, ENDIANESS_BIG_ENDIAN);
|
2021-02-07 09:22:50 -08:00
|
|
|
|
|
|
|
const Compression comp = static_cast<Compression>(f.get_8());
|
|
|
|
ERR_FAIL_INDEX_V(comp, COMPRESSION_COUNT, false);
|
|
|
|
|
|
|
|
switch (comp) {
|
|
|
|
case COMPRESSION_NONE: {
|
|
|
|
// We still have to do a copy. The point of this container is compression,
|
|
|
|
// so we don't worry too much about the performance impact of not using `src` directly.
|
|
|
|
dst.resize(src.size() - 1);
|
|
|
|
memcpy(dst.data(), src.data() + 1, dst.size());
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case COMPRESSION_LZ4: {
|
|
|
|
const uint32_t decompressed_size = f.get_32();
|
|
|
|
const uint32_t header_size = sizeof(uint8_t) + sizeof(uint32_t);
|
|
|
|
|
|
|
|
dst.resize(decompressed_size);
|
|
|
|
|
|
|
|
const uint32_t actually_decompressed_size = LZ4_decompress_safe(
|
2021-12-13 13:38:10 -08:00
|
|
|
(const char *)src.data() + header_size, (char *)dst.data(), src.size() - header_size, dst.size());
|
2021-02-07 09:22:50 -08:00
|
|
|
|
|
|
|
ERR_FAIL_COND_V_MSG(actually_decompressed_size < 0, false,
|
|
|
|
String("LZ4 decompression error {0}").format(varray(actually_decompressed_size)));
|
|
|
|
|
|
|
|
ERR_FAIL_COND_V_MSG(actually_decompressed_size != decompressed_size, false,
|
|
|
|
String("Expected {0} bytes, obtained {1}")
|
|
|
|
.format(varray(decompressed_size, actually_decompressed_size)));
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ERR_PRINT("Invalid compression header");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-05-31 08:48:49 -07:00
|
|
|
bool compress(Span<const uint8_t> src, std::vector<uint8_t> &dst, Compression comp) {
|
2021-02-07 09:22:50 -08:00
|
|
|
VOXEL_PROFILE_SCOPE();
|
|
|
|
|
|
|
|
switch (comp) {
|
|
|
|
case COMPRESSION_NONE: {
|
|
|
|
dst.resize(src.size() + 1);
|
|
|
|
dst[0] = comp;
|
|
|
|
memcpy(dst.data() + 1, src.data(), src.size());
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case COMPRESSION_LZ4: {
|
|
|
|
ERR_FAIL_COND_V(src.size() > std::numeric_limits<uint32_t>::max(), false);
|
|
|
|
|
|
|
|
// Write header
|
2021-02-07 15:31:42 -08:00
|
|
|
// Must clear first because MemoryWriter writes from the end
|
|
|
|
dst.clear();
|
2022-01-02 17:50:58 -08:00
|
|
|
MemoryWriter f(dst, ENDIANESS_BIG_ENDIAN);
|
2021-02-07 09:22:50 -08:00
|
|
|
f.store_8(comp);
|
|
|
|
f.store_32(src.size());
|
|
|
|
|
2021-02-07 15:31:42 -08:00
|
|
|
const uint32_t header_size = sizeof(uint8_t) + sizeof(uint32_t);
|
|
|
|
dst.resize(header_size + LZ4_compressBound(src.size()));
|
|
|
|
|
2021-02-07 09:22:50 -08:00
|
|
|
const uint32_t compressed_size = LZ4_compress_default(
|
2021-12-13 13:38:10 -08:00
|
|
|
(const char *)src.data(), (char *)dst.data() + header_size, src.size(), dst.size() - header_size);
|
2021-02-07 09:22:50 -08:00
|
|
|
|
|
|
|
ERR_FAIL_COND_V(compressed_size < 0, false);
|
|
|
|
ERR_FAIL_COND_V(compressed_size == 0, false);
|
|
|
|
|
|
|
|
dst.resize(header_size + compressed_size);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ERR_PRINT("Invalid compression header");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-01-02 17:50:58 -08:00
|
|
|
} // namespace zylann::voxel::CompressedData
|