/* Copyright (c) 2003-2006 MySQL AB Use is subject to license terms This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ #ifndef ARRAY_POOL_HPP #define ARRAY_POOL_HPP #include #include "ndbd_malloc.hpp" #include #include "Pool.hpp" #include #include #include #include template class Array; /** * Template class used for implementing an * pool of object (in an array with a free list) */ template class ArrayPool { public: ArrayPool(); ~ArrayPool(); /** * Set the size of the pool * * Note, can currently only be called once */ bool setSize(Uint32 noOfElements, bool align = false, bool exit_on_error = true, bool guard = true, Uint32 paramId = 0); bool set(T*, Uint32 cnt, bool align = false); void clear() { theArray = 0; } inline Uint32 getNoOfFree() const { return noOfFree; } inline Uint32 getSize() const { return size; } /** * Update p value for ptr according to i value */ void getPtr(Ptr &); void getPtr(ConstPtr &) const; void getPtr(Ptr &, bool CrashOnBoundaryError); void getPtr(ConstPtr &, bool CrashOnBoundaryError) const; /** * Get pointer for i value */ T * getPtr(Uint32 i); const T * getConstPtr(Uint32 i) const; T * getPtr(Uint32 i, bool CrashOnBoundaryError); const T * getConstPtr(Uint32 i, bool CrashOnBoundaryError) const; /** * Update p & i value for ptr according to i value */ void getPtr(Ptr &, Uint32 i); void getPtr(ConstPtr &, Uint32 i) const; void getPtr(Ptr &, Uint32 i, bool CrashOnBoundaryError); void getPtr(ConstPtr &, Uint32 i, bool CrashOnBoundaryError) const; /** * Allocate an object from pool - update Ptr * * Return i */ bool seize(Ptr &); /** * Allocate object i from pool - update Ptr */ bool seizeId(Ptr &, Uint32 i); /** * Check if i is allocated. */ bool findId(Uint32 i) const; /** * Return an object to pool */ void release(Uint32 i); /** * Return an object to pool */ void release(Ptr &); #ifdef ARRAY_GUARD /** * Checks if i is a correct seized record * * @note Since this is either an expensive method, * or needs bitmask stuff, this method is only * recommended for debugging. * */ bool isSeized(Uint32 i) const { if (i>=size) return false; return BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i); } #endif protected: friend class Array; /** * Allocate n consecutive object from pool * return base */ Uint32 seizeN(Uint32 n); /** * Deallocate n consecutive object to pool * starting from base */ void releaseN(Uint32 base, Uint32 n); public: /** * Release a singel linked list in o(1) * @param first i-value of first element in list * @param last i-value of last element in list * @note nextPool must be used as next pointer in list */ void releaseList(Uint32 n, Uint32 first, Uint32 last); //private: #ifdef DEBUG Uint32 getNoOfFree2() const { Uint32 c2 = size; for(Uint32 i = 0; i<((size + 31)>> 5); i++){ Uint32 w = theAllocatedBitmask[i]; for(Uint32 j = 0; j<32; j++){ if((w & 1) == 1){ c2--; } w >>= 1; } } return c2; } Uint32 getNoOfFree3() const { Uint32 c = 0; Ptr p; p.i = firstFree; while(p.i != RNIL){ c++; p.p = &theArray[p.i]; p.i = p.p->next; } return c; } #endif protected: Uint32 firstFree; Uint32 size; Uint32 noOfFree; T * theArray; void * alloc_ptr; Uint32 bitmaskSz; Uint32 *theAllocatedBitmask; }; template inline ArrayPool::ArrayPool(){ firstFree = RNIL; size = 0; noOfFree = 0; theArray = 0; alloc_ptr = 0; #ifdef ARRAY_GUARD theAllocatedBitmask = 0; #endif } template inline ArrayPool::~ArrayPool(){ if(theArray != 0){ ndbd_free(alloc_ptr, size * sizeof(T)); theArray = 0; alloc_ptr = 0; #ifdef ARRAY_GUARD if (theAllocatedBitmask) delete []theAllocatedBitmask; theAllocatedBitmask = 0; #endif } } /** * Set the size of the pool * * Note, can currently only be called once */ template inline bool ArrayPool::setSize(Uint32 noOfElements, bool align, bool exit_on_error, bool guard, Uint32 paramId){ if(size == 0){ if(noOfElements == 0) return true; Uint64 real_size = (Uint64)noOfElements * sizeof(T); size_t req_size = (size_t)real_size; Uint64 real_size_align = real_size + sizeof(T); size_t req_size_align = (size_t)real_size_align; if(align) { if((Uint64)req_size_align == real_size_align && req_size_align > 0) alloc_ptr = ndbd_malloc(req_size_align); UintPtr p = (UintPtr)alloc_ptr; UintPtr mod = p % sizeof(T); if (mod) { p += sizeof(T) - mod; } theArray = (T *)p; } else if((Uint64)req_size == real_size && req_size > 0) theArray = (T *)(alloc_ptr = ndbd_malloc(req_size)); if(theArray == 0) { char errmsg[255] = "ArrayPool::setSize malloc failed"; struct ndb_mgm_param_info param_info; size_t size = sizeof(ndb_mgm_param_info); if (!exit_on_error) return false; if(0 != paramId && 0 == ndb_mgm_get_db_parameter_info(paramId, ¶m_info, &size)) { BaseString::snprintf(errmsg, sizeof(errmsg), "Malloc memory for %s failed", param_info.m_name); } ErrorReporter::handleAssert(errmsg, __FILE__, __LINE__, NDBD_EXIT_MEMALLOC); return false; // not reached } size = noOfElements; noOfFree = noOfElements; /** * Set next pointers */ T * t = &theArray[0]; for(Uint32 i = 0; inextPool = (i + 1); t++; } theArray[size-1].nextPool = RNIL; firstFree = 0; #ifdef ARRAY_GUARD if (guard) { bitmaskSz = (noOfElements + 31) >> 5; theAllocatedBitmask = new Uint32[bitmaskSz]; BitmaskImpl::clear(bitmaskSz, theAllocatedBitmask); } #endif return true; } if (!exit_on_error) return false; ErrorReporter::handleAssert("ArrayPool::setSize called twice", __FILE__, __LINE__); return false; // not reached } template inline bool ArrayPool::set(T* ptr, Uint32 cnt, bool align){ if (size == 0) { alloc_ptr = ptr; if(align) { UintPtr p = (UintPtr)alloc_ptr; UintPtr mod = p % sizeof(T); if (mod) { p += sizeof(T) - mod; cnt --; } theArray = (T *)p; } else { theArray = (T *)alloc_ptr; } size = cnt; noOfFree = 0; return true; } ErrorReporter::handleAssert("ArrayPool::set called twice", __FILE__, __LINE__); return false; // not reached } template inline void ArrayPool::getPtr(Ptr & ptr){ Uint32 i = ptr.i; if(likely (i < size)){ ptr.p = &theArray[i]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } #endif } else { ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } } template inline void ArrayPool::getPtr(ConstPtr & ptr) const { Uint32 i = ptr.i; if(likely(i < size)){ ptr.p = &theArray[i]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } #endif } else { ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } } template inline void ArrayPool::getPtr(Ptr & ptr, Uint32 i){ ptr.i = i; if(likely(i < size)){ ptr.p = &theArray[i]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } #endif } else { ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } } template inline void ArrayPool::getPtr(ConstPtr & ptr, Uint32 i) const { ptr.i = i; if(likely(i < size)){ ptr.p = &theArray[i]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } #endif } else { ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } } template inline T * ArrayPool::getPtr(Uint32 i){ if(likely(i < size)){ #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return &theArray[i]; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); return 0; } #endif return &theArray[i]; } else { ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); return 0; } } template inline const T * ArrayPool::getConstPtr(Uint32 i) const { if(likely(i < size)){ #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return &theArray[i]; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); return 0; } #endif return &theArray[i]; } else { ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); return 0; } } template inline void ArrayPool::getPtr(Ptr & ptr, bool CrashOnBoundaryError){ Uint32 i = ptr.i; if(likely(i < size)){ ptr.p = &theArray[i]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } #endif } else { ptr.i = RNIL; } } template inline void ArrayPool::getPtr(ConstPtr & ptr, bool CrashOnBoundaryError) const { Uint32 i = ptr.i; if(likely(i < size)){ ptr.p = &theArray[i]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } #endif } else { ptr.i = RNIL; } } template inline void ArrayPool::getPtr(Ptr & ptr, Uint32 i, bool CrashOnBoundaryError){ ptr.i = i; if(likely(i < size)){ ptr.p = &theArray[i]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } #endif } else { ptr.i = RNIL; } } template inline void ArrayPool::getPtr(ConstPtr & ptr, Uint32 i, bool CrashOnBoundaryError) const { ptr.i = i; if(likely(i < size)){ ptr.p = &theArray[i]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); } #endif } else { ptr.i = RNIL; } } template inline T * ArrayPool::getPtr(Uint32 i, bool CrashOnBoundaryError){ if(likely(i < size)){ #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return &theArray[i]; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getPtr", __FILE__, __LINE__); return 0; } #endif return &theArray[i]; } else { return 0; } } template inline const T * ArrayPool::getConstPtr(Uint32 i, bool CrashOnBoundaryError) const { if(likely(i < size)){ #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)) return &theArray[i]; /** * Getting a non-seized element */ ErrorReporter::handleAssert("ArrayPool::getConstPtr", __FILE__,__LINE__); return 0; } #endif return &theArray[i]; } else { return 0; } } /** * Allocate an object from pool - update Ptr * * Return i */ template inline bool ArrayPool::seize(Ptr & ptr){ Uint32 ff = firstFree; if(ff != RNIL){ firstFree = theArray[ff].nextPool; ptr.i = ff; ptr.p = &theArray[ff]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(!BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, ff)){ BitmaskImpl::set(bitmaskSz, theAllocatedBitmask, ff); noOfFree--; return true; } else { /** * Seizing an already seized element */ ErrorReporter::handleAssert("ArrayPool::seize", __FILE__, __LINE__); return false; } } #endif noOfFree--; return true; } ptr.i = RNIL; ptr.p = NULL; return false; } template inline bool ArrayPool::seizeId(Ptr & ptr, Uint32 i){ Uint32 ff = firstFree; Uint32 prev = RNIL; while(ff != i && ff != RNIL){ prev = ff; ff = theArray[ff].nextPool; } if(ff != RNIL){ if(prev == RNIL) firstFree = theArray[ff].nextPool; else theArray[prev].nextPool = theArray[ff].nextPool; ptr.i = ff; ptr.p = &theArray[ff]; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(!BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, ff)){ BitmaskImpl::set(bitmaskSz, theAllocatedBitmask, ff); noOfFree--; return true; } else { /** * Seizing an already seized element */ ErrorReporter::handleAssert("ArrayPool::seizeId", __FILE__, __LINE__); return false; } } #endif noOfFree--; return true; } ptr.i = RNIL; ptr.p = NULL; return false; } template inline bool ArrayPool::findId(Uint32 i) const { if (i >= size) return false; Uint32 ff = firstFree; while(ff != i && ff != RNIL){ ff = theArray[ff].nextPool; } return (ff == RNIL); } template Uint32 ArrayPool::seizeN(Uint32 n){ Uint32 curr = firstFree; Uint32 prev = RNIL; Uint32 sz = 0; while(sz < n && curr != RNIL){ if(theArray[curr].nextPool == (curr + 1)){ sz++; } else { sz = 0; prev = curr; } curr = theArray[curr].nextPool; } if(sz != n){ return RNIL; } const Uint32 base = curr - n; if(base == firstFree){ firstFree = curr; } else { theArray[prev].nextPool = curr; } noOfFree -= n; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { for(Uint32 j = base; j::seize", __FILE__, __LINE__); return RNIL; } } } #endif return base; } template inline void ArrayPool::releaseN(Uint32 base, Uint32 n){ Uint32 curr = firstFree; Uint32 prev = RNIL; while(curr < base){ prev = curr; curr = theArray[curr].nextPool; } if(curr == firstFree){ firstFree = base; } else { theArray[prev].nextPool = base; } const Uint32 end = base + n; for(Uint32 i = base; i::release", __FILE__, __LINE__); return; } } #endif theArray[i].nextPool = i + 1; } theArray[end-1].nextPool = curr; noOfFree += n; } template inline void ArrayPool::releaseList(Uint32 n, Uint32 first, Uint32 last){ if(first < size && last < size){ Uint32 ff = firstFree; firstFree = first; theArray[last].nextPool = ff; noOfFree += n; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { Uint32 tmp = first; for(Uint32 i = 0; i::releaseList", __FILE__, __LINE__); return; } tmp = theArray[tmp].nextPool; } } #endif return; } ErrorReporter::handleAssert("ArrayPool::releaseList", __FILE__, __LINE__); } /** * Return an object to pool */ template inline void ArrayPool::release(Uint32 _i){ const Uint32 i = _i; if(likely(i < size)){ Uint32 ff = firstFree; theArray[i].nextPool = ff; firstFree = i; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)){ BitmaskImpl::clear(bitmaskSz, theAllocatedBitmask, i); noOfFree++; return; } /** * Relesing a already released element */ ErrorReporter::handleAssert("ArrayPool::release", __FILE__, __LINE__); } #endif noOfFree++; return; } ErrorReporter::handleAssert("ArrayPool::release", __FILE__, __LINE__); } /** * Return an object to pool */ template inline void ArrayPool::release(Ptr & ptr){ Uint32 i = ptr.i; if(likely(i < size)){ Uint32 ff = firstFree; theArray[i].nextPool = ff; firstFree = i; #ifdef ARRAY_GUARD if (theAllocatedBitmask) { if(BitmaskImpl::get(bitmaskSz, theAllocatedBitmask, i)){ BitmaskImpl::clear(bitmaskSz, theAllocatedBitmask, i); //assert(noOfFree() == noOfFree2()); noOfFree++; return; } /** * Relesing a already released element */ ErrorReporter::handleAssert("ArrayPool::release", __FILE__, __LINE__); } #endif noOfFree++; return; } ErrorReporter::handleAssert("ArrayPool::release", __FILE__, __LINE__); } template class UnsafeArrayPool : public ArrayPool { public: /** * Update p value for ptr according to i value * ignore if it's allocated or not */ void getPtrForce(Ptr &); void getPtrForce(ConstPtr &) const; T * getPtrForce(Uint32 i); const T * getConstPtrForce(Uint32 i) const; void getPtrForce(Ptr &, Uint32 i); void getPtrForce(ConstPtr &, Uint32 i) const; }; template inline void UnsafeArrayPool::getPtrForce(Ptr & ptr){ Uint32 i = ptr.i; if(likely(i < this->size)){ ptr.p = &this->theArray[i]; } else { ErrorReporter::handleAssert("UnsafeArrayPool::getPtr", __FILE__, __LINE__); } } template inline void UnsafeArrayPool::getPtrForce(ConstPtr & ptr) const{ Uint32 i = ptr.i; if(likely(i < this->size)){ ptr.p = &this->theArray[i]; } else { ErrorReporter::handleAssert("UnsafeArrayPool::getPtr", __FILE__, __LINE__); } } template inline T * UnsafeArrayPool::getPtrForce(Uint32 i){ if(likely(i < this->size)){ return &this->theArray[i]; } else { ErrorReporter::handleAssert("UnsafeArrayPool::getPtr", __FILE__, __LINE__); return 0; } } template inline const T * UnsafeArrayPool::getConstPtrForce(Uint32 i) const { if(likely(i < this->size)){ return &this->theArray[i]; } else { ErrorReporter::handleAssert("UnsafeArrayPool::getPtr", __FILE__, __LINE__); return 0; } } template inline void UnsafeArrayPool::getPtrForce(Ptr & ptr, Uint32 i){ ptr.i = i; if(likely(i < this->size)){ ptr.p = &this->theArray[i]; return ; } else { ErrorReporter::handleAssert("UnsafeArrayPool::getPtr", __FILE__, __LINE__); } } template inline void UnsafeArrayPool::getPtrForce(ConstPtr & ptr, Uint32 i) const{ ptr.i = i; if(likely(i < this->size)){ ptr.p = &this->theArray[i]; return ; } else { ErrorReporter::handleAssert("UnsafeArrayPool::getPtr", __FILE__, __LINE__); } } #endif