/****************************************************************************** Vector.inl This file contains function definitions for tVector. Copyright (c) 2004-2015 Ryan Juckett http://www.ryanjuckett.com/ This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This file is derived from software with the following restrictions: | Copyright (c) 1994 | Hewlett-Packard Company | | Copyright (c) 1996,1997 | Silicon Graphics Computer Systems, Inc. | | Copyright (c) 1997 | Moscow Center for SPARC Technology | | Copyright (c) 1999 | Boris Fomitchev | | This material is provided "as is", with absolutely no warranty expressed | or implied. Any use is at your own risk. | | Permission to use or copy this software for any purpose is hereby granted | without fee, provided the above notices are retained on all copies. | Permission to modify the code and to distribute modified code is granted, | provided the above notices are retained, and a notice that the code was | modified is included with the above copyright notice. ******************************************************************************/ //============================================================================= // Class Member Function Definitions //============================================================================= namespace RJ { namespace Containers { //****************************************************************************** // tVectorIteratorBase == tVectorIteratorBase::tOther //****************************************************************************** template inline tB operator == ( const tVectorIteratorBase & lhs, const typename tVectorIteratorBase::tOther & rhs ) { RJ_ASSERT( (lhs.m_pElement && rhs.m_pElement) || (!lhs.m_pElement && !rhs.m_pElement) ); return (lhs.m_pElement == rhs.m_pElement); } //****************************************************************************** // tVectorIteratorBase < tVectorIteratorBase::tOther //****************************************************************************** template inline tB operator < ( const tVectorIteratorBase & lhs, const typename tVectorIteratorBase::tOther & rhs ) { RJ_ASSERT( (lhs.m_pElement && rhs.m_pElement) || (!lhs.m_pElement && !rhs.m_pElement) ); return (lhs.m_pElement < rhs.m_pElement); } //****************************************************************************** // tVector::tVector // Constructor //****************************************************************************** template inline tVector::tVector() : m_pBegin(NULL), m_pStackEnd(NULL), m_pBufferEnd(NULL) { } //****************************************************************************** // tVector::tVector // Copy Constructor // This function will assert on non empty values of rhs. It is only // made a public function so that the container can be supplied as a // template parameter to classes which might need to compile // the function if the user will never call said logic. //****************************************************************************** template inline tVector::tVector ( const tThis & rhs ) : m_pBegin(NULL), m_pStackEnd(NULL), m_pBufferEnd(NULL) { RJ_UNUSED_VARIABLE(rhs); // Due to not supporting exceptions, we would ne be able to return // an allocation error from the copy constructor. This means that // it is only safe to copy an empty container. RJ_ASSERT( rhs.IsEmpty() ); } //****************************************************************************** // tVector::~tVector // Destructor //****************************************************************************** template inline tVector::~tVector() { Clear(); } //****************************************************************************** // tVector::operator = // This function will assert on non empty values of rhs. It is only // made a public function so that the container can be supplied as a // template parameter to classes which might need to compile // the function if the user will never call said logic. //****************************************************************************** template inline tVector &tVector::operator = ( const tThis & rhs ) { // Due to not supporting exceptions, we would ne be able to return // an allocation error from the copy constructor. This means that // it is only safe to copy an empty container. RJ_ASSERT( rhs.IsEmpty() ); // Copy should never fail due to rhs being empty const tB success = Copy(rhs); RJ_UNUSED_VARIABLE(success); RJ_ASSERT( success == true ); return (*this); } //****************************************************************************** // tVector::Reserve // Reserve the specified number of elements for the capacity of the vector. If // we already have more than the specified count, do nothing. //****************************************************************************** template inline tB tVector::Reserve ( tSize numElements ) { // ensure the allocator will support the new size RJ_ASSERT( numElements <= GetMaxSize() ); // if we do not already have enough room if( GetCapacity() < numElements ) { t_Element* pNewElements = (t_Element*)this->ElementAllocator().Allocate(sizeof(t_Element)*numElements); if( pNewElements == NULL ) return false; ConstructAndCopyRange( GetBegin(), GetEnd(), pNewElements ); tSize cur_size = GetSize(); // destroy and deallocate old array if( m_pBegin != NULL ) { Destroy( m_pBegin, m_pStackEnd ); this->ElementAllocator().Deallocate( m_pBegin ); } m_pBufferEnd = pNewElements + numElements; m_pStackEnd = pNewElements + cur_size; m_pBegin = pNewElements; } return true; } //****************************************************************************** // tVector::GrowCapacity // If we do not already have a capcity that can support the specified number // of elements, exponentially grow the capacity to hold the specified number // of elements. //****************************************************************************** template inline tB tVector::GrowCapacity ( tSize numElements ) { const tSize capacity = GetCapacity(); if( numElements <= capacity ) return true; const tSize maxSize = GetMaxSize(); if( numElements > maxSize ) return false; tSize newCapacity = Math::Clamp( capacity + capacity/2, numElements, maxSize ); return Reserve( newCapacity ); } //****************************************************************************** // tVector::Resize //****************************************************************************** template inline tB tVector::Resize ( tSize numElements ) { if( GetSize() < numElements ) { if( GetCapacity() < numElements ) { if( Reserve( numElements ) == false ) return false; } AppendSequence( numElements - GetSize() ); } else if( numElements < GetSize() ) { EraseRange( GetBegin() + numElements, GetEnd() ); } return true; } //****************************************************************************** // tVector::Resize //****************************************************************************** template inline tB tVector::Resize ( tSize numElements, const t_Element & element ) { if( GetSize() < numElements ) { if( GetCapacity() < numElements ) { if( Reserve( numElements ) == false ) return false; } AppendSequence( numElements - GetSize(), element ); } else if( numElements < GetSize() ) { EraseRange( GetBegin() + numElements, GetEnd() ); } return true; } //****************************************************************************** // tVector::ResizeMax //****************************************************************************** template inline tB tVector::ResizeMax() { const tSize maxSize = GetMaxSize(); RJ_ASSERT( GetSize() <= maxSize ); if( GetSize() != maxSize ) { RJ_ASSERT( GetCapacity() <= maxSize ); if( GetCapacity() != maxSize ) { if( Reserve( maxSize ) == false ) return false; } AppendSequence( maxSize - GetSize() ); } return true; } //****************************************************************************** // tVector::ResizeMax //****************************************************************************** template inline tB tVector::ResizeMax ( const t_Element & element ) { const tSize maxSize = GetMaxSize(); RJ_ASSERT( GetSize() <= maxSize ); if( GetSize() != maxSize ) { RJ_ASSERT( GetCapacity() <= maxSize ); if( GetCapacity() != maxSize ) { if( Reserve( maxSize ) == false ) return false; } AppendSequence( maxSize - GetSize(), element ); } return true; } //****************************************************************************** //****************************************************************************** template inline void tVector::PushBack() { // check for reserved space RJ_ASSERT( GetSize() < GetCapacity() ); AppendSequence(1); } //****************************************************************************** //****************************************************************************** template inline void tVector::PushBack ( const t_Element & element ) { // check for reserved space RJ_ASSERT( GetSize() < GetCapacity() ); Insert( GetEnd(), element ); } //****************************************************************************** //****************************************************************************** template inline tB tVector::PushBackAndGrow() { if (!GrowCapacity(GetSize() + 1)) return false; AppendSequence(1); return true; } //****************************************************************************** //****************************************************************************** template inline tB tVector::PushBackAndGrow ( const t_Element & element ) { if (!GrowCapacity(GetSize() + 1)) return false; Insert( GetEnd(), element ); return true; } //****************************************************************************** // tVector::PopBack //****************************************************************************** template inline void tVector::PopBack() { RJ_ASSERT( !IsEmpty() ); Destroy( m_pStackEnd - 1, m_pStackEnd ); --m_pStackEnd; } //****************************************************************************** // tVector::AssignRange //****************************************************************************** template template inline void tVector::AssignRange ( t_Iterator iBegin, t_Iterator iEnd ) { EraseAll(); InsertRange( GetBegin(), iBegin, iEnd ); } //****************************************************************************** // tVector::AssignSequence //****************************************************************************** template inline void tVector::AssignSequence ( tSize numElements, const t_Element &element ) { // use temporary storage incase element is in the sequence t_Element temp(element); EraseRange( GetBegin(), GetEnd() ); InsertSequence( GetBegin(), numElements, temp ); } //****************************************************************************** // tVector::Insert //****************************************************************************** template inline typename tVector::tIterator tVector::Insert ( tIterator iInsert, const t_Element & element ) { // check that the insert position is in range RJ_ASSERT( iInsert >= GetBegin() ); RJ_ASSERT( iInsert <= GetEnd() ); // check for reserved space RJ_ASSERT( GetSize() < GetCapacity() ); typename tIterator::tElementPtrDiff offset = IsEmpty() ? 0 : iInsert - GetBegin(); InsertSequence( iInsert, static_cast(1), element ); return (GetBegin() + offset); } //****************************************************************************** // tVector::InsertSequence //****************************************************************************** template inline void tVector::InsertSequence ( tIterator iInsert, tSize numElements, const t_Element & element ) { // check that the insert position is in range RJ_ASSERT( iInsert >= GetBegin() ); RJ_ASSERT( iInsert <= GetEnd() ); // make sure we have enough memory RJ_ASSERT( GetSize() + numElements <= GetCapacity() ); // check if we don't need to insert if( numElements == 0 ) return; // use a temp value incase element is in part of the sequence // that will be modified t_Element temp(element); // if the new elements will extend off the stack end if( static_cast(GetEnd() - iInsert) < numElements ) { // shift the suffix foward in the buffer ConstructAndCopyRange( iInsert.m_pElement, m_pStackEnd, iInsert.m_pElement + numElements ); // insert the portion of the new elements that goes past the old stack end ConstructAndFillSequence( m_pStackEnd, numElements - (GetEnd() - iInsert), temp ); m_pStackEnd += numElements; // copy in the non overflowing new elements FillRange( iInsert.m_pElement, m_pStackEnd - numElements, temp ); } // else the new elements fit inside the existing stack else { t_Element *pOld_stack_end = m_pStackEnd; // copy suffix m_pStackEnd = ConstructAndCopyRange( pOld_stack_end - numElements, pOld_stack_end, pOld_stack_end ); // copy the elements that will be overwritten CopyRangeBackward( iInsert.m_pElement, pOld_stack_end - numElements, pOld_stack_end ); // insert the new elements FillRange( iInsert.m_pElement, iInsert.m_pElement + numElements, temp); } } //****************************************************************************** // tVector::InsertRange //****************************************************************************** template template inline void tVector::InsertRange ( tIterator iInsert, t_Iterator iBegin, t_Iterator iEnd ) { InsertRange( iInsert, iBegin, iEnd, typename tIteratorTraits::tIteratorCategory() ); } //****************************************************************************** // tVector::Erase //****************************************************************************** template inline typename tVector::tIterator tVector::Erase ( tIterator iErase ) { RJ_ASSERT( !IsEmpty() ); RJ_ASSERT( iErase.m_pElement >= m_pBegin ); RJ_ASSERT( iErase.m_pElement < m_pStackEnd ); CopyRange( iErase.m_pElement + 1, m_pStackEnd, iErase.m_pElement ); Destroy( m_pStackEnd - 1, m_pStackEnd ); --m_pStackEnd; return iErase; } //****************************************************************************** // tVector::EraseRange //****************************************************************************** template inline typename tVector::tIterator tVector::EraseRange ( tIterator iBegin, tIterator iEnd ) { // if we need to erase if( iBegin != iEnd) { RJ_ASSERT( !IsEmpty() ); t_Element *pNew_stack_end = CopyRange( iEnd.m_pElement, m_pStackEnd, iBegin.m_pElement ); Destroy( pNew_stack_end, m_pStackEnd ); m_pStackEnd = pNew_stack_end; } return iBegin; } //****************************************************************************** // tVector::EraseAll // This will only erase the elements. The reserve will not be cleared. //****************************************************************************** template inline void tVector::EraseAll() { Resize(0); } //****************************************************************************** // tVector::Clear // This will clear the elements and the reserve. //****************************************************************************** template inline void tVector::Clear() { // if there is anything to clear if( m_pBegin != NULL ) { Destroy( m_pBegin, m_pStackEnd ); this->ElementAllocator().Deallocate( m_pBegin ); m_pBegin = NULL; m_pStackEnd = NULL; m_pBufferEnd = NULL; } } //****************************************************************************** // tVector::Swap //****************************************************************************** template inline tB tVector::Swap ( tThis * pRhs ) { // if the allocator is the same if( this->ElementAllocator().CompareHeap(pRhs->ElementAllocator()) ) { // swap control information RJ::Swap( &m_pBegin, &pRhs->m_pBegin ); RJ::Swap( &m_pStackEnd, &pRhs->m_pStackEnd ); RJ::Swap( &m_pBufferEnd, &pRhs->m_pBufferEnd ); } // else the allocators are different else { tThis temp; if (!temp.ElementAllocator().CopyHeap(this->ElementAllocator())) return false; if( temp.Copy( *this ) == false ) return false; if( this->Copy(*pRhs) == false ) return false; if( pRhs->Copy(temp) == false ) return false; } return true; } //****************************************************************************** // tVector::Copy //****************************************************************************** template inline tB tVector::Copy ( const tThis & rhs ) { // optimize out equal containers if( this != &rhs ) { // if we have enough existing elements if( rhs.GetSize() <= GetSize() ) { // copy new t_Element * pEnd = CopyRange(rhs.m_pBegin, rhs.m_pStackEnd, m_pBegin); // destroy old Destroy( pEnd, m_pStackEnd ); // update pointers m_pStackEnd = m_pBegin + rhs.GetSize(); } // else if we have enough buffered space else if( rhs.GetSize() <= GetCapacity() ) { t_Element *pCopy_end = rhs.m_pBegin + GetSize(); CopyRange(rhs.m_pBegin, pCopy_end, m_pBegin); m_pStackEnd = ConstructAndCopyRange(pCopy_end, rhs.m_pStackEnd, m_pStackEnd); } // else we do not have enough room, allocate new array and construct new else { // discard old array Clear(); // allocate and initialize the new array if( Alloc( rhs.GetSize() ) == false ) return false; m_pStackEnd = ConstructAndCopyRange(rhs.m_pBegin, rhs.m_pStackEnd, m_pBegin); } } return true; } //****************************************************************************** // tVector::IsEqual //****************************************************************************** template inline tB tVector::IsEqual ( const tThis & rhs ) const { if( this == &rhs ) return true; return ( GetSize() == rhs.GetSize() ) && IsEqualRange( GetBegin(), GetEnd(), rhs.GetBegin() ); } //****************************************************************************** // tVector::IsLess //****************************************************************************** template inline tB tVector::IsLess ( const tThis & rhs ) const { return LexicographicalCompare( GetBegin(), GetEnd(), rhs.GetBegin(), rhs.GetEnd() ); } //****************************************************************************** // tVector::InsertRange //****************************************************************************** template template inline void tVector::InsertRange ( tIterator iInsert, t_Iterator iBegin, t_Iterator iEnd, tInputItrCategory ) { for( ; iBegin != iEnd; ++iBegin, ++iInsert ) iInsert = Insert( iInsert, *iBegin ); } //****************************************************************************** // tVector::InsertRange //****************************************************************************** template template inline void tVector::InsertRange ( tIterator iInsert, t_Iterator iBegin, t_Iterator iEnd, tForwardItrCategory ) { // check that the insert position is in range RJ_ASSERT( iInsert >= GetBegin() ); RJ_ASSERT( iInsert <= GetEnd() ); // determine how many elements need to be inserted tSize numElements = GetDistance( iBegin, iEnd ); // make sure we have enough memory RJ_ASSERT( GetSize() + numElements <= GetCapacity() ); // check if we don't need to insert if( numElements == 0 ) return; // if the new elements will extend off the stack end if( tSize(GetEnd() - iInsert) < numElements ) { // shift the suffix foward in the buffer ConstructAndCopyRange( iInsert.m_pElement, m_pStackEnd, iInsert.m_pElement + numElements ); // get the position in the new elements where it overflows // past the end of the old stack t_Iterator iMid = iBegin; Advance( &iMid, GetEnd() - iInsert ); // insert the portion of the new elements that goes past the old stack end ConstructAndCopyRange( iMid, iEnd, m_pStackEnd ); m_pStackEnd += numElements; // copy in the non overflowing new elements CopyRange( iBegin, iMid, iInsert.m_pElement ); } // else the new elements fit inside the existing stack else { t_Element *pOld_stack_end = m_pStackEnd; // copy suffix m_pStackEnd = ConstructAndCopyRange( pOld_stack_end - numElements, pOld_stack_end, pOld_stack_end ); // copy the elements that will be overwritten CopyRangeBackward( iInsert.m_pElement, pOld_stack_end - numElements, pOld_stack_end ); // insert the new elements CopyRange( iBegin, iEnd, iInsert.m_pElement ); } } //****************************************************************************** // tVector::AppendSequence //****************************************************************************** template inline void tVector::AppendSequence ( tSize numElements ) { // make sure we have enough memory RJ_ASSERT( GetSize() + numElements <= GetCapacity() ); // check if we don't need to insert if( numElements == 0 ) return; // insert the the new elements at the old stack end ConstructSequence( m_pStackEnd, numElements ); m_pStackEnd += numElements; } //****************************************************************************** // tVector::AppendSequence //****************************************************************************** template inline void tVector::AppendSequence ( tSize numElements, const t_Element & element ) { // make sure we have enough memory RJ_ASSERT( GetSize() + numElements <= GetCapacity() ); // check if we don't need to insert if( numElements == 0 ) return; // insert the portion of the new elements that goes past the old stack end ConstructAndFillSequence( m_pStackEnd, numElements, element ); m_pStackEnd += numElements; } //****************************************************************************** // tVector::Alloc //****************************************************************************** template inline tB tVector::Alloc( tSize numElements ) { RJ_ASSERT( m_pBegin == NULL ); RJ_ASSERT( m_pStackEnd == NULL ); RJ_ASSERT( m_pStackEnd == NULL ); if( numElements == 0 ) return true; // allocate storage m_pBegin = (t_Element*)this->ElementAllocator().Allocate( sizeof(t_Element)*numElements ); if( m_pBegin == NULL ) return false; m_pStackEnd = m_pBegin; m_pBufferEnd = m_pBegin + numElements; return true; } //****************************************************************************** // tVector::Destroy //****************************************************************************** template inline void tVector::Destroy ( t_Element * pBegin, t_Element * pEnd ) { RJ_ASSERT( pBegin <= pEnd ); RJ_ASSERT( pBegin >= m_pBegin && pBegin <= m_pStackEnd ); RJ_ASSERT( pEnd >= m_pBegin && pEnd <= m_pStackEnd ); RJ::DestructRange(pBegin,pEnd); } //****************************************************************************** // tVector::ConstructAndCopyRange //****************************************************************************** template template inline t_Element * tVector::ConstructAndCopyRange ( t_Iterator iSrcBegin, t_Iterator iSrcEnd, t_Element * pDest ) { return RJ::ConstructAndCopyRange(iSrcBegin, iSrcEnd, pDest); } //****************************************************************************** // tVector::ConstructSequence //****************************************************************************** template inline t_Element * tVector::ConstructSequence ( t_Element * pBegin, tSize numElements ) { return RJ::ConstructSequence(pBegin, numElements); } //****************************************************************************** // tVector::ConstructAndFillSequence //****************************************************************************** template inline t_Element * tVector::ConstructAndFillSequence ( t_Element * pBegin, tSize numElements, const t_Element & element ) { return RJ::ConstructAndFillSequence(pBegin, numElements, element); } } }