/* * * Copyright (c) 2020 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. */ /** * @file * Defines scoped auto-free buffers for CHIP. * */ #pragma once #include #include #include #include namespace chip { namespace Platform { namespace Impl { /** * Represents a memory buffer that is auto-freed in the destructor. * * This class uses void* underneath on purpose (rather than a unique_ptr like * 'Type') and uses templated type on Ptr(). This is to avoid template explosion * when the buffers are used for different types - only one implementation of * the class will be stored in flash. */ template class ScopedMemoryBufferBase { public: ScopedMemoryBufferBase() {} ScopedMemoryBufferBase(const ScopedMemoryBufferBase &) = delete; ScopedMemoryBufferBase & operator=(const ScopedMemoryBufferBase & other) = delete; ScopedMemoryBufferBase(ScopedMemoryBufferBase && other) { *this = std::move(other); } ScopedMemoryBufferBase & operator=(ScopedMemoryBufferBase && other) { if (this != &other) { mBuffer = other.mBuffer; other.mBuffer = nullptr; } return *this; } ~ScopedMemoryBufferBase() { Free(); } /** Check if a buffer is valid */ explicit operator bool() const { return mBuffer != nullptr; } bool operator!() const { return mBuffer == nullptr; } /** Release memory used */ void Free() { if (mBuffer == nullptr) { return; } Impl::MemoryFree(mBuffer); mBuffer = nullptr; } protected: void * Ptr() { return mBuffer; } const void * Ptr() const { return mBuffer; } /** * Releases the underlying buffer. * * The buffer stops being managed and will not be auto-freed. */ CHECK_RETURN_VALUE void * Release() { void * buffer = mBuffer; mBuffer = nullptr; return buffer; } void Alloc(size_t size) { Free(); mBuffer = Impl::MemoryAlloc(size); } void Calloc(size_t elementCount, size_t elementSize) { Free(); mBuffer = Impl::MemoryCalloc(elementCount, elementSize); } private: void * mBuffer = nullptr; }; /** * Helper class that forwards memory management tasks to Platform::Memory* calls. */ class PlatformMemoryManagement { public: static void MemoryFree(void * p) { chip::Platform::MemoryFree(p); } static void * MemoryAlloc(size_t size) { return chip::Platform::MemoryAlloc(size); } static void * MemoryCalloc(size_t num, size_t size) { return chip::Platform::MemoryCalloc(num, size); } }; } // namespace Impl /** * Represents a memory buffer allocated using chip::Platform::Memory*Alloc * methods. * * Use for RAII to auto-free after use. */ template class ScopedMemoryBuffer : public Impl::ScopedMemoryBufferBase { friend class Impl::ScopedMemoryBufferBase; public: using Base = Impl::ScopedMemoryBufferBase; static_assert(std::is_trivially_destructible::value, "Destructors won't get run"); T * Get() { return static_cast(Base::Ptr()); } T & operator[](size_t index) { return Get()[index]; } const T * Get() const { return static_cast(Base::Ptr()); } const T & operator[](size_t index) const { return Get()[index]; } /** * Releases the underlying buffer. * * The buffer stops being managed and will not be auto-freed. */ CHECK_RETURN_VALUE T * Release() { return static_cast(Base::Release()); } ScopedMemoryBuffer & Calloc(size_t elementCount) { Base::Calloc(elementCount, sizeof(T)); ExecuteConstructors(elementCount); return *this; } ScopedMemoryBuffer & Alloc(size_t elementCount) { Base::Alloc(elementCount * sizeof(T)); ExecuteConstructors(elementCount); return *this; } private: template ::value, int> = 0> void ExecuteConstructors(size_t elementCount) { // Do nothing if our type is trivial. In particular, if we are a buffer // of integers, we should not go zero-initializing them here: either // caller wants that and called Calloc(), or it doesn't and we shouldn't // do it. } template ::value, int> = 0> void ExecuteConstructors(size_t elementCount) { T * elementPtr = Get(); if (elementPtr == nullptr) { // Alloc failed, don't bother. return; } for (size_t i = 0; i < elementCount; ++i) { new (&elementPtr[i]) T(); } } }; /** * Represents a memory buffer with buffer size allocated using chip::Platform::Memory*Alloc * methods. * * Use for RAII to auto-free after use. */ template class ScopedMemoryBufferWithSize : public ScopedMemoryBuffer { public: ScopedMemoryBufferWithSize() {} ScopedMemoryBufferWithSize(ScopedMemoryBufferWithSize && other) { *this = std::move(other); } ScopedMemoryBufferWithSize & operator=(ScopedMemoryBufferWithSize && other) { if (this != &other) { mCount = other.mCount; other.mCount = 0; } ScopedMemoryBuffer::operator=(std::move(other)); return *this; } ~ScopedMemoryBufferWithSize() { mCount = 0; } // return the size as count of elements inline size_t AllocatedSize() const { return mCount; } void Free() { mCount = 0; ScopedMemoryBuffer::Free(); } /** * Releases the underlying buffer. * * The buffer stops being managed and will not be auto-freed. */ CHECK_RETURN_VALUE T * Release() { T * buffer = ScopedMemoryBuffer::Release(); mCount = 0; return buffer; } ScopedMemoryBufferWithSize & Calloc(size_t elementCount) { ScopedMemoryBuffer::Calloc(elementCount); if (this->Get() != nullptr) { mCount = elementCount; } return *this; } ScopedMemoryBufferWithSize & Alloc(size_t elementCount) { ScopedMemoryBuffer::Alloc(elementCount); if (this->Get() != nullptr) { mCount = elementCount; } return *this; } private: size_t mCount = 0; }; } // namespace Platform } // namespace chip