565 lines
13 KiB
C++
565 lines
13 KiB
C++
/*******************************************************************************
|
|
* simplevector.h
|
|
*
|
|
* Very simple, basic vector-like classes containing just enough functionality
|
|
* for their intended uses within POV. Flexibility is sacrificed for performance
|
|
* as these classes will typically be used in places where they may constructed
|
|
* and destroyed many millions of times per render. (For example, mediasky.pov
|
|
* rendered at only 160x120 with no AA results in over 16 million instances of
|
|
* construction of a FixedSimpleVector).
|
|
*
|
|
* These classes were added after extensive profiling pointed to a number of
|
|
* instances of our use of std::vector causing slowdowns, particularly when
|
|
* multiple threads were in use (due to locks in the RTL memory management used
|
|
* to prevent heap corruption). Experiments with non-heap-based allocators (e.g.
|
|
* refpools or thread-local storage) did improve the situation somewhat but weren't
|
|
* enough, hence this file. At the time of writing we get about a 10% improvement
|
|
* as compared to the old code.
|
|
*
|
|
* NOTE NOTE NOTE NOTE
|
|
* -------------------
|
|
* Be aware that these classes do NOT run destructors on contained objects.
|
|
* This is intentional as we currently do not store any objects in them that
|
|
* require this functionality.
|
|
*
|
|
* Author: Christopher J. Cason.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
|
|
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
|
|
*
|
|
* POV-Ray is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* POV-Ray 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 Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------------
|
|
* POV-Ray is based on the popular DKB raytracer version 2.12.
|
|
* DKBTrace was originally written by David K. Buck.
|
|
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
|
|
* ---------------------------------------------------------------------------
|
|
* $File: //depot/public/povray/3.x/source/backend/support/simplevector.h $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
#ifndef __SIMPLEVECTOR__
|
|
#define __SIMPLEVECTOR__
|
|
|
|
#include <stdexcept>
|
|
#include "base/pov_err.h"
|
|
|
|
namespace pov
|
|
{
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Works like std::vector in some ways, but very limited and not at all as
|
|
// flexible. Does not implement all the methods of std::vector (just what
|
|
// is needed in POV). Has different allocation behaviour, which will probably
|
|
// need to be tweaked over time for best performance.
|
|
//
|
|
// Be aware that this class does NOT run destructors on contained objects.
|
|
// This is intentional as we currently do not store any objects in it that
|
|
// require this functionality. // TODO FIXME
|
|
////////////////////////////////////////////////////////////////////////////
|
|
template<class ContainerType, class Allocator = std::allocator<ContainerType> >
|
|
class SimpleVector
|
|
{
|
|
public:
|
|
typedef SimpleVector<ContainerType> MyType;
|
|
typedef size_t size_type;
|
|
typedef size_t difference_type;
|
|
typedef ContainerType *pointer;
|
|
typedef ContainerType& reference;
|
|
typedef ContainerType value_type;
|
|
typedef const ContainerType *const_pointer;
|
|
typedef const ContainerType& const_reference;
|
|
typedef const ContainerType *const_iterator;
|
|
typedef ContainerType *iterator;
|
|
typedef Allocator allocator;
|
|
typedef std::reverse_iterator<iterator> reverse_iterator;
|
|
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
|
|
|
SimpleVector()
|
|
{
|
|
m_First = m_Last = m_End = NULL;
|
|
}
|
|
|
|
SimpleVector(size_type nItems, const ContainerType& InitialVal)
|
|
{
|
|
m_First = m_Last = m_End = NULL;
|
|
if (nItems)
|
|
allocate (nItems, InitialVal);
|
|
}
|
|
|
|
SimpleVector(const MyType& RHS)
|
|
{
|
|
if (RHS.m_First != RHS.m_Last)
|
|
{
|
|
allocate (RHS.capacity());
|
|
for (pointer p = RHS.m_First ; p != RHS.m_Last ; )
|
|
*m_Last++ = *p++;
|
|
}
|
|
else
|
|
m_First = m_Last = m_End = NULL;
|
|
}
|
|
|
|
~SimpleVector()
|
|
{
|
|
// we don't call destructors, even if they exist
|
|
if (m_First != NULL)
|
|
deallocate ();
|
|
}
|
|
|
|
MyType& operator=(const MyType& RHS)
|
|
{
|
|
if (RHS.size() > capacity())
|
|
{
|
|
if (m_First != NULL)
|
|
deallocate ();
|
|
allocate (RHS.size());
|
|
}
|
|
m_Last = m_First;
|
|
for (pointer p = RHS.m_First ; p != RHS.m_Last ; )
|
|
*m_Last++ = *p++;
|
|
return (*this);
|
|
}
|
|
|
|
size_type capacity() const
|
|
{
|
|
return (m_End - m_First);
|
|
}
|
|
|
|
iterator begin()
|
|
{
|
|
return (m_First);
|
|
}
|
|
|
|
const_iterator begin() const
|
|
{
|
|
return (m_First);
|
|
}
|
|
|
|
iterator end()
|
|
{
|
|
return (m_Last);
|
|
}
|
|
|
|
const_iterator end() const
|
|
{
|
|
return (m_Last);
|
|
}
|
|
|
|
reverse_iterator rbegin()
|
|
{
|
|
return (reverse_iterator (m_Last));
|
|
}
|
|
|
|
const_reverse_iterator rbegin() const
|
|
{
|
|
return (const_reverse_iterator (m_Last));
|
|
}
|
|
|
|
reverse_iterator rend()
|
|
{
|
|
return (reverse_iterator (m_First));
|
|
}
|
|
|
|
const_reverse_iterator rend() const
|
|
{
|
|
return (const_reverse_iterator (m_First));
|
|
}
|
|
|
|
size_type size() const
|
|
{
|
|
return (m_Last - m_First);
|
|
}
|
|
|
|
size_type max_size() const
|
|
{
|
|
return (alloc.max_size ());
|
|
}
|
|
|
|
bool empty() const
|
|
{
|
|
return (m_First == m_Last);
|
|
}
|
|
|
|
const_reference at(size_type Index) const
|
|
{
|
|
if (Index > size())
|
|
throw std::out_of_range ("index out of range in SimpleVector::at");
|
|
return (m_First [Index]);
|
|
}
|
|
|
|
reference at(size_type Index)
|
|
{
|
|
if (Index > size())
|
|
throw std::out_of_range ("index out of range in SimpleVector::at");
|
|
return (m_First [Index]);
|
|
}
|
|
|
|
const_reference operator[](size_type Index) const
|
|
{
|
|
return (m_First [Index]);
|
|
}
|
|
|
|
reference operator[](size_type Index)
|
|
{
|
|
return (m_First [Index]);
|
|
}
|
|
|
|
reference front()
|
|
{
|
|
return (*m_First);
|
|
}
|
|
|
|
const_reference front() const
|
|
{
|
|
return (*m_First);
|
|
}
|
|
|
|
reference back()
|
|
{
|
|
return (*(m_Last - 1));
|
|
}
|
|
|
|
const_reference back() const
|
|
{
|
|
return (*(m_Last - 1));
|
|
}
|
|
|
|
void push_back(const ContainerType& NewVal)
|
|
{
|
|
if (m_Last < m_End)
|
|
{
|
|
*m_Last++ = NewVal;
|
|
return;
|
|
}
|
|
insert(m_Last, NewVal);
|
|
}
|
|
|
|
void pop_back()
|
|
{
|
|
if (m_Last > m_First)
|
|
--m_Last;
|
|
}
|
|
|
|
iterator insert(iterator Where, const ContainerType& NewVal)
|
|
{
|
|
size_type Index = 0;
|
|
|
|
if (m_Last > m_First)
|
|
Index = Where - m_First;
|
|
|
|
if (m_Last == m_End)
|
|
{
|
|
size_type c = size() + 1;
|
|
size_type n = capacity();
|
|
size_type nc = n * 2;
|
|
if (nc < 8)
|
|
nc = 8;
|
|
pointer p = alloc.allocate (nc);
|
|
p [Index] = NewVal;
|
|
for (size_type i = 0 ; i < Index ; i++)
|
|
p [i] = m_First [i];
|
|
for (size_type i = Index + 1 ; i < c ; i++)
|
|
p [i] = m_First [i];
|
|
if (m_First != NULL)
|
|
alloc.deallocate (m_First, n);
|
|
m_First = p;
|
|
m_End = m_First + nc;
|
|
m_Last = m_First + c;
|
|
return (m_First + Index);
|
|
}
|
|
|
|
if (Index == size())
|
|
{
|
|
*m_Last = NewVal;
|
|
return (m_Last++);
|
|
}
|
|
|
|
for (size_type i = size() ; i > Index ; i--)
|
|
m_First [i] = m_First [i - 1];
|
|
m_Last++;
|
|
m_First [Index] = NewVal;
|
|
return (m_First + Index);
|
|
}
|
|
|
|
iterator erase(iterator What)
|
|
{
|
|
size_type Index = What - begin();
|
|
|
|
if (What == m_Last - 1)
|
|
return (m_Last--);
|
|
|
|
for (pointer p1 = What, p2 = What + 1 ; p2 < m_Last ; )
|
|
*p1++ = *p2++;
|
|
m_Last--;
|
|
return (++What);
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
m_Last = m_First;
|
|
}
|
|
|
|
private:
|
|
void allocate (size_type nItems)
|
|
{
|
|
m_Last = m_First = alloc.allocate (nItems);
|
|
m_End = m_First + nItems;
|
|
}
|
|
|
|
void allocate (size_type nItems, const ContainerType& InitialVal)
|
|
{
|
|
m_Last = m_First = alloc.allocate (nItems);
|
|
m_End = m_First + nItems;
|
|
while (nItems--)
|
|
*m_Last++ = InitialVal;
|
|
}
|
|
|
|
void deallocate ()
|
|
{
|
|
alloc.deallocate (m_First, m_End - m_First);
|
|
}
|
|
|
|
allocator alloc;
|
|
pointer m_First;
|
|
pointer m_End;
|
|
pointer m_Last;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// This template class requires a maximum size (ElementCount) and maintains
|
|
// its storage internally (typically therefore this will end up on the stack
|
|
// rather than being allocated upon request). The up side to this behaviour
|
|
// is that no time is spent obtaining memory from a pool, or for that matter
|
|
// copying data if a reallocation is needed. The down side is that firstly
|
|
// it cannot expand beyond the maximum size specified, and secondly that
|
|
// binary copies of the object take longer because they contain the entire
|
|
// storage (even if no entries are allocated).
|
|
//
|
|
// Be aware that this class does NOT run destructors on contained objects.
|
|
// This is intentional as we currently do not store any objects in it that
|
|
// require this functionality.
|
|
////////////////////////////////////////////////////////////////////////////
|
|
template<class ContainerType, int ElementCount>
|
|
class FixedSimpleVector
|
|
{
|
|
public:
|
|
typedef FixedSimpleVector<ContainerType, ElementCount> MyType;
|
|
typedef size_t size_type;
|
|
typedef size_t difference_type;
|
|
typedef ContainerType *pointer;
|
|
typedef ContainerType& reference;
|
|
typedef ContainerType value_type;
|
|
typedef const ContainerType *const_pointer;
|
|
typedef const ContainerType& const_reference;
|
|
typedef const ContainerType *const_iterator;
|
|
typedef ContainerType *iterator;
|
|
typedef std::reverse_iterator<iterator> reverse_iterator;
|
|
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
|
|
|
FixedSimpleVector() :
|
|
m_Last ((pointer) m_Data),
|
|
m_End (pointer (m_Data) + ElementCount)
|
|
{
|
|
}
|
|
|
|
FixedSimpleVector(size_type nItems, const ContainerType& InitialVal) :
|
|
m_Last ((pointer) m_Data),
|
|
m_End (pointer (m_Data) + ElementCount)
|
|
{
|
|
if (nItems > ElementCount)
|
|
throw POV_EXCEPTION(pov_base::kInternalLimitErr, "Internal limit exceeded in FixedSimpleVector");
|
|
while (nItems--)
|
|
*m_Last++ = InitialVal;
|
|
}
|
|
|
|
FixedSimpleVector(const MyType& RHS) :
|
|
m_Last ((pointer) m_Data),
|
|
m_End (pointer (m_Data) + ElementCount)
|
|
{
|
|
for (pointer p = pointer (RHS.m_Data) ; p != RHS.m_Last ; )
|
|
*m_Last++ = *p++;
|
|
}
|
|
|
|
~FixedSimpleVector()
|
|
{
|
|
// we don't call destructors, even if they exist
|
|
}
|
|
|
|
MyType& operator=(const MyType& RHS)
|
|
{
|
|
if (RHS.size() > ElementCount)
|
|
throw POV_EXCEPTION(pov_base::kInternalLimitErr, "Internal limit exceeded in FixedSimpleVector");
|
|
m_Last = pointer (m_Data);
|
|
for (pointer p = pointer (RHS.m_Data) ; p != RHS.m_Last ; )
|
|
*m_Last++ = *p++;
|
|
return (*this);
|
|
}
|
|
|
|
size_type capacity() const
|
|
{
|
|
return (ElementCount);
|
|
}
|
|
|
|
iterator begin()
|
|
{
|
|
return (reinterpret_cast<pointer>(&(m_Data[0])));
|
|
}
|
|
|
|
const_iterator begin() const
|
|
{
|
|
return (reinterpret_cast<const_pointer>(&(m_Data[0])));
|
|
}
|
|
|
|
iterator end()
|
|
{
|
|
return (m_Last);
|
|
}
|
|
|
|
const_iterator end() const
|
|
{
|
|
return (m_Last);
|
|
}
|
|
|
|
reverse_iterator rbegin()
|
|
{
|
|
return (reverse_iterator (m_Last));
|
|
}
|
|
|
|
const_reverse_iterator rbegin() const
|
|
{
|
|
return (const_reverse_iterator (m_Last));
|
|
}
|
|
|
|
reverse_iterator rend()
|
|
{
|
|
return (reverse_iterator (pointer (m_Data)));
|
|
}
|
|
|
|
const_reverse_iterator rend() const
|
|
{
|
|
return (const_reverse_iterator (pointer (m_Data)));
|
|
}
|
|
|
|
size_type size() const
|
|
{
|
|
return (m_Last - const_pointer (m_Data));
|
|
}
|
|
|
|
size_type max_size() const
|
|
{
|
|
return (ElementCount);
|
|
}
|
|
|
|
bool empty() const
|
|
{
|
|
return (const_pointer (m_Data) == m_Last);
|
|
}
|
|
|
|
const_reference at(size_type Index) const
|
|
{
|
|
if (Index > size())
|
|
throw std::out_of_range ("index out of range in FixedSimpleVector::at");
|
|
return (pointer (m_Data) [Index]);
|
|
}
|
|
|
|
reference at(size_type Index)
|
|
{
|
|
if (Index > size())
|
|
throw std::out_of_range ("index out of range in FixedSimpleVector::at");
|
|
return (pointer (m_Data) [Index]);
|
|
}
|
|
|
|
const_reference operator[](size_type Index) const
|
|
{
|
|
return (pointer (m_Data) [Index]);
|
|
}
|
|
|
|
reference operator[](size_type Index)
|
|
{
|
|
return (pointer (m_Data) [Index]);
|
|
}
|
|
|
|
reference front()
|
|
{
|
|
return (*pointer (m_Data));
|
|
}
|
|
|
|
const_reference front() const
|
|
{
|
|
return (*pointer (m_Data));
|
|
}
|
|
|
|
reference back()
|
|
{
|
|
return (*(m_Last - 1));
|
|
}
|
|
|
|
const_reference back() const
|
|
{
|
|
return (*(m_Last - 1));
|
|
}
|
|
|
|
void push_back(const ContainerType& NewVal)
|
|
{
|
|
if (m_Last == m_End)
|
|
throw POV_EXCEPTION(pov_base::kInternalLimitErr, "Internal limit exceeded in FixedSimpleVector");
|
|
*m_Last++ = NewVal;
|
|
}
|
|
|
|
void pop_back()
|
|
{
|
|
if (m_Last > pointer (m_Data))
|
|
--m_Last;
|
|
}
|
|
|
|
iterator insert(iterator Where, const ContainerType& NewVal)
|
|
{
|
|
if (m_Last == m_End)
|
|
throw POV_EXCEPTION(pov_base::kInternalLimitErr, "Internal limit exceeded in FixedSimpleVector");
|
|
for (pointer p1 = Where, p2 = Where + 1 ; p1 < m_Last ; )
|
|
*p2++ = *p1++;
|
|
m_Last++;
|
|
*Where = NewVal;
|
|
return (Where);
|
|
}
|
|
|
|
iterator erase(iterator Where)
|
|
{
|
|
if (Where == m_End)
|
|
throw POV_EXCEPTION(pov_base::kInternalLimitErr, "Attempt to erase past end of vector");
|
|
if (Where == m_Last - 1)
|
|
return (m_Last--);
|
|
for (pointer p1 = Where, p2 = Where + 1 ; p2 < m_Last ; )
|
|
*p1++ = *p2++;
|
|
m_Last--;
|
|
return (++Where);
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
m_Last = pointer (m_Data);
|
|
}
|
|
|
|
private:
|
|
unsigned char m_Data [sizeof (value_type) * ElementCount];
|
|
const pointer m_End;
|
|
pointer m_Last;
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|