/* * * 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 #include #include #include #include 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::max() < std::numeric_limits::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::max()) return CHIP_ERROR_INVALID_ARGUMENT; static_assert(std::numeric_limits::max() < std::numeric_limits::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(length); Write(reinterpret_cast(&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::max()) { return CHIP_ERROR_INVALID_ARGUMENT; } static_assert(std::numeric_limits::max() < std::numeric_limits::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(length); Write(reinterpret_cast(&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(&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