/*
* 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 .
*/
#include "ByteBuffer.h"
#include "Errors.h"
#include "Log.h"
#include
#include
#include
#include
ByteBufferPositionException::ByteBufferPositionException(size_t pos, size_t size, size_t valueSize)
: ByteBufferException(Trinity::StringFormat("Attempted to get value with size: {} in ByteBuffer (pos: {} size: {})", valueSize, pos, size))
{
}
ByteBufferInvalidValueException::ByteBufferInvalidValueException(char const* type, std::string_view value)
: ByteBufferException(Trinity::StringFormat("Invalid {} value ({}) found in ByteBuffer", type, value))
{
}
ByteBuffer& ByteBuffer::operator>>(float& value)
{
read(&value, 1);
if (!std::isfinite(value))
throw ByteBufferInvalidValueException("float", "infinity");
return *this;
}
ByteBuffer& ByteBuffer::operator>>(double& value)
{
read(&value, 1);
if (!std::isfinite(value))
throw ByteBufferInvalidValueException("double", "infinity");
return *this;
}
std::string_view ByteBuffer::ReadCString(bool requireValidUtf8 /*= true*/)
{
if (_rpos >= size())
throw ByteBufferPositionException(_rpos, 1, size());
ResetBitPos();
char const* begin = reinterpret_cast(_storage.data()) + _rpos;
char const* end = reinterpret_cast(_storage.data()) + size();
char const* stringEnd = std::ranges::find(begin, end, '\0');
if (stringEnd == end)
throw ByteBufferPositionException(size(), 1, size());
std::string_view value(begin, stringEnd);
_rpos += value.length() + 1;
if (requireValidUtf8 && !utf8::is_valid(value.begin(), value.end()))
throw ByteBufferInvalidValueException("string", value);
return value;
}
std::string_view ByteBuffer::ReadString(uint32 length, bool requireValidUtf8 /*= true*/)
{
if (_rpos + length > size())
throw ByteBufferPositionException(_rpos, length, size());
ResetBitPos();
if (!length)
return {};
std::string_view value(reinterpret_cast(&_storage[_rpos]), length);
_rpos += length;
if (requireValidUtf8 && !utf8::is_valid(value.begin(), value.end()))
throw ByteBufferInvalidValueException("string", value);
return value;
}
void ByteBuffer::append(uint8 const* src, size_t cnt)
{
ASSERT(src, "Attempted to put a NULL-pointer in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", _wpos, size());
ASSERT(cnt, "Attempted to put a zero-sized value in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", _wpos, size());
ASSERT((size() + cnt) < 100000000);
FlushBits();
size_t const newSize = _wpos + cnt;
if (_storage.capacity() < newSize) // custom memory allocation rules
{
if (newSize < 100)
_storage.reserve(300);
else if (newSize < 750)
_storage.reserve(2500);
else if (newSize < 6000)
_storage.reserve(10000);
else
_storage.reserve(400000);
}
if (_storage.size() < newSize)
_storage.resize(newSize);
std::memcpy(&_storage[_wpos], src, cnt);
_wpos = newSize;
}
void ByteBuffer::put(size_t pos, uint8 const* src, size_t cnt)
{
ASSERT(pos + cnt <= size(), "Attempted to put value with size: " SZFMTD " in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", cnt, pos, size());
ASSERT(src, "Attempted to put a NULL-pointer in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", pos, size());
ASSERT(cnt, "Attempted to put a zero-sized value in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", pos, size());
std::memcpy(&_storage[pos], src, cnt);
}
void ByteBuffer::PutBits(std::size_t pos, std::size_t value, uint32 bitCount)
{
ASSERT(pos + bitCount <= size() * 8, "Attempted to put %u bits in ByteBuffer (bitpos: " SZFMTD " size: " SZFMTD ")", bitCount, pos, size());
ASSERT(bitCount, "Attempted to put a zero bits in ByteBuffer");
for (uint32 i = 0; i < bitCount; ++i)
{
std::size_t wp = (pos + i) / 8;
std::size_t bit = (pos + i) % 8;
if ((value >> (bitCount - i - 1)) & 1)
_storage[wp] |= 1 << (7 - bit);
else
_storage[wp] &= ~(1 << (7 - bit));
}
}
void ByteBuffer::print_storage() const
{
Logger const* networkLogger = sLog->GetEnabledLogger("network", LOG_LEVEL_TRACE);
if (!networkLogger) // optimize disabled trace output
return;
std::ostringstream o;
for (uint32 i = 0; i < size(); ++i)
o << uint32(_storage[i]) << " - ";
TC_LOG_TRACE("network", "STORAGE_SIZE: {} {}", size(), o.view());
}
void ByteBuffer::textlike() const
{
Logger const* networkLogger = sLog->GetEnabledLogger("network", LOG_LEVEL_TRACE);
if (networkLogger) // optimize disabled trace output
return;
std::ostringstream o;
for (uint32 i = 0; i < size(); ++i)
o << char(_storage[i]);
sLog->OutMessageTo(networkLogger, "network", LOG_LEVEL_TRACE, "STORAGE_SIZE: {} {}", size(), o.view());
}
void ByteBuffer::hexlike() const
{
Logger const* networkLogger = sLog->GetEnabledLogger("network", LOG_LEVEL_TRACE);
if (!networkLogger) // optimize disabled trace output
return;
std::ostringstream o;
o.setf(std::ios_base::hex, std::ios_base::basefield);
o.fill('0');
for (uint32 i = 0; i < size(); )
{
char const* sep = " | ";
for (uint32 j = 0; j < 2; ++j)
{
for (uint32 k = 0; k < 8; ++k)
{
o.width(2);
o << _storage[i];
++i;
}
o << sep;
sep = "\n";
}
}
sLog->OutMessageTo(networkLogger, "network", LOG_LEVEL_TRACE, "STORAGE_SIZE: {} {}", size(), o.view());
}
void ByteBuffer::OnInvalidPosition(size_t pos, size_t valueSize) const
{
throw ByteBufferPositionException(pos, _storage.size(), valueSize);
}
template TC_SHARED_API char ByteBuffer::read();
template TC_SHARED_API uint8 ByteBuffer::read();
template TC_SHARED_API uint16 ByteBuffer::read();
template TC_SHARED_API uint32 ByteBuffer::read();
template TC_SHARED_API uint64 ByteBuffer::read();
template TC_SHARED_API int8 ByteBuffer::read();
template TC_SHARED_API int16 ByteBuffer::read();
template TC_SHARED_API int32 ByteBuffer::read();
template TC_SHARED_API int64 ByteBuffer::read();
template TC_SHARED_API float ByteBuffer::read();
template TC_SHARED_API double ByteBuffer::read();