#ifndef _DEPOOLARRAY_HPP #define _DEPOOLARRAY_HPP /*------------------------------------------------------------------------- * drawElements C++ Base Library * ----------------------------- * * Copyright 2014 The Android Open Source Project * * 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 * \brief Array template backed by memory pool. *//*--------------------------------------------------------------------*/ #include "deDefs.hpp" #include "deMemPool.hpp" #include "deInt32.h" #include namespace de { //! Self-test for PoolArray void PoolArray_selfTest(void); template class PoolArrayConstIterator; template class PoolArrayIterator; /*--------------------------------------------------------------------*//*! * \brief Array template backed by memory pool * * \note Memory in PoolArray is not contiguous so pointer arithmetic * to access next element(s) doesn't work. * \todo [2013-02-11 pyry] Make elements per page template argument. *//*--------------------------------------------------------------------*/ template sizeof(void *) ? (uint32_t)sizeof(void *) : (uint32_t)sizeof(T))> class PoolArray { public: typedef PoolArrayIterator Iterator; typedef PoolArrayConstIterator ConstIterator; typedef Iterator iterator; typedef ConstIterator const_iterator; explicit PoolArray(MemPool *pool); PoolArray(MemPool *pool, const PoolArray &other); ~PoolArray(void); void clear(void); void reserve(uintptr_t capacity); void resize(uintptr_t size); void resize(uintptr_t size, const T &value); uintptr_t size(void) const { return m_numElements; } bool empty(void) const { return m_numElements == 0; } void pushBack(const T &value); T popBack(void); const T &at(intptr_t ndx) const { return *getPtr(ndx); } T &at(intptr_t ndx) { return *getPtr(ndx); } const T &operator[](intptr_t ndx) const { return at(ndx); } T &operator[](intptr_t ndx) { return at(ndx); } Iterator begin(void) { return Iterator(this, 0); } Iterator end(void) { return Iterator(this, (intptr_t)m_numElements); } ConstIterator begin(void) const { return ConstIterator(this, 0); } ConstIterator end(void) const { return ConstIterator(this, (intptr_t)m_numElements); } const T &front(void) const { return at(0); } T &front(void) { return at(0); } const T &back(void) const { return at(m_numElements - 1); } T &back(void) { return at(m_numElements - 1); } private: enum { ELEMENTS_PER_PAGE_LOG2 = 4 //!< 16 elements per page. }; PoolArray(const PoolArray &other); // \note Default copy ctor is not allowed, use PoolArray(pool, copy) instead. T *getPtr(intptr_t ndx) const; MemPool *m_pool; uintptr_t m_numElements; //!< Number of elements in the array. uintptr_t m_capacity; //!< Number of allocated elements in the array. uintptr_t m_pageTableCapacity; //!< Size of the page table. void **m_pageTable; //!< Pointer to the page table. }; template class PoolArrayIteratorBase { public: PoolArrayIteratorBase(uintptr_t ndx) : m_ndx(ndx) { } ~PoolArrayIteratorBase(void) { } intptr_t getNdx(void) const throw() { return m_ndx; } protected: intptr_t m_ndx; }; template class PoolArrayConstIterator : public PoolArrayIteratorBase { public: PoolArrayConstIterator(void); PoolArrayConstIterator(const PoolArray *array, intptr_t ndx); PoolArrayConstIterator(const PoolArrayIterator &iterator); ~PoolArrayConstIterator(void); // \note Default assignment and copy-constructor are auto-generated. const PoolArray *getArray(void) const throw() { return m_array; } // De-reference operators. const T *operator->(void) const throw() { return &(*m_array)[this->m_ndx]; } const T &operator*(void) const throw() { return (*m_array)[this->m_ndx]; } const T &operator[](uintptr_t offs) const throw() { return (*m_array)[this->m_ndx + offs]; } // Pre-increment and decrement. PoolArrayConstIterator &operator++(void) { this->m_ndx += 1; return *this; } PoolArrayConstIterator &operator--(void) { this->m_ndx -= 1; return *this; } // Post-increment and decrement. PoolArrayConstIterator operator++(int) { PoolArrayConstIterator copy(*this); this->m_ndx += 1; return copy; } PoolArrayConstIterator operator--(int) { PoolArrayConstIterator copy(*this); this->m_ndx -= 1; return copy; } // Compound assignment. PoolArrayConstIterator &operator+=(intptr_t offs) { this->m_ndx += offs; return *this; } PoolArrayConstIterator &operator-=(intptr_t offs) { this->m_ndx -= offs; return *this; } // Assignment from non-const. PoolArrayConstIterator &operator=(const PoolArrayIterator &iter); private: const PoolArray *m_array; }; template class PoolArrayIterator : public PoolArrayIteratorBase { public: PoolArrayIterator(void); PoolArrayIterator(PoolArray *array, intptr_t ndx); ~PoolArrayIterator(void); // \note Default assignment and copy-constructor are auto-generated. PoolArray *getArray(void) const throw() { return m_array; } // De-reference operators. T *operator->(void) const throw() { return &(*m_array)[this->m_ndx]; } T &operator*(void) const throw() { return (*m_array)[this->m_ndx]; } T &operator[](uintptr_t offs) const throw() { return (*m_array)[this->m_ndx + offs]; } // Pre-increment and decrement. PoolArrayIterator &operator++(void) { this->m_ndx += 1; return *this; } PoolArrayIterator &operator--(void) { this->m_ndx -= 1; return *this; } // Post-increment and decrement. PoolArrayIterator operator++(int) { PoolArrayIterator copy(*this); this->m_ndx += 1; return copy; } PoolArrayIterator operator--(int) { PoolArrayIterator copy(*this); this->m_ndx -= 1; return copy; } // Compound assignment. PoolArrayIterator &operator+=(intptr_t offs) { this->m_ndx += offs; return *this; } PoolArrayIterator &operator-=(intptr_t offs) { this->m_ndx -= offs; return *this; } private: PoolArray *m_array; }; // Initializer helper for array. template struct PoolArrayElement { static void constructDefault(void *ptr) { new (ptr) T(); } //!< Called for non-initialized memory. static void constructCopy(void *ptr, const T &val) { new (ptr) T(val); } //!< Called for non-initialized memory when initial value is provided. static void destruct(T *ptr) { ptr->~T(); } //!< Called when element is destructed. }; // Specialization for basic types. #define DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(TYPE) \ template <> \ struct PoolArrayElement \ { \ static void constructDefault(void *) \ { \ } \ static void constructCopy(void *ptr, TYPE val) \ { \ *(TYPE *)ptr = val; \ } \ static void destruct(TYPE *) \ { \ } \ } DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(uint8_t); DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(uint16_t); DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(uint32_t); DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(uint64_t); DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(int8_t); DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(int16_t); DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(int32_t); DE_SPECIALIZE_POOL_ARRAY_ELEMENT_BASIC_TYPE(int64_t); // PoolArray implementation. template PoolArray::PoolArray(MemPool *pool) : m_pool(pool) , m_numElements(0) , m_capacity(0) , m_pageTableCapacity(0) , m_pageTable(0) { DE_ASSERT(deIsPowerOfTwo32(Alignment)); } template PoolArray::~PoolArray(void) { // Clear resets values to T() clear(); } template inline void PoolArray::clear(void) { resize(0); } template inline void PoolArray::resize(uintptr_t newSize) { if (newSize < m_numElements) { // Destruct elements that are no longer active. for (uintptr_t ndx = newSize; ndx < m_numElements; ndx++) PoolArrayElement::destruct(getPtr(ndx)); m_numElements = newSize; } else if (newSize > m_numElements) { uintptr_t prevSize = m_numElements; reserve(newSize); m_numElements = newSize; // Fill new elements with default values for (uintptr_t ndx = prevSize; ndx < m_numElements; ndx++) PoolArrayElement::constructDefault(getPtr(ndx)); } } template inline void PoolArray::resize(uintptr_t newSize, const T &value) { if (newSize < m_numElements) resize(newSize); // value is not used else if (newSize > m_numElements) { uintptr_t prevSize = m_numElements; reserve(newSize); m_numElements = newSize; // Fill new elements with copies of value for (uintptr_t ndx = prevSize; ndx < m_numElements; ndx++) PoolArrayElement::constructCopy(getPtr(ndx), value); } } template inline void PoolArray::reserve(uintptr_t capacity) { if (capacity >= m_capacity) { void *oldPageTable = DE_NULL; uintptr_t oldPageTableSize = 0; uintptr_t newCapacity = (uintptr_t)deAlignPtr((void *)capacity, 1 << ELEMENTS_PER_PAGE_LOG2); uintptr_t reqPageTableCapacity = newCapacity >> ELEMENTS_PER_PAGE_LOG2; if (m_pageTableCapacity < reqPageTableCapacity) { uintptr_t newPageTableCapacity = max(2 * m_pageTableCapacity, reqPageTableCapacity); void **newPageTable = (void **)m_pool->alloc(newPageTableCapacity * sizeof(void *)); uintptr_t i; for (i = 0; i < m_pageTableCapacity; i++) newPageTable[i] = m_pageTable[i]; for (; i < newPageTableCapacity; i++) newPageTable[i] = DE_NULL; // Grab information about old page table for recycling purposes. oldPageTable = m_pageTable; oldPageTableSize = m_pageTableCapacity * sizeof(T *); m_pageTable = newPageTable; m_pageTableCapacity = newPageTableCapacity; } // Allocate new pages. { uintptr_t elementSize = (uintptr_t)deAlignPtr((void *)(uintptr_t)sizeof(T), Alignment); uintptr_t pageAllocSize = elementSize << ELEMENTS_PER_PAGE_LOG2; uintptr_t pageTableNdx = m_capacity >> ELEMENTS_PER_PAGE_LOG2; // Allocate new pages from recycled old page table. for (;;) { void *newPage = deAlignPtr(oldPageTable, Alignment); uintptr_t alignPadding = (uintptr_t)newPage - (uintptr_t)oldPageTable; if (oldPageTableSize < pageAllocSize + alignPadding) break; // No free space for alloc + alignment. DE_ASSERT(m_pageTableCapacity > pageTableNdx); DE_ASSERT(!m_pageTable[pageTableNdx]); m_pageTable[pageTableNdx++] = newPage; oldPageTable = (void *)((uint8_t *)newPage + pageAllocSize); oldPageTableSize -= pageAllocSize + alignPadding; } // Allocate the rest of the needed pages from the pool. for (; pageTableNdx < reqPageTableCapacity; pageTableNdx++) { DE_ASSERT(!m_pageTable[pageTableNdx]); m_pageTable[pageTableNdx] = m_pool->alignedAlloc(pageAllocSize, Alignment); } m_capacity = pageTableNdx << ELEMENTS_PER_PAGE_LOG2; DE_ASSERT(m_capacity >= newCapacity); } } } template inline void PoolArray::pushBack(const T &value) { resize(size() + 1); at(size() - 1) = value; } template inline T PoolArray::popBack(void) { T val = at(size() - 1); resize(size() - 1); return val; } template inline T *PoolArray::getPtr(intptr_t ndx) const { DE_ASSERT(inBounds(ndx, 0, (intptr_t)m_numElements)); uintptr_t pageNdx = ((uintptr_t)ndx >> ELEMENTS_PER_PAGE_LOG2); uintptr_t subNdx = (uintptr_t)ndx & ((1 << ELEMENTS_PER_PAGE_LOG2) - 1); uintptr_t elemSize = (uintptr_t)deAlignPtr((void *)(uintptr_t)sizeof(T), Alignment); T *ptr = (T *)((uint8_t *)m_pageTable[pageNdx] + (subNdx * elemSize)); DE_ASSERT(deIsAlignedPtr(ptr, Alignment)); return ptr; } // PoolArrayIteratorBase implementation template inline bool operator==(const PoolArrayIteratorBase &a, const PoolArrayIteratorBase &b) { // \todo [2013-02-08 pyry] Compare array ptr. return a.getNdx() == b.getNdx(); } template inline bool operator!=(const PoolArrayIteratorBase &a, const PoolArrayIteratorBase &b) { // \todo [2013-02-08 pyry] Compare array ptr. return a.getNdx() != b.getNdx(); } template inline bool operator<(const PoolArrayIteratorBase &a, const PoolArrayIteratorBase &b) { return a.getNdx() < b.getNdx(); } template inline bool operator>(const PoolArrayIteratorBase &a, const PoolArrayIteratorBase &b) { return a.getNdx() > b.getNdx(); } template inline bool operator<=(const PoolArrayIteratorBase &a, const PoolArrayIteratorBase &b) { return a.getNdx() <= b.getNdx(); } template inline bool operator>=(const PoolArrayIteratorBase &a, const PoolArrayIteratorBase &b) { return a.getNdx() >= b.getNdx(); } // PoolArrayConstIterator implementation template inline PoolArrayConstIterator::PoolArrayConstIterator(void) : PoolArrayIteratorBase(0) , m_array(DE_NULL) { } template inline PoolArrayConstIterator::PoolArrayConstIterator(const PoolArray *array, intptr_t ndx) : PoolArrayIteratorBase(ndx) , m_array(array) { } template inline PoolArrayConstIterator::PoolArrayConstIterator(const PoolArrayIterator &iter) : PoolArrayIteratorBase(iter) , m_array(iter.getArray()) { } template inline PoolArrayConstIterator::~PoolArrayConstIterator(void) { } // Arithmetic operators. template inline PoolArrayConstIterator operator+(const PoolArrayConstIterator &iter, intptr_t offs) { return PoolArrayConstIterator(iter->getArray(), iter->getNdx() + offs); } template inline PoolArrayConstIterator operator+(uintptr_t offs, const PoolArrayConstIterator &iter) { return PoolArrayConstIterator(iter->getArray(), iter->getNdx() + offs); } template PoolArrayConstIterator operator-(const PoolArrayConstIterator &iter, intptr_t offs) { return PoolArrayConstIterator(iter.getArray(), iter.getNdx() - offs); } template intptr_t operator-(const PoolArrayConstIterator &iter, const PoolArrayConstIterator &other) { return iter.getNdx() - other.getNdx(); } // PoolArrayIterator implementation. template inline PoolArrayIterator::PoolArrayIterator(void) : PoolArrayIteratorBase(0) , m_array(DE_NULL) { } template inline PoolArrayIterator::PoolArrayIterator(PoolArray *array, intptr_t ndx) : PoolArrayIteratorBase(ndx) , m_array(array) { } template inline PoolArrayIterator::~PoolArrayIterator(void) { } // Arithmetic operators. template inline PoolArrayIterator operator+(const PoolArrayIterator &iter, intptr_t offs) { return PoolArrayIterator(iter.getArray(), iter.getNdx() + offs); } template inline PoolArrayIterator operator+(uintptr_t offs, const PoolArrayIterator &iter) { return PoolArrayIterator(iter.getArray(), iter.getNdx() + offs); } template PoolArrayIterator operator-(const PoolArrayIterator &iter, intptr_t offs) { return PoolArrayIterator(iter.getArray(), iter.getNdx() - offs); } template intptr_t operator-(const PoolArrayIterator &iter, const PoolArrayIterator &other) { return iter.getNdx() - other.getNdx(); } } // namespace de // std::iterator_traits specializations namespace std { template struct iterator_traits> { typedef intptr_t difference_type; typedef T value_type; typedef const T *pointer; typedef const T &reference; typedef random_access_iterator_tag iterator_category; }; template struct iterator_traits> { typedef intptr_t difference_type; typedef T value_type; typedef T *pointer; typedef T &reference; typedef random_access_iterator_tag iterator_category; }; } // namespace std #endif // _DEPOOLARRAY_HPP