Core/Misc: Replaced boost::container::static_vector in packet classes with plain vector with custom static storage allocatlr

This commit is contained in:
Shauren
2023-01-06 16:52:44 +01:00
parent 42fbcf2fc5
commit b65c3f5f4a
9 changed files with 230 additions and 20 deletions

View File

@@ -32,6 +32,7 @@ if(SERVERS)
add_subdirectory(rapidjson)
add_subdirectory(efsw)
add_subdirectory(protobuf)
add_subdirectory(short_alloc)
endif()
if(TOOLS)

View File

@@ -72,3 +72,7 @@ rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http
protobuf (Protocol Buffers - Google's data interchange format https://developers.google.com/protocol-buffers/)
https://github.com/google/protobuf
Version: v2.6.1
short_alloc (Stack based allocator) https://howardhinnant.github.io/stack_alloc.html
https://howardhinnant.github.io/short_alloc.h
Version: N/A

View File

@@ -0,0 +1,15 @@
# This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
add_library(short_alloc INTERFACE)
target_include_directories(short_alloc
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -0,0 +1,162 @@
#ifndef SHORT_ALLOC_H
#define SHORT_ALLOC_H
// The MIT License (MIT)
//
// Copyright (c) 2015 Howard Hinnant
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <cassert>
#include <cstddef>
#include <cstdint>
namespace short_alloc
{
template <std::size_t N, std::size_t alignment = alignof(std::max_align_t)>
class arena
{
alignas(alignment) char buf_[N];
char* ptr_;
public:
~arena() {ptr_ = nullptr;}
arena() noexcept : ptr_(buf_) {}
arena(const arena&) = delete;
arena& operator=(const arena&) = delete;
template <std::size_t ReqAlign> char* allocate(std::size_t n);
void deallocate(char* p, std::size_t n) noexcept;
static constexpr std::size_t size() noexcept {return N;}
std::size_t used() const noexcept {return static_cast<std::size_t>(ptr_ - buf_);}
void reset() noexcept {ptr_ = buf_;}
private:
static
std::size_t
align_up(std::size_t n) noexcept
{return (n + (alignment-1)) & ~(alignment-1);}
bool
pointer_in_buffer(char* p) noexcept
{
return std::uintptr_t(buf_) <= std::uintptr_t(p) &&
std::uintptr_t(p) <= std::uintptr_t(buf_) + N;
}
};
template <std::size_t N, std::size_t alignment>
template <std::size_t ReqAlign>
char*
arena<N, alignment>::allocate(std::size_t n)
{
static_assert(ReqAlign <= alignment, "alignment is too small for this arena");
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
auto const aligned_n = align_up(n);
if (static_cast<decltype(aligned_n)>(buf_ + N - ptr_) >= aligned_n)
{
char* r = ptr_;
ptr_ += aligned_n;
return r;
}
static_assert(alignment <= alignof(std::max_align_t), "you've chosen an "
"alignment that is larger than alignof(std::max_align_t), and "
"cannot be guaranteed by normal operator new");
return static_cast<char*>(::operator new(n));
}
template <std::size_t N, std::size_t alignment>
void
arena<N, alignment>::deallocate(char* p, std::size_t n) noexcept
{
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
if (pointer_in_buffer(p))
{
n = align_up(n);
if (p + n == ptr_)
ptr_ = p;
}
else
::operator delete(p);
}
template <class T, std::size_t N, std::size_t Align = alignof(std::max_align_t)>
class short_alloc
{
public:
using value_type = T;
static auto constexpr alignment = Align;
static auto constexpr size = N;
using arena_type = arena<size, alignment>;
private:
arena_type& a_;
public:
short_alloc(const short_alloc&) = default;
short_alloc& operator=(const short_alloc&) = delete;
short_alloc(arena_type& a) noexcept : a_(a)
{
static_assert(size % alignment == 0,
"size N needs to be a multiple of alignment Align");
}
template <class U>
short_alloc(const short_alloc<U, N, alignment>& a) noexcept
: a_(a.a_) {}
template <class _Up> struct rebind {using other = short_alloc<_Up, N, alignment>;};
T* allocate(std::size_t n)
{
return reinterpret_cast<T*>(a_.template allocate<alignof(T)>(n*sizeof(T)));
}
void deallocate(T* p, std::size_t n) noexcept
{
a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T));
}
template <class T1, std::size_t N1, std::size_t A1,
class U, std::size_t M, std::size_t A2>
friend
bool
operator==(const short_alloc<T1, N1, A1>& x, const short_alloc<U, M, A2>& y) noexcept;
template <class U, std::size_t M, std::size_t A> friend class short_alloc;
};
template <class T, std::size_t N, std::size_t A1, class U, std::size_t M, std::size_t A2>
inline
bool
operator==(const short_alloc<T, N, A1>& x, const short_alloc<U, M, A2>& y) noexcept
{
return N == M && A1 == A2 && &x.a_ == &y.a_;
}
template <class T, std::size_t N, std::size_t A1, class U, std::size_t M, std::size_t A2>
inline
bool
operator!=(const short_alloc<T, N, A1>& x, const short_alloc<U, M, A2>& y) noexcept
{
return !(x == y);
}
}
#endif // SHORT_ALLOC_H

View File

@@ -73,7 +73,8 @@ target_link_libraries(common
valgrind
threads
jemalloc
openssl_ed25519)
openssl_ed25519
short_alloc)
add_dependencies(common revision_data.h)

View File

@@ -18,7 +18,6 @@
#include "PacketUtilities.h"
#include "Hyperlinks.h"
#include <utf8.h>
#include <sstream>
WorldPackets::InvalidStringValueException::InvalidStringValueException(std::string const& value) : ByteBufferInvalidValueException("string", value.c_str())
{
@@ -59,7 +58,5 @@ bool WorldPackets::Strings::NoHyperlinks::Validate(std::string const& value)
WorldPackets::PacketArrayMaxCapacityException::PacketArrayMaxCapacityException(std::size_t requestedSize, std::size_t sizeLimit)
{
std::ostringstream builder;
builder << "Attempted to read more array elements from packet " << requestedSize << " than allowed " << sizeLimit;
message().assign(builder.str());
message().assign("Attempted to read more array elements from packet " + Trinity::ToString(requestedSize) + " than allowed " + Trinity::ToString(sizeLimit));
}

View File

@@ -21,7 +21,7 @@
#include "ByteBuffer.h"
#include "Duration.h"
#include "Tuples.h"
#include <boost/container/static_vector.hpp>
#include <short_alloc/short_alloc.h>
#include <string_view>
#include <ctime>
@@ -133,24 +133,49 @@ namespace WorldPackets
/**
* Utility class for automated prevention of loop counter spoofing in client packets
*/
template<typename T, std::size_t N, typename Container = boost::container::static_vector<T, N>>
template<typename T, std::size_t N>
class Array
{
public:
typedef Container storage_type;
using allocator_type = short_alloc::short_alloc<T, (N * sizeof(T) + (alignof(std::max_align_t) - 1)) & ~(alignof(std::max_align_t) - 1)>;
using arena_type = typename allocator_type::arena_type;
typedef std::integral_constant<std::size_t, N> max_capacity;
using storage_type = std::vector<T, allocator_type>;
typedef typename storage_type::value_type value_type;
typedef typename storage_type::size_type size_type;
typedef typename storage_type::pointer pointer;
typedef typename storage_type::const_pointer const_pointer;
typedef typename storage_type::reference reference;
typedef typename storage_type::const_reference const_reference;
typedef typename storage_type::iterator iterator;
typedef typename storage_type::const_iterator const_iterator;
using max_capacity = std::integral_constant<std::size_t, N>;
Array() { }
using value_type = typename storage_type::value_type;
using size_type = typename storage_type::size_type;
using pointer = typename storage_type::pointer;
using const_pointer = typename storage_type::const_pointer;
using reference = typename storage_type::reference;
using const_reference = typename storage_type::const_reference;
using iterator = typename storage_type::iterator;
using const_iterator = typename storage_type::const_iterator;
Array() : _storage(_data) { }
Array(Array const& other) : Array()
{
for (T const& element : other)
_storage.push_back(element);
}
Array(Array&& other) noexcept = delete;
Array& operator=(Array const& other)
{
if (this == &other)
return *this;
_storage.clear();
for (T const& element : other)
_storage.push_back(element);
return *this;
}
Array& operator=(Array&& other) noexcept = delete;
iterator begin() { return _storage.begin(); }
const_iterator begin() const { return _storage.begin(); }
@@ -209,6 +234,7 @@ namespace WorldPackets
}
private:
arena_type _data;
storage_type _storage;
};

View File

@@ -71,7 +71,11 @@ ByteBuffer& operator>>(ByteBuffer& data, TraitConfig& traitConfig)
{
data >> traitConfig.ID;
traitConfig.Type = data.read<TraitConfigType, int32>();
traitConfig.Entries.resize(data.read<uint32>());
uint32 entriesSize = data.read<uint32>();
if (entriesSize > 100)
throw PacketArrayMaxCapacityException(entriesSize, 100);
traitConfig.Entries.resize(entriesSize);
switch (traitConfig.Type)
{
case TraitConfigType::Combat:

View File

@@ -54,7 +54,7 @@ struct TraitConfig
int32 LocalIdentifier = 0; // Local to specialization
int32 SkillLineID = 0;
int32 TraitSystemID = 0;
Array<TraitEntry, 100, std::vector<TraitEntry>> Entries;
std::vector<TraitEntry> Entries;
String<259> Name;
};