1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-19 13:03:11 +01:00

Improve the performance of sawyer chunk decoding (#22449)

This commit is contained in:
Matt
2024-08-01 23:39:17 +03:00
committed by GitHub
parent 65bf7753a1
commit da6fb7872a
4 changed files with 91 additions and 96 deletions

View File

@@ -12,9 +12,8 @@
#include "../core/Memory.hpp"
#include "SawyerChunkReader.h"
SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, std::unique_ptr<uint8_t[]> data, size_t length)
SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, std::vector<uint8_t>&& data)
: _data(std::move(data))
, _encoding(encoding)
{
_encoding = encoding;
_data = std::move(data);
_length = length;
}

View File

@@ -10,6 +10,7 @@
#pragma once
#include <memory>
#include <vector>
/**
* The type of encoding / compression for a sawyer encoded chunk.
@@ -28,23 +29,22 @@ enum class SAWYER_ENCODING : uint8_t
class SawyerChunk final
{
private:
std::unique_ptr<uint8_t[]> _data;
size_t _length = 0;
std::vector<uint8_t> _data;
SAWYER_ENCODING _encoding = SAWYER_ENCODING::NONE;
public:
const void* GetData() const
{
return _data.get();
return _data.data();
}
size_t GetLength() const
{
return _length;
return _data.size();
}
SAWYER_ENCODING GetEncoding() const
{
return _encoding;
}
SawyerChunk(SAWYER_ENCODING encoding, std::unique_ptr<uint8_t[]> data, size_t length);
SawyerChunk(SAWYER_ENCODING encoding, std::vector<uint8_t>&& data);
};

View File

@@ -76,15 +76,12 @@ std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunk()
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE);
}
auto buffer = std::make_unique<uint8_t[]>(MAX_UNCOMPRESSED_CHUNK_SIZE);
size_t uncompressedLength = DecodeChunk(
buffer.get(), MAX_UNCOMPRESSED_CHUNK_SIZE, compressedData.get(), header);
if (uncompressedLength == 0)
auto buffer = DecodeChunk(compressedData.get(), header);
if (buffer.empty())
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
return std::make_shared<SawyerChunk>(
static_cast<SAWYER_ENCODING>(header.encoding), std::move(buffer), uncompressedLength);
return std::make_shared<SawyerChunk>(static_cast<SAWYER_ENCODING>(header.encoding), std::move(buffer));
}
default:
throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING);
@@ -117,14 +114,13 @@ std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunkTrack()
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE);
}
auto buffer = std::make_unique<uint8_t[]>(MAX_UNCOMPRESSED_CHUNK_SIZE);
SawyerCodingChunkHeader header{ CHUNK_ENCODING_RLE, compressedDataLength };
size_t uncompressedLength = DecodeChunk(buffer.get(), MAX_UNCOMPRESSED_CHUNK_SIZE, compressedData.get(), header);
if (uncompressedLength == 0)
auto buffer = DecodeChunk(compressedData.get(), header);
if (buffer.empty())
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
return std::make_shared<SawyerChunk>(SAWYER_ENCODING::RLE, std::move(buffer), uncompressedLength);
return std::make_shared<SawyerChunk>(SAWYER_ENCODING::RLE, std::move(buffer));
}
catch (const std::exception&)
{
@@ -155,47 +151,42 @@ void SawyerChunkReader::ReadChunk(void* dst, size_t length)
}
}
size_t SawyerChunkReader::DecodeChunk(void* dst, size_t dstCapacity, const void* src, const SawyerCodingChunkHeader& header)
std::vector<uint8_t> SawyerChunkReader::DecodeChunk(const void* src, const SawyerCodingChunkHeader& header)
{
size_t resultLength;
std::vector<uint8_t> buf;
switch (header.encoding)
{
case CHUNK_ENCODING_NONE:
if (header.length > dstCapacity)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
std::memcpy(dst, src, header.length);
resultLength = header.length;
buf.resize(header.length);
std::memcpy(buf.data(), src, header.length);
break;
case CHUNK_ENCODING_RLE:
resultLength = DecodeChunkRLE(dst, dstCapacity, src, header.length);
buf = DecodeChunkRLE(src, header.length);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
resultLength = DecodeChunkRLERepeat(dst, dstCapacity, src, header.length);
buf = DecodeChunkRLERepeat(src, header.length);
break;
case CHUNK_ENCODING_ROTATE:
resultLength = DecodeChunkRotate(dst, dstCapacity, src, header.length);
buf = DecodeChunkRotate(src, header.length);
break;
default:
throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING);
}
return resultLength;
return buf;
}
size_t SawyerChunkReader::DecodeChunkRLERepeat(void* dst, size_t dstCapacity, const void* src, size_t srcLength)
std::vector<uint8_t> SawyerChunkReader::DecodeChunkRLERepeat(const void* src, size_t srcLength)
{
auto immBuffer = std::make_unique<uint8_t[]>(MAX_UNCOMPRESSED_CHUNK_SIZE);
auto immLength = DecodeChunkRLE(immBuffer.get(), MAX_UNCOMPRESSED_CHUNK_SIZE, src, srcLength);
auto size = DecodeChunkRepeat(dst, dstCapacity, immBuffer.get(), immLength);
return size;
auto tempBuf = DecodeChunkRLE(src, srcLength);
return DecodeChunkRepeat(tempBuf.data(), tempBuf.size());
}
size_t SawyerChunkReader::DecodeChunkRLE(void* dst, size_t dstCapacity, const void* src, size_t srcLength)
std::vector<uint8_t> SawyerChunkReader::DecodeChunkRLE(const void* src, size_t srcLength)
{
std::vector<uint8_t> buf;
buf.reserve(srcLength);
auto src8 = static_cast<const uint8_t*>(src);
auto dst8 = static_cast<uint8_t*>(dst);
auto dstEnd = dst8 + dstCapacity;
for (size_t i = 0; i < srcLength; i++)
{
uint8_t rleCodeByte = src8[i];
@@ -208,88 +199,91 @@ size_t SawyerChunkReader::DecodeChunkRLE(void* dst, size_t dstCapacity, const vo
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (dst8 + count > dstEnd)
if (buf.size() + count > MAX_UNCOMPRESSED_CHUNK_SIZE)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
std::fill_n(dst8, count, src8[i]);
dst8 += count;
buf.insert(buf.end(), count, src8[i]);
}
else
{
const auto len = rleCodeByte + 1;
if (i + 1 >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (buf.size() + len > MAX_UNCOMPRESSED_CHUNK_SIZE)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
if (i + 1 + len > srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
const auto* pos = src8 + i + 1;
buf.insert(buf.end(), pos, pos + len);
i += len;
}
}
return buf;
}
std::vector<uint8_t> SawyerChunkReader::DecodeChunkRepeat(const void* src, size_t srcLength)
{
std::vector<uint8_t> buf;
buf.reserve(srcLength);
auto src8 = static_cast<const uint8_t*>(src);
for (size_t i = 0; i < srcLength; i++)
{
if (src8[i] == 0xFF)
{
if (i + 1 >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (dst8 + rleCodeByte + 1 > dstEnd)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
if (i + 1 + rleCodeByte + 1 > srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
std::memcpy(dst8, src8 + i + 1, rleCodeByte + 1);
dst8 += rleCodeByte + 1;
i += rleCodeByte + 1;
}
}
return reinterpret_cast<uintptr_t>(dst8) - reinterpret_cast<uintptr_t>(dst);
}
size_t SawyerChunkReader::DecodeChunkRepeat(void* dst, size_t dstCapacity, const void* src, size_t srcLength)
{
auto src8 = static_cast<const uint8_t*>(src);
auto dst8 = static_cast<uint8_t*>(dst);
auto dstEnd = dst8 + dstCapacity;
for (size_t i = 0; i < srcLength; i++)
{
if (src8[i] == 0xFF)
{
*dst8++ = src8[++i];
buf.push_back(src8[++i]);
}
else
{
size_t count = (src8[i] & 7) + 1;
const uint8_t* copySrc = dst8 + static_cast<int32_t>(src8[i] >> 3) - 32;
int32_t offset = static_cast<int32_t>(src8[i] >> 3) - 32;
const uint8_t* copySrc = buf.data() + (buf.size() + offset);
if (dst8 + count >= dstEnd || copySrc + count >= dstEnd)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
if (copySrc < dst)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if ((copySrc < (dst8 + count) && copySrc >= dst8)
|| ((copySrc + count) <= (dst8 + count) && (copySrc + count) > dst8))
if (copySrc < buf.data() || copySrc + count > buf.data() + buf.size())
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
std::memcpy(dst8, copySrc, count);
dst8 += count;
// We need a temporary buffer as the vector might invalidate the pointer.
uint8_t temp[16];
std::memcpy(temp, copySrc, count);
buf.insert(buf.end(), std::begin(temp), std::begin(temp) + count);
}
}
return reinterpret_cast<uintptr_t>(dst8) - reinterpret_cast<uintptr_t>(dst);
return buf;
}
size_t SawyerChunkReader::DecodeChunkRotate(void* dst, size_t dstCapacity, const void* src, size_t srcLength)
std::vector<uint8_t> SawyerChunkReader::DecodeChunkRotate(const void* src, size_t srcLength)
{
if (srcLength > dstCapacity)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
std::vector<uint8_t> buf;
buf.reserve(srcLength);
auto src8 = static_cast<const uint8_t*>(src);
auto dst8 = static_cast<uint8_t*>(dst);
uint8_t code = 1;
for (size_t i = 0; i < srcLength; i++)
{
dst8[i] = Numerics::ror8(src8[i], code);
buf.push_back(Numerics::ror8(src8[i], code));
code = (code + 2) % 8;
}
return srcLength;
return buf;
}

View File

@@ -13,7 +13,9 @@
#include "../util/SawyerCoding.h"
#include "SawyerChunk.h"
#include <cstdint>
#include <memory>
#include <vector>
class SawyerChunkException : public IOException
{
@@ -85,9 +87,9 @@ public:
}
private:
static size_t DecodeChunk(void* dst, size_t dstCapacity, const void* src, const SawyerCodingChunkHeader& header);
static size_t DecodeChunkRLERepeat(void* dst, size_t dstCapacity, const void* src, size_t srcLength);
static size_t DecodeChunkRLE(void* dst, size_t dstCapacity, const void* src, size_t srcLength);
static size_t DecodeChunkRepeat(void* dst, size_t dstCapacity, const void* src, size_t srcLength);
static size_t DecodeChunkRotate(void* dst, size_t dstCapacity, const void* src, size_t srcLength);
static std::vector<uint8_t> DecodeChunk(const void* src, const SawyerCodingChunkHeader& header);
static std::vector<uint8_t> DecodeChunkRLERepeat(const void* src, size_t srcLength);
static std::vector<uint8_t> DecodeChunkRLE(const void* src, size_t srcLength);
static std::vector<uint8_t> DecodeChunkRepeat(const void* src, size_t srcLength);
static std::vector<uint8_t> DecodeChunkRotate(const void* src, size_t srcLength);
};