blob: b0eba364308dfb05f1f1ecb5698999a5604a1024 [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <lib/support/BytesCircularBuffer.h>
#include <algorithm>
#include <limits>
#include <nlassert.h>
#include <string.h>
#include <lib/support/CodeUtils.h>
namespace chip {
size_t BytesCircularBuffer::Advance(size_t dataLocation, size_t amount) const
{
dataLocation += amount;
if (dataLocation >= mCapacity)
dataLocation -= mCapacity;
return dataLocation;
}
void BytesCircularBuffer::Read(uint8_t * dest, size_t length, size_t offset) const
{
// length is an instance of SizeType
// offset is maximized at sizeof(SizeType) for all use cases.
static_assert(std::numeric_limits<SizeType>::max() < std::numeric_limits<size_t>::max() - sizeof(SizeType),
"SizeType too large, may cause overflow");
VerifyOrDie(StorageUsed() >= offset + length);
size_t start = Advance(mDataStart, offset);
size_t firstPiece = std::min(mCapacity - start, length);
size_t secondPiece = length - firstPiece;
::memcpy(dest, mStorage + start, firstPiece);
::memcpy(dest + firstPiece, mStorage, secondPiece);
}
void BytesCircularBuffer::Write(const uint8_t * source, size_t length)
{
// Always reserve 1 byte to prevent mDataStart == mDataEnd because then it would be
// ambiguous whether we have 0 bytes or mCapacity bytes stored.
VerifyOrDie(StorageAvailable() - 1 >= length);
size_t firstPiece = std::min(mCapacity - mDataEnd, length);
size_t secondPiece = length - firstPiece;
::memcpy(mStorage + mDataEnd, source, firstPiece);
::memcpy(mStorage, source + firstPiece, secondPiece);
mDataEnd = Advance(mDataEnd, length);
}
void BytesCircularBuffer::Drop(size_t length)
{
VerifyOrDie(StorageUsed() >= length);
mDataStart = Advance(mDataStart, length);
}
size_t BytesCircularBuffer::StorageAvailable() const
{
return mCapacity - StorageUsed();
}
size_t BytesCircularBuffer::StorageUsed() const
{
if (mDataStart <= mDataEnd)
{
return mDataEnd - mDataStart;
}
return mCapacity + mDataEnd - mDataStart;
}
CHIP_ERROR BytesCircularBuffer::Push(const ByteSpan & payload)
{
size_t length = payload.size();
if (length > std::numeric_limits<SizeType>::max())
return CHIP_ERROR_INVALID_ARGUMENT;
static_assert(std::numeric_limits<SizeType>::max() < std::numeric_limits<size_t>::max() - (sizeof(SizeType) + 1),
"SizeType too large, may cause overflow");
size_t storageNeed = length + sizeof(SizeType) + 1;
if (storageNeed > mCapacity)
return CHIP_ERROR_INVALID_ARGUMENT;
// Free up space until there is enough space.
while (storageNeed > StorageAvailable())
{
VerifyOrDie(Pop() == CHIP_NO_ERROR);
}
SizeType size = static_cast<SizeType>(length);
Write(reinterpret_cast<uint8_t *>(&size), sizeof(size));
Write(payload.data(), length);
return CHIP_NO_ERROR;
}
CHIP_ERROR BytesCircularBuffer::Push(const ByteSpan & payload1, const ByteSpan & payload2)
{
size_t length = payload1.size() + payload2.size();
if (length > std::numeric_limits<SizeType>::max())
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
static_assert(std::numeric_limits<SizeType>::max() < std::numeric_limits<size_t>::max() - (sizeof(SizeType) + 1),
"SizeType too large, may cause overflow");
size_t storageNeed = length + sizeof(SizeType) + 1;
if (storageNeed > mCapacity)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
// Free up space until there is enough space.
while (storageNeed > StorageAvailable())
{
VerifyOrDie(Pop() == CHIP_NO_ERROR);
}
SizeType size = static_cast<SizeType>(length);
Write(reinterpret_cast<uint8_t *>(&size), sizeof(size));
Write(payload1.data(), payload1.size());
Write(payload2.data(), payload2.size());
return CHIP_NO_ERROR;
}
CHIP_ERROR BytesCircularBuffer::Pop()
{
if (IsEmpty())
return CHIP_ERROR_INCORRECT_STATE;
size_t length = GetFrontSize();
Drop(sizeof(SizeType));
Drop(length);
return CHIP_NO_ERROR;
}
bool BytesCircularBuffer::IsEmpty() const
{
return StorageUsed() == 0;
}
size_t BytesCircularBuffer::GetFrontSize() const
{
if (IsEmpty())
return 0;
SizeType length;
Read(reinterpret_cast<uint8_t *>(&length), sizeof(length), 0 /* offset */);
return length;
}
CHIP_ERROR BytesCircularBuffer::ReadFront(MutableByteSpan & dest) const
{
if (IsEmpty())
return CHIP_ERROR_INCORRECT_STATE;
size_t length = GetFrontSize();
if (dest.size() < length)
return CHIP_ERROR_INVALID_ARGUMENT;
dest = dest.SubSpan(0, length);
Read(dest.data(), length, sizeof(SizeType) /* offset */);
return CHIP_NO_ERROR;
}
} // namespace chip