From f6a23fd5f5f983d23e4947dfd9f0e17fe65e4360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= Date: Sat, 9 Nov 2019 15:54:43 +0100 Subject: [PATCH] Add compile time selection for copying data with specialised paths (#9331) --- src/openrct2/core/IStream.hpp | 98 +++++++++++++++++++++++++++++- src/openrct2/core/MemoryStream.cpp | 54 +++++++++++++++- src/openrct2/core/MemoryStream.h | 44 ++++++++++++++ 3 files changed, 191 insertions(+), 5 deletions(-) diff --git a/src/openrct2/core/IStream.hpp b/src/openrct2/core/IStream.hpp index 0ded91b216..815ed43063 100644 --- a/src/openrct2/core/IStream.hpp +++ b/src/openrct2/core/IStream.hpp @@ -51,16 +51,84 @@ interface IStream virtual const void* GetData() const abstract; + /////////////////////////////////////////////////////////////////////////// + // Fast path methods, class can override them to use specialised copies. + /////////////////////////////////////////////////////////////////////////// + virtual void Read1(void* buffer) + { + Read(buffer, 1); + } + virtual void Read2(void* buffer) + { + Read(buffer, 2); + } + virtual void Read4(void* buffer) + { + Read(buffer, 4); + } + virtual void Read8(void* buffer) + { + Read(buffer, 8); + } + virtual void Read16(void* buffer) + { + Read(buffer, 16); + } + + virtual void Write1(const void* buffer) + { + Write(buffer, 1); + } + virtual void Write2(const void* buffer) + { + Write(buffer, 2); + } + virtual void Write4(const void* buffer) + { + Write(buffer, 4); + } + virtual void Write8(const void* buffer) + { + Write(buffer, 8); + } + virtual void Write16(const void* buffer) + { + Write(buffer, 16); + } + /////////////////////////////////////////////////////////////////////////// // Helper methods /////////////////////////////////////////////////////////////////////////// - /** * Reads the size of the given type from the stream directly into the given address. */ template void Read(T * value) { - Read(value, sizeof(T)); + // Selects the best path at compile time + if constexpr (sizeof(T) == 1) + { + Read1(value); + } + else if constexpr (sizeof(T) == 2) + { + Read2(value); + } + else if constexpr (sizeof(T) == 4) + { + Read4(value); + } + else if constexpr (sizeof(T) == 8) + { + Read8(value); + } + else if constexpr (sizeof(T) == 16) + { + Read16(value); + } + else + { + Read(value, sizeof(T)); + } } /** @@ -68,7 +136,31 @@ interface IStream */ template void Write(const T* value) { - Write(value, sizeof(T)); + // Selects the best path at compile time + if constexpr (sizeof(T) == 1) + { + Write1(value); + } + else if constexpr (sizeof(T) == 2) + { + Write2(value); + } + else if constexpr (sizeof(T) == 4) + { + Write4(value); + } + else if constexpr (sizeof(T) == 8) + { + Write8(value); + } + else if constexpr (sizeof(T) == 16) + { + Write16(value); + } + else + { + Write(value, sizeof(T)); + } } /** diff --git a/src/openrct2/core/MemoryStream.cpp b/src/openrct2/core/MemoryStream.cpp index fcae646afc..c4a58f8cab 100644 --- a/src/openrct2/core/MemoryStream.cpp +++ b/src/openrct2/core/MemoryStream.cpp @@ -155,10 +155,35 @@ void MemoryStream::Read(void* buffer, uint64_t length) throw IOException("Attempted to read past end of stream."); } - std::copy_n((const uint8_t*)_position, length, (uint8_t*)buffer); + std::memcpy(buffer, _position, length); _position = (void*)((uintptr_t)_position + length); } +void MemoryStream::Read1(void* buffer) +{ + Read<1>(buffer); +} + +void MemoryStream::Read2(void* buffer) +{ + Read<2>(buffer); +} + +void MemoryStream::Read4(void* buffer) +{ + Read<4>(buffer); +} + +void MemoryStream::Read8(void* buffer) +{ + Read<8>(buffer); +} + +void MemoryStream::Read16(void* buffer) +{ + Read<16>(buffer); +} + uint64_t MemoryStream::TryRead(void* buffer, uint64_t length) { uint64_t remainingBytes = GetLength() - GetPosition(); @@ -183,11 +208,36 @@ void MemoryStream::Write(const void* buffer, uint64_t length) } } - std::copy_n((const uint8_t*)buffer, length, (uint8_t*)_position); + std::memcpy(_position, buffer, length); _position = (void*)((uintptr_t)_position + length); _dataSize = std::max(_dataSize, (size_t)nextPosition); } +void MemoryStream::Write1(const void* buffer) +{ + Write<1>(buffer); +} + +void MemoryStream::Write2(const void* buffer) +{ + Write<2>(buffer); +} + +void MemoryStream::Write4(const void* buffer) +{ + Write<4>(buffer); +} + +void MemoryStream::Write8(const void* buffer) +{ + Write<8>(buffer); +} + +void MemoryStream::Write16(const void* buffer) +{ + Write<16>(buffer); +} + void MemoryStream::EnsureCapacity(size_t capacity) { if (_dataCapacity < capacity) diff --git a/src/openrct2/core/MemoryStream.h b/src/openrct2/core/MemoryStream.h index 1401d1a87b..18b15dbfd2 100644 --- a/src/openrct2/core/MemoryStream.h +++ b/src/openrct2/core/MemoryStream.h @@ -58,7 +58,51 @@ public: void Seek(int64_t offset, int32_t origin) override; void Read(void* buffer, uint64_t length) override; + void Read1(void* buffer) override; + void Read2(void* buffer) override; + void Read4(void* buffer) override; + void Read8(void* buffer) override; + void Read16(void* buffer) override; + + template void Read(void* buffer) + { + uint64_t position = GetPosition(); + if (position + N > _dataSize) + { + throw IOException("Attempted to read past end of stream."); + } + + std::memcpy(buffer, _position, N); + _position = (void*)((uintptr_t)_position + N); + } + void Write(const void* buffer, uint64_t length) override; + void Write1(const void* buffer) override; + void Write2(const void* buffer) override; + void Write4(const void* buffer) override; + void Write8(const void* buffer) override; + void Write16(const void* buffer) override; + + template void Write(const void* buffer) + { + uint64_t position = GetPosition(); + uint64_t nextPosition = position + N; + if (nextPosition > _dataCapacity) + { + if (_access & MEMORY_ACCESS::OWNER) + { + EnsureCapacity((size_t)nextPosition); + } + else + { + throw IOException("Attempted to write past end of stream."); + } + } + + std::memcpy(_position, buffer, N); + _position = (void*)((uintptr_t)_position + N); + _dataSize = std::max(_dataSize, (size_t)nextPosition); + } uint64_t TryRead(void* buffer, uint64_t length) override;