/* * Copyright (C) 2014 Pavel Kirienko */ #ifndef UAVCAN_DYNAMIC_MEMORY_HPP_INCLUDED #define UAVCAN_DYNAMIC_MEMORY_HPP_INCLUDED #include #include #include #include #include #include #include namespace uavcan { /** * This interface is used by other library components that need dynamic memory. */ class UAVCAN_EXPORT IPoolAllocator { public: virtual ~IPoolAllocator() { } virtual void* allocate(std::size_t size) = 0; virtual void deallocate(const void* ptr) = 0; virtual bool isInPool(const void* ptr) const = 0; virtual std::size_t getBlockSize() const = 0; virtual std::size_t getNumBlocks() const = 0; }; /** * Pool manager contains multiple pool allocators of different block sizes and * finds the most suitable allocator for every allocation request. */ template class UAVCAN_EXPORT PoolManager : public IPoolAllocator, Noncopyable { IPoolAllocator* pools_[MaxPools]; static int qsortComparePoolAllocators(const void* raw_a, const void* raw_b) { const IPoolAllocator* const a = *static_cast(raw_a); const IPoolAllocator* const b = *static_cast(raw_b); const std::size_t a_size = a ? a->getBlockSize() : NumericTraits::max(); const std::size_t b_size = b ? b->getBlockSize() : NumericTraits::max(); if (a_size != b_size) { return (a_size > b_size) ? 1 : -1; } return 0; } public: PoolManager() { (void)std::memset(pools_, 0, sizeof(pools_)); } bool addPool(IPoolAllocator* pool); virtual void* allocate(std::size_t size); virtual void deallocate(const void* ptr); virtual bool isInPool(const void* ptr) const; virtual std::size_t getBlockSize() const; virtual std::size_t getNumBlocks() const; }; /** * Classic implementation of a pool allocator (Meyers). */ template class UAVCAN_EXPORT PoolAllocator : public IPoolAllocator, Noncopyable { union Node { uint8_t data[BlockSize]; Node* next; }; Node* free_list_; union { uint8_t bytes[PoolSize]; long double _aligner1; long long _aligner2; Node _aligner3; } pool_; public: static const unsigned NumBlocks = unsigned(PoolSize / BlockSize); PoolAllocator(); virtual void* allocate(std::size_t size); virtual void deallocate(const void* ptr); virtual bool isInPool(const void* ptr) const; virtual std::size_t getBlockSize() const { return BlockSize; } virtual std::size_t getNumBlocks() const { return NumBlocks; } unsigned getNumFreeBlocks() const; unsigned getNumUsedBlocks() const { return NumBlocks - getNumFreeBlocks(); } }; /** * Limits the maximum number of blocks that can be allocated in a given allocator. */ class LimitedPoolAllocator : public IPoolAllocator { IPoolAllocator& allocator_; const std::size_t max_blocks_; std::size_t used_blocks_; public: LimitedPoolAllocator(IPoolAllocator& allocator, std::size_t max_blocks) : allocator_(allocator) , max_blocks_(max_blocks) , used_blocks_(0) { UAVCAN_ASSERT(max_blocks_ > 0); } virtual void* allocate(std::size_t size); virtual void deallocate(const void* ptr); virtual bool isInPool(const void* ptr) const; virtual std::size_t getBlockSize() const; virtual std::size_t getNumBlocks() const; }; // ---------------------------------------------------------------------------- /* * PoolManager<> */ template bool PoolManager::addPool(IPoolAllocator* pool) { UAVCAN_ASSERT(pool); bool retval = false; for (unsigned i = 0; i < MaxPools; i++) { UAVCAN_ASSERT(pools_[i] != pool); if (pools_[i] == NULL || pools_[i] == pool) { pools_[i] = pool; retval = true; break; } } // We need to keep the pools in order, so that smallest blocks go first qsort(pools_, MaxPools, sizeof(IPoolAllocator*), &PoolManager::qsortComparePoolAllocators); return retval; } template void* PoolManager::allocate(std::size_t size) { for (unsigned i = 0; i < MaxPools; i++) { if (pools_[i] == NULL) { break; } void* const pmem = pools_[i]->allocate(size); if (pmem != NULL) { return pmem; } } return NULL; } template void PoolManager::deallocate(const void* ptr) { for (unsigned i = 0; i < MaxPools; i++) { if (pools_[i] == NULL) { UAVCAN_ASSERT(0); break; } if (pools_[i]->isInPool(ptr)) { pools_[i]->deallocate(ptr); break; } } } template bool PoolManager::isInPool(const void* ptr) const { for (unsigned i = 0; i < MaxPools; i++) { if (pools_[i] == NULL) { break; } if (pools_[i]->isInPool(ptr)) { return true; } } return false; } template std::size_t PoolManager::getBlockSize() const { return 0; } template std::size_t PoolManager::getNumBlocks() const { std::size_t ret = 0; for (unsigned i = 0; i < MaxPools; i++) { if (pools_[i] == NULL) { break; } ret += pools_[i]->getNumBlocks(); } return ret; } /* * PoolAllocator<> */ template PoolAllocator::PoolAllocator() : free_list_(reinterpret_cast(pool_.bytes)) { (void)std::memset(pool_.bytes, 0, PoolSize); for (unsigned i = 0; (i + 1) < (NumBlocks - 1 + 1); i++) // -Werror=type-limits { // coverity[dead_error_line : FALSE] free_list_[i].next = free_list_ + i + 1; } free_list_[NumBlocks - 1].next = NULL; } template void* PoolAllocator::allocate(std::size_t size) { if (free_list_ == NULL || size > BlockSize) { return NULL; } void* pmem = free_list_; free_list_ = free_list_->next; return pmem; } template void PoolAllocator::deallocate(const void* ptr) { if (ptr == NULL) { return; } Node* p = static_cast(const_cast(ptr)); p->next = free_list_; free_list_ = p; } template bool PoolAllocator::isInPool(const void* ptr) const { return ptr >= pool_.bytes && ptr < (pool_.bytes + PoolSize); } template unsigned PoolAllocator::getNumFreeBlocks() const { unsigned num = 0; Node* p = free_list_; while (p) { num++; UAVCAN_ASSERT(num <= NumBlocks); p = p->next; } return num; } } #endif // UAVCAN_DYNAMIC_MEMORY_HPP_INCLUDED