/* * 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