povray/source/backend/support/simplevector.h
2013-11-06 13:07:19 -05:00

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