/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#ifndef TRINITYCORE_PACKET_OPERATORS_H
#define TRINITYCORE_PACKET_OPERATORS_H
#include "ByteBuffer.h"
#include "Optional.h"
#include
namespace WorldPackets
{
namespace Strings
{
enum Utf8Mode : uint8
{
DontValidateUtf8,
ValidUtf8
};
}
template
concept AsWritable = std::is_default_constructible_v && HasByteBufferShiftOperators;
template
concept AsWritableFor = requires { static_cast(std::declval()); }
&& requires { static_cast(Underlying()); };
template T>
struct AsWriter
{
T const& Value;
friend inline ByteBuffer& operator<<(ByteBuffer& data, AsWriter const& as)
{
data << static_cast(as.Value);
return data;
}
};
template T>
struct AsReaderWriter : AsWriter
{
friend inline ByteBuffer& operator>>(ByteBuffer& data, AsReaderWriter const& as)
{
Underlying temp;
data >> temp;
const_cast(as.Value) = static_cast(temp);
return data;
}
};
template T>
inline AsWriter As(T const& value) { return { value }; }
template T>
inline AsReaderWriter As(T& value) { return { value }; }
template
concept OptionalWritable = std::is_default_constructible_v;
template
struct OptionalInitWriter
{
Optional const& Opt;
friend inline ByteBuffer& operator<<(ByteBuffer& data, OptionalInitWriter const& opt)
{
data.WriteBit(opt.Opt.has_value());
return data;
}
};
template
struct OptionalInitReaderWriter : OptionalInitWriter
{
friend inline ByteBuffer& operator>>(ByteBuffer& data, OptionalInitReaderWriter const& opt)
{
if (data.ReadBit())
const_cast&>(opt.Opt).emplace();
return data;
}
};
template
inline OptionalInitWriter OptionalInit(Optional const& value) { return { value }; }
template
inline OptionalInitReaderWriter OptionalInit(Optional& value) { return { value }; }
template
struct PtrInitWriter
{
std::unique_ptr const& Ptr;
friend inline ByteBuffer& operator<<(ByteBuffer& data, PtrInitWriter const& opt)
{
data.WriteBit(opt.Ptr != nullptr);
return data;
}
};
template
struct PtrInitReaderWriter : PtrInitWriter
{
friend inline ByteBuffer& operator>>(ByteBuffer& data, PtrInitReaderWriter const& opt)
{
if (data.ReadBit())
const_cast&>(opt.Ptr) = std::make_unique();
return data;
}
};
template
inline PtrInitWriter OptionalInit(std::unique_ptr const& value) { return { value }; }
template
inline PtrInitReaderWriter OptionalInit(std::unique_ptr& value) { return { value }; }
template
concept BitsWritable = AsWritableFor;
template
struct BitsWriter
{
T const& Value;
friend inline ByteBuffer& operator<<(ByteBuffer& data, BitsWriter const& bits)
{
if constexpr (BitCount != 1)
data.WriteBits(static_cast(bits.Value), BitCount);
else
data.WriteBit(static_cast(bits.Value) != 0);
return data;
}
};
template
struct BitsReaderWriter : BitsWriter
{
friend inline ByteBuffer& operator>>(ByteBuffer& data, BitsReaderWriter const& bits)
{
if constexpr (BitCount != 1)
const_cast(bits.Value) = static_cast(data.ReadBits(BitCount));
else
const_cast(bits.Value) = static_cast(data.ReadBit() ? 1 : 0);
return data;
}
};
template
inline BitsWriter Bits(T const& value) { return { value }; }
template
inline BitsReaderWriter Bits(T& value) { return { value }; }
template
concept ContainerWritable = requires(T const& container) { { container.size() } -> AsWritableFor; }
&& !std::same_as
&& !std::same_as;
template
concept ContainerReadable = ContainerWritable
&& !std::is_const_v
&& requires(T & container) { container.resize(SizeType{}); };
template Container>
struct SizeWriter
{
Container const& Value;
friend inline ByteBuffer& operator<<(ByteBuffer& data, SizeWriter const& size)
{
data << static_cast(size.Value.size());
return data;
}
};
template Container>
struct SizeReaderWriter : SizeWriter
{
friend inline ByteBuffer& operator>>(ByteBuffer& data, SizeReaderWriter const& size)
{
Underlying temp;
data >> temp;
if constexpr (std::is_same_v || std::is_same_v)
if (size_t rpos = data.rpos(); temp > data.size() - rpos)
data.OnInvalidPosition(rpos, temp);
if constexpr (std::is_same_v, std::string_view>)
// create a temporary string_view pointing at random position in ByteBuffer to be able to retrieve the length later
const_cast(size.Value) = { reinterpret_cast(data.data()), temp };
else
const_cast(size.Value).resize(temp);
return data;
}
};
template Container>
inline SizeWriter Size(Container const& value) { return { value }; }
template Container>
inline SizeReaderWriter Size(Container& value) { return { value }; }
template Container>
struct BitsSizeWriter
{
Container const& Value;
friend inline ByteBuffer& operator<<(ByteBuffer& data, BitsSizeWriter const& size)
{
data.WriteBits(static_cast(size.Value.size()), BitCount);
return data;
}
};
template Container>
struct BitsSizeReaderWriter : BitsSizeWriter
{
friend inline ByteBuffer& operator>>(ByteBuffer& data, BitsSizeReaderWriter const& size)
{
const_cast(size.Value).resize(data.ReadBits(BitCount));
return data;
}
};
template Container>
inline BitsSizeWriter BitsSize(Container const& value) { return { value }; }
template Container>
inline BitsSizeReaderWriter BitsSize(Container& value) { return { value }; }
template
concept StringWritable = requires(T const& container) { { container.length() } -> AsWritableFor; }
&& requires(ByteBuffer& data, T const& string) { data.WriteString(static_cast(string)); /*TODO: Kill String class and remove the cast*/ };
template
concept StringReadable = StringWritable
&& !std::is_const_v
&& (requires(T& container) { container.resize(uint32()); } || std::same_as)
&& requires(ByteBuffer& data, T& string) { string = data.ReadString(uint32(), bool()); };
namespace SizedString
{
template
struct SizeWriter
{
Container const& Value;
friend inline ByteBuffer& operator<<(ByteBuffer& data, SizeWriter const& size)
{
data.WriteBits(static_cast(size.Value.length()), BitCount);
return data;
}
};
template
struct SizeReaderWriter : SizeWriter
{
friend inline ByteBuffer& operator>>(ByteBuffer& data, SizeReaderWriter const& size)
{
uint32 length = data.ReadBits(BitCount);
if (size_t rpos = data.rpos(); length > data.size() - rpos)
data.OnInvalidPosition(rpos, length);
if constexpr (std::is_same_v)
// create a temporary string_view pointing at start of ByteBuffer to be able to retrieve the length later
const_cast(size.Value) = { reinterpret_cast(data.data()), length };
else
const_cast(size.Value).resize(length);
return data;
}
};
template
inline SizeWriter BitsSize(Container const& value) { return { value }; }
template
inline SizeReaderWriter BitsSize(Container& value) { return { value }; }
template
struct DataWriter
{
Container const& Value;
friend inline ByteBuffer& operator<<(ByteBuffer& data, DataWriter const& string)
{
data.WriteString(string.Value);
return data;
}
};
template
struct DataReaderWriter : DataWriter
{
static constexpr bool IsUtf8() { return Mode == Strings::ValidUtf8; }
friend inline ByteBuffer& operator>>(ByteBuffer& data, DataReaderWriter const& string)
{
const_cast(string.Value) = data.ReadString(string.Value.length(), IsUtf8());
return data;
}
};
template
inline DataWriter Data(Container const& value) { return { value }; }
template
inline DataReaderWriter Data(Container& value) { return { value }; }
}
// SizedCString (sends size + string + null terminator but only if not empty)
namespace SizedCString
{
template
struct SizeWriter
{
Container const& Value;
friend inline ByteBuffer& operator<<(ByteBuffer& data, SizeWriter const& size)
{
data.WriteBits(static_cast(size.Value.length() + 1), BitCount);
return data;
}
};
template
struct SizeReaderWriter : SizeWriter
{
friend inline ByteBuffer& operator>>(ByteBuffer& data, SizeReaderWriter const& size)
{
if (uint32 bytesIncludingNullTerminator = data.ReadBits(BitCount); bytesIncludingNullTerminator > 1)
{
uint32 length = bytesIncludingNullTerminator - 1;
if (size_t rpos = data.rpos(); length > data.size() - rpos)
data.OnInvalidPosition(rpos, bytesIncludingNullTerminator);
if constexpr (std::is_same_v)
// create a temporary string_view pointing at start of ByteBuffer to be able to retrieve the length later
const_cast(size.Value) = { reinterpret_cast(data.data()), length };
else
const_cast(size.Value).resize(length);
}
return data;
}
};
template
inline SizeWriter BitsSize(Container const& value) { return { value }; }
template
inline SizeReaderWriter BitsSize(Container& value) { return { value }; }
template
struct DataWriter
{
Container const& Value;
friend inline ByteBuffer& operator<<(ByteBuffer& data, DataWriter const& string)
{
if (!string.Value.empty())
{
data.WriteString(string.Value);
data << char('\0');
}
return data;
}
};
template
struct DataReaderWriter : DataWriter
{
static constexpr bool IsUtf8() { return Mode == Strings::ValidUtf8; }
friend inline ByteBuffer& operator>>(ByteBuffer& data, DataReaderWriter const& string)
{
if (!string.Value.empty())
{
const_cast(string.Value) = data.ReadString(string.Value.length(), IsUtf8());
(void)data.read(); // null terminator
}
return data;
}
};
template
inline DataWriter Data(Container const& value) { return { value }; }
template
inline DataReaderWriter Data(Container& value) { return { value }; }
}
}
#endif // TRINITYCORE_PACKET_OPERATORS_H