1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-16 03:23:15 +01:00

Merge pull request #22595 from ZehMatt/sawyer-junk-2

Improve performance for SawyerChunkReader once again
This commit is contained in:
Michael Steenbeek
2024-08-21 23:08:23 +02:00
committed by GitHub
4 changed files with 71 additions and 75 deletions

View File

@@ -9,10 +9,11 @@
#include "SawyerChunk.h"
#include "../core/Memory.hpp"
#include "SawyerChunkReader.h"
SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, std::vector<uint8_t>&& data)
using namespace OpenRCT2;
SawyerChunk::SawyerChunk(SAWYER_ENCODING encoding, MemoryStream&& data)
: _data(std::move(data))
, _encoding(encoding)
{

View File

@@ -9,8 +9,9 @@
#pragma once
#include <memory>
#include <vector>
#include "../core/MemoryStream.h"
#include <cstdint>
/**
* The type of encoding / compression for a sawyer encoded chunk.
@@ -29,22 +30,22 @@ enum class SAWYER_ENCODING : uint8_t
class SawyerChunk final
{
private:
std::vector<uint8_t> _data;
OpenRCT2::MemoryStream _data;
SAWYER_ENCODING _encoding = SAWYER_ENCODING::NONE;
public:
const void* GetData() const
{
return _data.data();
return _data.GetData();
}
size_t GetLength() const
{
return _data.size();
return _data.GetLength();
}
SAWYER_ENCODING GetEncoding() const
{
return _encoding;
}
SawyerChunk(SAWYER_ENCODING encoding, std::vector<uint8_t>&& data);
SawyerChunk(SAWYER_ENCODING encoding, OpenRCT2::MemoryStream&& data);
};

View File

@@ -10,20 +10,11 @@
#include "SawyerChunkReader.h"
#include "../core/IStream.hpp"
#include "../core/MemoryStream.h"
#include "../core/Numerics.hpp"
using namespace OpenRCT2;
// malloc is very slow for large allocations in MSVC debug builds as it allocates
// memory on a special debug heap and then initialises all the memory to 0xCC.
#if defined(_WIN32) && defined(DEBUG)
# define __USE_HEAP_ALLOC__
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
#endif
// Allow chunks to be uncompressed to a maximum of 16 MiB
constexpr size_t MAX_UNCOMPRESSED_CHUNK_SIZE = 16 * 1024 * 1024;
@@ -33,6 +24,8 @@ constexpr const char* EXCEPTION_MSG_DESTINATION_TOO_SMALL = "Chunk data larger t
constexpr const char* EXCEPTION_MSG_INVALID_CHUNK_ENCODING = "Invalid chunk encoding.";
constexpr const char* EXCEPTION_MSG_ZERO_SIZED_CHUNK = "Encountered zero-sized chunk.";
static MemoryStream DecodeChunk(const void* src, const SawyerCodingChunkHeader& header);
SawyerChunkReader::SawyerChunkReader(OpenRCT2::IStream* stream)
: _stream(stream)
{
@@ -77,10 +70,11 @@ std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunk()
}
auto buffer = DecodeChunk(compressedData.get(), header);
if (buffer.empty())
if (buffer.GetLength() == 0)
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
return std::make_shared<SawyerChunk>(static_cast<SAWYER_ENCODING>(header.encoding), std::move(buffer));
}
default:
@@ -116,7 +110,7 @@ std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunkTrack()
SawyerCodingChunkHeader header{ CHUNK_ENCODING_RLE, compressedDataLength };
auto buffer = DecodeChunk(compressedData.get(), header);
if (buffer.empty())
if (buffer.GetLength() == 0)
{
throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK);
}
@@ -151,40 +145,9 @@ void SawyerChunkReader::ReadChunk(void* dst, size_t length)
}
}
std::vector<uint8_t> SawyerChunkReader::DecodeChunk(const void* src, const SawyerCodingChunkHeader& header)
static MemoryStream DecodeChunkRLE(const void* src, size_t srcLength)
{
std::vector<uint8_t> buf;
switch (header.encoding)
{
case CHUNK_ENCODING_NONE:
buf.resize(header.length);
std::memcpy(buf.data(), src, header.length);
break;
case CHUNK_ENCODING_RLE:
buf = DecodeChunkRLE(src, header.length);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
buf = DecodeChunkRLERepeat(src, header.length);
break;
case CHUNK_ENCODING_ROTATE:
buf = DecodeChunkRotate(src, header.length);
break;
default:
throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING);
}
return buf;
}
std::vector<uint8_t> SawyerChunkReader::DecodeChunkRLERepeat(const void* src, size_t srcLength)
{
auto tempBuf = DecodeChunkRLE(src, srcLength);
return DecodeChunkRepeat(tempBuf.data(), tempBuf.size());
}
std::vector<uint8_t> SawyerChunkReader::DecodeChunkRLE(const void* src, size_t srcLength)
{
std::vector<uint8_t> buf;
buf.reserve(srcLength);
MemoryStream buf;
auto src8 = static_cast<const uint8_t*>(src);
for (size_t i = 0; i < srcLength; i++)
@@ -199,12 +162,15 @@ std::vector<uint8_t> SawyerChunkReader::DecodeChunkRLE(const void* src, size_t s
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (buf.size() + count > MAX_UNCOMPRESSED_CHUNK_SIZE)
if (buf.GetLength() + count > MAX_UNCOMPRESSED_CHUNK_SIZE)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
buf.insert(buf.end(), count, src8[i]);
for (size_t n = 0; n < count; n++)
{
buf.Write1(src8 + i);
}
}
else
{
@@ -214,7 +180,7 @@ std::vector<uint8_t> SawyerChunkReader::DecodeChunkRLE(const void* src, size_t s
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (buf.size() + len > MAX_UNCOMPRESSED_CHUNK_SIZE)
if (buf.GetLength() + len > MAX_UNCOMPRESSED_CHUNK_SIZE)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
@@ -224,8 +190,8 @@ std::vector<uint8_t> SawyerChunkReader::DecodeChunkRLE(const void* src, size_t s
}
const auto* pos = src8 + i + 1;
buf.insert(buf.end(), pos, pos + len);
buf.Write(pos, len);
i += len;
}
}
@@ -233,10 +199,9 @@ std::vector<uint8_t> SawyerChunkReader::DecodeChunkRLE(const void* src, size_t s
return buf;
}
std::vector<uint8_t> SawyerChunkReader::DecodeChunkRepeat(const void* src, size_t srcLength)
static MemoryStream DecodeChunkRepeat(const void* src, size_t srcLength)
{
std::vector<uint8_t> buf;
buf.reserve(srcLength);
MemoryStream buf;
auto src8 = static_cast<const uint8_t*>(src);
for (size_t i = 0; i < srcLength; i++)
@@ -247,15 +212,17 @@ std::vector<uint8_t> SawyerChunkReader::DecodeChunkRepeat(const void* src, size_
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
buf.push_back(src8[++i]);
i++;
buf.Write1(src8 + i);
}
else
{
size_t count = (src8[i] & 7) + 1;
int32_t offset = static_cast<int32_t>(src8[i] >> 3) - 32;
const uint8_t* copySrc = buf.data() + (buf.size() + offset);
const uint8_t* copySrc = static_cast<const uint8_t*>(buf.GetData()) + (buf.GetLength() + offset);
if (copySrc < buf.data() || copySrc + count > buf.data() + buf.size())
if (copySrc < static_cast<const uint8_t*>(buf.GetData())
|| copySrc + count > static_cast<const uint8_t*>(buf.GetData()) + buf.GetLength())
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
@@ -264,26 +231,60 @@ std::vector<uint8_t> SawyerChunkReader::DecodeChunkRepeat(const void* src, size_
uint8_t temp[16];
std::memcpy(temp, copySrc, count);
buf.insert(buf.end(), std::begin(temp), std::begin(temp) + count);
buf.Write(temp, count);
}
}
return buf;
}
std::vector<uint8_t> SawyerChunkReader::DecodeChunkRotate(const void* src, size_t srcLength)
static MemoryStream DecodeChunkRLERepeat(const void* src, size_t srcLength)
{
std::vector<uint8_t> buf;
buf.reserve(srcLength);
auto tempBuf = DecodeChunkRLE(src, srcLength);
return DecodeChunkRepeat(tempBuf.GetData(), tempBuf.GetLength());
}
static MemoryStream DecodeChunkRotate(const void* src, size_t srcLength)
{
MemoryStream buf;
auto src8 = static_cast<const uint8_t*>(src);
uint8_t code = 1;
for (size_t i = 0; i < srcLength; i++)
{
buf.push_back(Numerics::ror8(src8[i], code));
uint8_t temp = Numerics::ror8(src8[i], code);
buf.Write1(&temp);
code = (code + 2) % 8;
}
return buf;
}
static MemoryStream DecodeChunk(const void* src, const SawyerCodingChunkHeader& header)
{
MemoryStream buf;
switch (header.encoding)
{
case CHUNK_ENCODING_NONE:
buf.Write(src, header.length);
break;
case CHUNK_ENCODING_RLE:
buf = DecodeChunkRLE(src, header.length);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
buf = DecodeChunkRLERepeat(src, header.length);
break;
case CHUNK_ENCODING_ROTATE:
buf = DecodeChunkRotate(src, header.length);
break;
default:
throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING);
}
// Return the stream with the position at the beginning.
buf.SetPosition(0);
return buf;
}

View File

@@ -85,11 +85,4 @@ public:
ReadChunk(&result, sizeof(result));
return result;
}
private:
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);
};