Files
o3de/Code/Framework/GridMate/GridMate/Serialize/Buffer.cpp
T
Steve Pham 38261d0800 Shorten copyright headers by splitting into 2 lines (#2213)
* Updated all copyright headers to split the longer original copyright line into 2 shorter lines

Signed-off-by: Steve Pham <spham@amazon.com>
2021-07-16 15:25:48 -07:00

377 lines
14 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <GridMate/Serialize/Buffer.h>
namespace GridMate
{
//-----------------------------------------------------------------------------
// ReadBuffer
//-----------------------------------------------------------------------------
ReadBuffer::ReadBuffer(EndianType endianType, const char* data, PackedSize size, PackedSize offset)
: m_data(data)
, m_startOffset(offset)
, m_read()
, m_length(size)
, m_overrun(false)
, m_endianType(endianType)
{ }
//-----------------------------------------------------------------------------
ReadBuffer::ReadBuffer(const ReadBuffer& rhs)
: m_data(rhs.m_data)
, m_startOffset(rhs.m_startOffset)
, m_read(rhs.m_read)
, m_length(rhs.m_length)
, m_overrun(rhs.m_overrun)
, m_endianType(rhs.m_endianType)
{
}
//-----------------------------------------------------------------------------
bool ReadBuffer::ReadRaw(void* data, PackedSize dataSize)
{
if (m_overrun || dataSize > (m_length - m_read))
{
m_overrun = true;
return false;
}
if ((m_startOffset.GetAdditionalBits() + m_read.GetAdditionalBits()) % CHAR_BIT == 0)
{
// The easy case - no bit shifting needed to read stored data.
memcpy(data, GetRawBytePtr(), dataSize.GetSizeInBytesRoundUp());
m_read += dataSize;
}
else
{
// The hard case - bit shift each byte of the stored data.
for (AZ::u64 i = 0; i < dataSize; ++i, m_read.IncrementBytes(1))
{
/*
* Given the first byte is D1[1234 5678] and next byte is D2[1234 5678] and the current bit offset is 3,
* and our byte of data to be read is A[1234 5678] then:
*
* A[ 45678] A[123 ]
* ||||| |||
* then we need to read D1[12345678] and D2[12345678].
*/
AZ::u8 firstPart = GetRawByte() >> GetBitOffset();
AZ::u8 next_byte = GetNextRawByte();
next_byte = next_byte & ((1 << GetBitOffset()) - 1);
AZ::u8 lastPart = next_byte << (CHAR_BIT - GetBitOffset());
AZ::u8 combinedValue = (firstPart | lastPart);
AZ::u8* outLocation = reinterpret_cast<AZ::u8*>(data) + i;
memcpy(outLocation, &combinedValue, 1);
}
}
if (dataSize.GetAdditionalBits() > 0)
{
// initialize the left over bits with zeroes
AZ::u8* lastByte = reinterpret_cast<AZ::u8*>(data) + dataSize.GetBytes();
*lastByte &= (1U << dataSize.GetAdditionalBits()) - 1U;
}
return true;
}
//-----------------------------------------------------------------------------
bool ReadBuffer::ReadRawBit(bool& data)
{
AZ_Assert(m_read < m_length, "Attempting to read beyond buffer length!");
if (m_overrun || 0 >= Left())
{
m_overrun = true;
return false;
}
data = !!((GetRawByte() & (1 << GetBitOffset())) >> GetBitOffset());
m_read.IncrementBit();
return true;
}
//-----------------------------------------------------------------------------
bool ReadBuffer::Skip(PackedSize skipSize)
{
AZ_Assert((m_read + skipSize) <= m_length, "Attempting to skip beyond buffer length!");
if (m_overrun || skipSize > Left())
{
m_overrun = true;
return false;
}
m_read += skipSize;
return true;
}
//-----------------------------------------------------------------------------
// WriteBuffer
//-----------------------------------------------------------------------------
WriteBuffer::WriteBuffer(EndianType endianType)
: m_data(nullptr)
, m_size()
, m_capacity(0)
, m_endianType(endianType)
{ }
//-----------------------------------------------------------------------------
WriteBuffer::~WriteBuffer()
{
AZ_Assert(m_data == nullptr, "Derived class you call Destroy prior to destruction!");
}
//-----------------------------------------------------------------------------
void WriteBuffer::WriteRaw(const void* data, PackedSize dataSize)
{
if (dataSize > 0)
{
// check remaining capacity
if (m_capacity - m_size.GetSizeInBytesRoundUp() < dataSize)
{
Grow(dataSize.GetSizeInBytesRoundUp());
}
if (m_size.GetAdditionalBits() == 0)
{
// The easy case - no shifting of each byte is necessary
memcpy(m_data + m_size.GetBytes(), data, dataSize.GetSizeInBytesRoundUp());
m_size += dataSize;
}
else
{
// The hard case - we need to shift each byte before writing it
for (AZ::u64 i = 0; i < dataSize.GetBytes(); ++i, m_size.IncrementBytes(1))
{
/*
* Given the input byte is A[1234 5678], the current bit offset is (for example) 3,
* meaning D1[---- -XXX] is already written in the byte.
* current byte D1[1234 5678] and next byte D2[1234 5678] then:
*
* A[ 45678] A[123 ]
* ||||| |||
* we need to write D1[12345678] D2[12345678].
*/
AZ::u8 inputByte = *(reinterpret_cast<const AZ::u8*>(data) + i);
AZ::u8 firstPart = GetRawByte() | (inputByte << GetBitOffset()); // Do take into account the current value in the byte.
AZ::u8 lastPart = (inputByte >> (CHAR_BIT - GetBitOffset())); // Grab the remaining most significant digits
memcpy(GetRawBytePtr(), &firstPart, 1);
memcpy(GetRawBytePtr(1), &lastPart, 1);
}
if(dataSize.GetAdditionalBits() > 0)
{
AZ::u8 inputByte = *(reinterpret_cast<const AZ::u8*>(data) + dataSize.GetBytes());
AZ::u8 part[2];
part[0] = GetRawByte() | (inputByte << GetBitOffset()); // Do take into account the current value in the byte.
part[1] = (inputByte >> (CHAR_BIT - GetBitOffset())); // Grab the remaining most significant digits
memcpy(GetRawBytePtr(), &part[0], sizeof(part) / sizeof(part[0]));
m_size.IncrementBits(dataSize.GetAdditionalBits());
}
}
}
}
void WriteBuffer::WriteFromBuffer(ReadBuffer& rb, PackedSize size)
{
AZ_Assert(rb.Left() >= size, "Not enough available data in the input buffer!");
for (AZStd::size_t i = 0; i < size.GetBytes(); ++i)
{
AZ::u8 tmp;
rb.ReadRaw(&tmp, 1);
WriteRaw(&tmp, 1);
}
if (size.GetAdditionalBits() > 0)
{
auto bitsLeft = size.GetAdditionalBits();
for (AZStd::size_t i = 0; i < bitsLeft; ++i)
{
bool bit = false;
rb.ReadRawBit(bit);
WriteRawBit(bit);
}
}
}
//-----------------------------------------------------------------------------
void WriteBuffer::WriteRawBit(bool data)
{
if (m_capacity <= m_size)
{
Grow(1);
}
// zero out bits we are about to write
*GetRawBytePtr() = (GetRawByte() & ((1 << GetBitOffset()) - 1));
unsigned char beforeValue = GetRawByte();
unsigned char newValue = data ?
(beforeValue | (1 << GetBitOffset())) :
(beforeValue & (((1 << GetBitOffset()) - 1)));
*GetRawBytePtr() = newValue;
m_size.IncrementBit();
}
//-----------------------------------------------------------------------------
void WriteBuffer::Destroy()
{
if (m_data)
{
DeAllocate(m_data, m_capacity.GetBytes(), 1);
m_data = nullptr;
m_capacity = 0;
}
}
//-----------------------------------------------------------------------------
void WriteBuffer::Grow(size_t growSize)
{
size_t newCapacity = m_size.GetSizeInBytesRoundUp() + growSize;
newCapacity += newCapacity / 2; // preallocate 50% more as the AZStd::vector does.
char* newData = reinterpret_cast<char*>(Allocate(newCapacity, 1));
if (m_data)
{
memcpy(newData, m_data, m_size.GetSizeInBytesRoundUp());
DeAllocate(m_data, m_capacity.GetBytes(), 1);
}
m_data = newData;
m_capacity = newCapacity;
}
//-----------------------------------------------------------------------------
// WriteBufferDynamic
//-----------------------------------------------------------------------------
WriteBufferDynamic::WriteBufferDynamic(EndianType endianType, size_t initialCapacity)
: WriteBuffer(endianType)
{
Init(initialCapacity);
}
//-----------------------------------------------------------------------------
WriteBufferDynamic::WriteBufferDynamic(const WriteBufferDynamic& rhs)
: WriteBuffer(rhs.GetEndianType())
{
Init(rhs.Size());
WriteRaw(rhs.Get(), rhs.Size());
}
//-----------------------------------------------------------------------------
WriteBufferDynamic::WriteBufferDynamic(const BaseType& rhs)
: WriteBuffer(rhs.GetEndianType())
{
Init(rhs.Size());
WriteRaw(rhs.Get(), rhs.Size());
}
//-----------------------------------------------------------------------------
WriteBufferDynamic::WriteBufferDynamic(WriteBufferDynamic&& rhs)
: WriteBuffer(rhs.GetEndianType())
{
Swap(AZStd::forward<WriteBufferDynamic>(rhs));
}
//-----------------------------------------------------------------------------
WriteBufferDynamic& WriteBufferDynamic::operator=(WriteBufferDynamic&& rhs)
{
Swap(AZStd::forward<WriteBufferDynamic>(rhs));
return *this;
}
//-----------------------------------------------------------------------------
void WriteBufferDynamic::Swap(WriteBufferDynamic&& rhs)
{
m_data = rhs.m_data;
m_size = rhs.m_size;
m_capacity = rhs.m_capacity;
m_endianType = rhs.m_endianType;
rhs.m_data = nullptr;
rhs.m_size = 0;
rhs.m_capacity = 0;
rhs.m_endianType = EndianType::IgnoreEndian;
}
//-----------------------------------------------------------------------------
WriteBufferDynamic::~WriteBufferDynamic()
{
Destroy();
}
//-----------------------------------------------------------------------------
WriteBufferDynamic& WriteBufferDynamic::operator+=(const BaseType& rhs)
{
WriteRaw(rhs.Get(), rhs.Size());
return *this;
}
//-----------------------------------------------------------------------------
WriteBufferDynamic WriteBufferDynamic::operator+(const BaseType& rhs)
{
WriteBufferDynamic wb(rhs.GetEndianType());
wb += *this;
wb += rhs;
return wb;
}
//-----------------------------------------------------------------------------
void WriteBufferDynamic::Init(PackedSize capacity)
{
AZ_Assert(m_capacity == 0, "This WriteBufferDynamic has already been initialized!");
if (capacity > 0)
{
m_data = reinterpret_cast<char*>(Allocate(capacity.GetSizeInBytesRoundUp(), 1));
m_capacity = capacity;
}
}
//-----------------------------------------------------------------------------
void* WriteBufferDynamic::Allocate(size_t byteSize, size_t alignment)
{
return azmalloc(byteSize, alignment, GridMateAllocatorMP, "WriteBuffer");
}
//-----------------------------------------------------------------------------
void WriteBufferDynamic::DeAllocate(void* ptr, size_t byteSize, size_t alignment)
{
azfree(ptr, GridMateAllocatorMP, byteSize, alignment);
}
//-----------------------------------------------------------------------------
// WriteBufferStaticInPlace
//-----------------------------------------------------------------------------
WriteBufferStaticInPlace::WriteBufferStaticInPlace(EndianType endianType, void* data, size_t capacity)
: WriteBuffer(endianType)
{
m_data = reinterpret_cast<char*>(data);
m_capacity = capacity;
}
//-----------------------------------------------------------------------------
WriteBufferStaticInPlace::~WriteBufferStaticInPlace()
{
m_data = nullptr;
}
//-----------------------------------------------------------------------------
WriteBufferStaticInPlace& WriteBufferStaticInPlace::operator+=(const WriteBuffer& rhs)
{
WriteRaw(rhs.Get(), rhs.Size());
return *this;
}
//-----------------------------------------------------------------------------
void* WriteBufferStaticInPlace::Allocate(size_t byteSize, size_t alignment)
{
(void) byteSize;
(void) alignment;
AZ_Assert(false, "Requesting %d memory for WriteBufferStaticInPlace is invalid", byteSize);
return nullptr;
}
//-----------------------------------------------------------------------------
void WriteBufferStaticInPlace::DeAllocate(void* ptr, size_t byteSize, size_t alignment)
{
(void) ptr;
(void) byteSize;
(void) alignment;
AZ_Assert(false, "No need to call deallocate for this buffer!");
}
//-----------------------------------------------------------------------------
}