mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-29 15:32:38 +00:00
84173ee09b
The main bulk of this is the new start screen code. To make this work in Raze some more work on the startup procedure is needed. What this does provide is support for the DOS end-of-game text screens in Duke and SW on non-Windows systems.
1939 lines
41 KiB
C++
1939 lines
41 KiB
C++
#pragma once
|
|
/*
|
|
** tarray.h
|
|
** Templated, automatically resizing array
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 1998-2007 Randy Heit
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
** NOTE: TArray takes advantage of the assumption that the contained type is
|
|
** able to be trivially moved. The definition of trivially movable by the C++
|
|
** standard is more strict than the actual set of types that can be moved with
|
|
** memmove. For example, FString uses non-trivial constructors/destructor in
|
|
** order to maintain the reference count, but can be "safely" by passed if the
|
|
** opaque destructor call is avoided. Similarly types like TArray itself which
|
|
** only null the owning pointers when moving which can be skipped if the
|
|
** destructor is not called.
|
|
**
|
|
** It is possible that with LTO TArray could be made safe for non-trivial types,
|
|
** but we don't wish to rely on LTO to reach expected performance. The set of
|
|
** types which can not be contained by TArray as a result of this choice is
|
|
** actually extremely small.
|
|
**
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <new>
|
|
#include <utility>
|
|
#include <iterator>
|
|
|
|
#if !defined(_WIN32)
|
|
#include <inttypes.h> // for intptr_t
|
|
#else
|
|
#include <stdint.h> // for mingw
|
|
#endif
|
|
|
|
#if __has_include("m_alloc.h")
|
|
#include "m_alloc.h"
|
|
#else
|
|
#define M_Malloc malloc
|
|
#define M_Realloc realloc
|
|
#define M_Free free
|
|
#endif
|
|
|
|
template<typename T> class TIterator
|
|
{
|
|
public:
|
|
using iterator_category = std::random_access_iterator_tag;
|
|
using value_type = T;
|
|
using difference_type = ptrdiff_t;
|
|
using pointer = value_type*;
|
|
using reference = value_type&;
|
|
|
|
TIterator(T* ptr = nullptr) { m_ptr = ptr; }
|
|
|
|
// Comparison operators
|
|
bool operator==(const TIterator &other) const { return m_ptr == other.m_ptr; }
|
|
bool operator!=(const TIterator &other) const { return m_ptr != other.m_ptr; }
|
|
bool operator< (const TIterator &other) const { return m_ptr < other.m_ptr; }
|
|
bool operator<=(const TIterator &other) const { return m_ptr <= other.m_ptr; }
|
|
bool operator> (const TIterator &other) const { return m_ptr > other.m_ptr; }
|
|
bool operator>=(const TIterator &other) const { return m_ptr >= other.m_ptr; }
|
|
|
|
// Arithmetic operators
|
|
TIterator &operator++() { ++m_ptr; return *this; }
|
|
TIterator operator++(int) { pointer tmp = m_ptr; ++*this; return TIterator(tmp); }
|
|
TIterator &operator--() { --m_ptr; return *this; }
|
|
TIterator operator--(int) { pointer tmp = m_ptr; --*this; return TIterator(tmp); }
|
|
TIterator &operator+=(difference_type offset) { m_ptr += offset; return *this; }
|
|
TIterator operator+(difference_type offset) const { return TIterator(m_ptr + offset); }
|
|
friend TIterator operator+(difference_type offset, const TIterator &other) { return TIterator(offset + other.m_ptr); }
|
|
TIterator &operator-=(difference_type offset) { m_ptr -= offset; return *this; }
|
|
TIterator operator-(difference_type offset) const { return TIterator(m_ptr - offset); }
|
|
difference_type operator-(const TIterator &other) const { return m_ptr - other.m_ptr; }
|
|
|
|
// Random access operators
|
|
T& operator[](difference_type i) { return m_ptr[i]; }
|
|
const T& operator[](difference_type i) const { return m_ptr[i]; }
|
|
|
|
T &operator*() const { return *m_ptr; }
|
|
T* operator->() { return m_ptr; }
|
|
|
|
protected:
|
|
T* m_ptr;
|
|
};
|
|
|
|
// magic little helper. :)
|
|
template <class T>
|
|
class backwards
|
|
{
|
|
T& _obj;
|
|
public:
|
|
backwards(T &obj) : _obj(obj) {}
|
|
auto begin() {return _obj.rbegin();}
|
|
auto end() {return _obj.rend();}
|
|
};
|
|
|
|
|
|
// TArray -------------------------------------------------------------------
|
|
|
|
// Must match TArray's layout.
|
|
struct FArray
|
|
{
|
|
void *Array;
|
|
unsigned int Count;
|
|
unsigned int Most;
|
|
};
|
|
|
|
// T is the type stored in the array.
|
|
// TT is the type returned by operator().
|
|
template <class T, class TT=T>
|
|
class TArray
|
|
{
|
|
public:
|
|
|
|
typedef TIterator<T> iterator;
|
|
typedef TIterator<const T> const_iterator;
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
typedef T value_type;
|
|
|
|
iterator begin()
|
|
{
|
|
return &Array[0];
|
|
}
|
|
const_iterator begin() const
|
|
{
|
|
return &Array[0];
|
|
}
|
|
const_iterator cbegin() const
|
|
{
|
|
return &Array[0];
|
|
}
|
|
|
|
iterator end()
|
|
{
|
|
return &Array[Count];
|
|
}
|
|
const_iterator end() const
|
|
{
|
|
return &Array[Count];
|
|
}
|
|
const_iterator cend() const
|
|
{
|
|
return &Array[Count];
|
|
}
|
|
|
|
reverse_iterator rbegin()
|
|
{
|
|
return reverse_iterator(end());
|
|
}
|
|
const_reverse_iterator rbegin() const
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
const_reverse_iterator crbegin() const
|
|
{
|
|
return const_reverse_iterator(cend());
|
|
}
|
|
|
|
reverse_iterator rend()
|
|
{
|
|
return reverse_iterator(begin());
|
|
}
|
|
const_reverse_iterator rend() const
|
|
{
|
|
return const_reverse_iterator(begin());
|
|
}
|
|
const_reverse_iterator crend() const
|
|
{
|
|
return const_reverse_iterator(cbegin());
|
|
}
|
|
|
|
|
|
////////
|
|
// This is a dummy constructor that does nothing. The purpose of this
|
|
// is so you can create a global TArray in the data segment that gets
|
|
// used by code before startup without worrying about the constructor
|
|
// resetting it after it's already been used. You MUST NOT use it for
|
|
// heap- or stack-allocated TArrays.
|
|
enum ENoInit
|
|
{
|
|
NoInit
|
|
};
|
|
TArray (ENoInit dummy)
|
|
{
|
|
}
|
|
////////
|
|
TArray ()
|
|
{
|
|
Most = 0;
|
|
Count = 0;
|
|
Array = NULL;
|
|
}
|
|
explicit TArray (size_t max, bool reserve = false)
|
|
{
|
|
Most = (unsigned)max;
|
|
Count = (unsigned)(reserve? max : 0);
|
|
Array = (T *)M_Malloc (sizeof(T)*max);
|
|
if (reserve && Count > 0)
|
|
{
|
|
ConstructEmpty(0, Count - 1);
|
|
}
|
|
}
|
|
TArray (const TArray<T,TT> &other)
|
|
{
|
|
DoCopy (other);
|
|
}
|
|
TArray (TArray<T,TT> &&other)
|
|
{
|
|
Array = other.Array; other.Array = NULL;
|
|
Most = other.Most; other.Most = 0;
|
|
Count = other.Count; other.Count = 0;
|
|
}
|
|
TArray<T,TT> &operator= (const TArray<T,TT> &other)
|
|
{
|
|
if (&other != this)
|
|
{
|
|
if (Array != NULL)
|
|
{
|
|
if (Count > 0)
|
|
{
|
|
DoDelete (0, Count-1);
|
|
}
|
|
M_Free (Array);
|
|
}
|
|
DoCopy (other);
|
|
}
|
|
return *this;
|
|
}
|
|
TArray<T,TT> &operator= (TArray<T,TT> &&other)
|
|
{
|
|
if (Array)
|
|
{
|
|
if (Count > 0)
|
|
{
|
|
DoDelete (0, Count-1);
|
|
}
|
|
M_Free (Array);
|
|
}
|
|
Array = other.Array; other.Array = NULL;
|
|
Most = other.Most; other.Most = 0;
|
|
Count = other.Count; other.Count = 0;
|
|
return *this;
|
|
}
|
|
~TArray ()
|
|
{
|
|
if (Array)
|
|
{
|
|
if (Count > 0)
|
|
{
|
|
DoDelete (0, Count-1);
|
|
}
|
|
M_Free (Array);
|
|
Array = NULL;
|
|
Count = 0;
|
|
Most = 0;
|
|
}
|
|
}
|
|
// Check equality of two arrays
|
|
bool operator==(const TArray<T> &other) const
|
|
{
|
|
if (Count != other.Count)
|
|
{
|
|
return false;
|
|
}
|
|
for (unsigned int i = 0; i < Count; ++i)
|
|
{
|
|
if (Array[i] != other.Array[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
// Return a reference to an element.
|
|
// Note that the asserts must let the element after the end pass because this gets frequently used as a sentinel pointer.
|
|
T &operator[] (size_t index) const
|
|
{
|
|
assert(index <= Count);
|
|
return Array[index];
|
|
}
|
|
// Returns the value of an element
|
|
TT operator() (size_t index) const
|
|
{
|
|
assert(index <= Count);
|
|
return Array[index];
|
|
}
|
|
// Returns a reference to the last element
|
|
T &Last() const
|
|
{
|
|
return Array[Count-1];
|
|
}
|
|
|
|
// returns address of first element
|
|
T *Data() const
|
|
{
|
|
return &Array[0];
|
|
}
|
|
|
|
unsigned IndexOf(const T& elem) const
|
|
{
|
|
return &elem - Array;
|
|
}
|
|
|
|
unsigned IndexOf(const T* elem) const
|
|
{
|
|
return elem - Array;
|
|
}
|
|
|
|
unsigned int Find(const T& item) const
|
|
{
|
|
unsigned int i;
|
|
for(i = 0;i < Count;++i)
|
|
{
|
|
if(Array[i] == item)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
bool Contains(const T& item) const
|
|
{
|
|
unsigned int i;
|
|
for(i = 0;i < Count;++i)
|
|
{
|
|
if(Array[i] == item)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<class Func>
|
|
unsigned int FindEx(Func compare) const
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < Count; ++i)
|
|
{
|
|
if (compare(Array[i]))
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
unsigned int Push (const T &item)
|
|
{
|
|
Grow (1);
|
|
::new((void*)&Array[Count]) T(item);
|
|
return Count++;
|
|
}
|
|
|
|
unsigned int Push(T &&item)
|
|
{
|
|
Grow(1);
|
|
::new((void*)&Array[Count]) T(std::move(item));
|
|
return Count++;
|
|
}
|
|
|
|
unsigned Append(const TArray<T> &item)
|
|
{
|
|
unsigned start = Count;
|
|
|
|
Grow(item.Size());
|
|
Count += item.Size();
|
|
|
|
for (unsigned i = 0; i < item.Size(); i++)
|
|
{
|
|
new(&Array[start + i]) T(item[i]);
|
|
}
|
|
return start;
|
|
}
|
|
|
|
unsigned Append(TArray<T> &&item)
|
|
{
|
|
unsigned start = Count;
|
|
|
|
Grow(item.Size());
|
|
Count += item.Size();
|
|
|
|
for (unsigned i = 0; i < item.Size(); i++)
|
|
{
|
|
new(&Array[start + i]) T(std::move(item[i]));
|
|
}
|
|
item.Clear();
|
|
return start;
|
|
}
|
|
|
|
bool Pop ()
|
|
{
|
|
if (Count > 0)
|
|
{
|
|
Array[--Count].~T();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
bool Pop (T &item)
|
|
{
|
|
if (Count > 0)
|
|
{
|
|
item = Array[--Count];
|
|
Array[Count].~T();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
void Delete (unsigned int index)
|
|
{
|
|
if (index < Count)
|
|
{
|
|
Array[index].~T();
|
|
if (index < --Count)
|
|
{
|
|
// Cast to void to assume trivial move
|
|
memmove ((void*)&Array[index], (const void*)&Array[index+1], sizeof(T)*(Count - index));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Delete (unsigned int index, int deletecount)
|
|
{
|
|
if (index + deletecount > Count)
|
|
{
|
|
deletecount = Count - index;
|
|
}
|
|
if (deletecount > 0)
|
|
{
|
|
for (int i = 0; i < deletecount; i++)
|
|
{
|
|
Array[index + i].~T();
|
|
}
|
|
Count -= deletecount;
|
|
if (index < Count)
|
|
{
|
|
// Cast to void to assume trivial move
|
|
memmove ((void*)&Array[index], (const void*)&Array[index+deletecount], sizeof(T)*(Count - index));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Inserts an item into the array, shifting elements as needed
|
|
void Insert (unsigned int index, const T &item)
|
|
{
|
|
if (index >= Count)
|
|
{
|
|
// Inserting somewhere past the end of the array, so we can
|
|
// just add it without moving things.
|
|
Resize (index + 1);
|
|
::new ((void *)&Array[index]) T(item);
|
|
}
|
|
else
|
|
{
|
|
// Inserting somewhere in the middle of the array,
|
|
// so make room for it
|
|
Resize (Count + 1);
|
|
|
|
// Now move items from the index and onward out of the way
|
|
// Cast to void to assume trivial move
|
|
memmove ((void*)&Array[index+1], (const void*)&Array[index], sizeof(T)*(Count - index - 1));
|
|
|
|
// And put the new element in
|
|
::new ((void *)&Array[index]) T(item);
|
|
}
|
|
}
|
|
|
|
void ShrinkToFit ()
|
|
{
|
|
if (Most > Count)
|
|
{
|
|
Most = Count;
|
|
if (Most == 0)
|
|
{
|
|
if (Array != NULL)
|
|
{
|
|
M_Free (Array);
|
|
Array = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DoResize ();
|
|
}
|
|
}
|
|
}
|
|
// Grow Array to be large enough to hold amount more entries without
|
|
// further growing.
|
|
void Grow (unsigned int amount)
|
|
{
|
|
if (Count + amount > Most)
|
|
{
|
|
const unsigned int choicea = Count + amount;
|
|
const unsigned int choiceb = Most = (Most >= 16) ? Most + Most / 2 : 16;
|
|
Most = (choicea > choiceb ? choicea : choiceb);
|
|
DoResize ();
|
|
}
|
|
}
|
|
// Resize Array so that it has exactly amount entries in use.
|
|
void Resize (unsigned int amount)
|
|
{
|
|
if (Count < amount)
|
|
{
|
|
// Adding new entries
|
|
Grow (amount - Count);
|
|
ConstructEmpty(Count, amount - 1);
|
|
}
|
|
else if (Count != amount)
|
|
{
|
|
// Deleting old entries
|
|
DoDelete (amount, Count - 1);
|
|
}
|
|
Count = amount;
|
|
}
|
|
// Ensures that the array has at most amount entries.
|
|
// Useful in cases where the initial allocation may be larger than the final result.
|
|
// Resize would create a lot of unneeded code in those cases.
|
|
void Clamp(unsigned int amount)
|
|
{
|
|
if (Count > amount)
|
|
{
|
|
// Deleting old entries
|
|
DoDelete(amount, Count - 1);
|
|
Count = amount;
|
|
}
|
|
}
|
|
void Alloc(unsigned int amount)
|
|
{
|
|
// first destroys all content and then rebuilds the array.
|
|
if (Count > 0) DoDelete(0, Count - 1);
|
|
Count = 0;
|
|
Resize(amount);
|
|
ShrinkToFit();
|
|
}
|
|
// Reserves amount entries at the end of the array, but does nothing
|
|
// with them.
|
|
unsigned int Reserve (size_t amount)
|
|
{
|
|
Grow ((unsigned)amount);
|
|
unsigned int place = Count;
|
|
Count += (unsigned)amount;
|
|
if (Count > 0) ConstructEmpty(place, Count - 1);
|
|
return place;
|
|
}
|
|
unsigned int Size () const
|
|
{
|
|
return Count;
|
|
}
|
|
unsigned int Max () const
|
|
{
|
|
return Most;
|
|
}
|
|
void Clear ()
|
|
{
|
|
if (Count > 0)
|
|
{
|
|
DoDelete (0, Count-1);
|
|
Count = 0;
|
|
}
|
|
}
|
|
void Reset()
|
|
{
|
|
Clear();
|
|
Most = 0;
|
|
if (Array != nullptr)
|
|
{
|
|
M_Free(Array);
|
|
Array = nullptr;
|
|
}
|
|
}
|
|
|
|
void Swap(TArray<T, TT> &other)
|
|
{
|
|
std::swap(Array, other.Array);
|
|
std::swap(Count, other.Count);
|
|
std::swap(Most, other.Most);
|
|
}
|
|
|
|
private:
|
|
T *Array;
|
|
unsigned int Count;
|
|
unsigned int Most;
|
|
|
|
void DoCopy (const TArray<T> &other)
|
|
{
|
|
Most = Count = other.Count;
|
|
if (Count != 0)
|
|
{
|
|
Array = (T *)M_Malloc (sizeof(T)*Most);
|
|
for (unsigned int i = 0; i < Count; ++i)
|
|
{
|
|
::new(&Array[i]) T(other.Array[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Array = NULL;
|
|
}
|
|
}
|
|
|
|
void DoResize ()
|
|
{
|
|
size_t allocsize = sizeof(T)*Most;
|
|
Array = (T *)M_Realloc (Array, allocsize);
|
|
}
|
|
|
|
void DoDelete (unsigned int first, unsigned int last)
|
|
{
|
|
assert (last != ~0u);
|
|
for (unsigned int i = first; i <= last; ++i)
|
|
{
|
|
Array[i].~T();
|
|
}
|
|
}
|
|
|
|
void ConstructEmpty(unsigned int first, unsigned int last)
|
|
{
|
|
assert(last != ~0u);
|
|
for (unsigned int i = first; i <= last; ++i)
|
|
{
|
|
::new(&Array[i]) T;
|
|
}
|
|
}
|
|
};
|
|
|
|
// TDeletingArray -----------------------------------------------------------
|
|
// An array that deletes its elements when it gets deleted.
|
|
template<class T, class TT=T>
|
|
class TDeletingArray : public TArray<T, TT>
|
|
{
|
|
public:
|
|
TDeletingArray() : TArray<T,TT>() {}
|
|
TDeletingArray(TDeletingArray<T,TT> &&other) : TArray<T,TT>(std::move(other)) {}
|
|
TDeletingArray<T,TT> &operator=(TDeletingArray<T,TT> &&other)
|
|
{
|
|
TArray<T,TT>::operator=(std::move(other));
|
|
return *this;
|
|
}
|
|
|
|
~TDeletingArray<T, TT> ()
|
|
{
|
|
for (unsigned int i = 0; i < TArray<T,TT>::Size(); ++i)
|
|
{
|
|
if ((*this)[i] != NULL)
|
|
delete (*this)[i];
|
|
}
|
|
}
|
|
void DeleteAndClear()
|
|
{
|
|
for (unsigned int i = 0; i < TArray<T,TT>::Size(); ++i)
|
|
{
|
|
if ((*this)[i] != NULL)
|
|
delete (*this)[i];
|
|
}
|
|
this->Clear();
|
|
}
|
|
};
|
|
|
|
// This is only used for exposing the sector's Lines array to ZScript.
|
|
// Unlike TArrayView, its members are public as needed by the map loader.
|
|
|
|
template <class T>
|
|
class TStaticPointedArray
|
|
{
|
|
public:
|
|
|
|
typedef TIterator<T> iterator;
|
|
typedef TIterator<const T> const_iterator;
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
typedef T value_type;
|
|
|
|
iterator begin()
|
|
{
|
|
return &Array[0];
|
|
}
|
|
const_iterator begin() const
|
|
{
|
|
return &Array[0];
|
|
}
|
|
const_iterator cbegin() const
|
|
{
|
|
return &Array[0];
|
|
}
|
|
|
|
iterator end()
|
|
{
|
|
return &Array[Count];
|
|
}
|
|
const_iterator end() const
|
|
{
|
|
return &Array[Count];
|
|
}
|
|
const_iterator cend() const
|
|
{
|
|
return &Array[Count];
|
|
}
|
|
|
|
reverse_iterator rbegin()
|
|
{
|
|
return reverse_iterator(end());
|
|
}
|
|
const_reverse_iterator rbegin() const
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
const_reverse_iterator crbegin() const
|
|
{
|
|
return const_reverse_iterator(cend());
|
|
}
|
|
|
|
reverse_iterator rend()
|
|
{
|
|
return reverse_iterator(begin());
|
|
}
|
|
const_reverse_iterator rend() const
|
|
{
|
|
return const_reverse_iterator(begin());
|
|
}
|
|
const_reverse_iterator crend() const
|
|
{
|
|
return const_reverse_iterator(cbegin());
|
|
}
|
|
|
|
void Init(T *ptr, unsigned cnt)
|
|
{
|
|
Array = ptr;
|
|
Count = cnt;
|
|
}
|
|
// Return a reference to an element
|
|
T &operator[] (size_t index) const
|
|
{
|
|
return Array[index];
|
|
}
|
|
T &At(size_t index) const
|
|
{
|
|
return Array[index];
|
|
}
|
|
unsigned int Size() const
|
|
{
|
|
return Count;
|
|
}
|
|
// Some code needs to access these directly so they cannot be private.
|
|
T *Array;
|
|
unsigned int Count;
|
|
};
|
|
|
|
// TAutoGrowArray -----------------------------------------------------------
|
|
// An array with accessors that automatically grow the array as needed.
|
|
// It can still be used as a normal TArray if needed. ACS uses this for
|
|
// world and global arrays.
|
|
|
|
template <class T, class TT=T>
|
|
class TAutoGrowArray : public TArray<T, TT>
|
|
{
|
|
public:
|
|
T GetVal (unsigned int index)
|
|
{
|
|
if (index >= this->Size())
|
|
{
|
|
return 0;
|
|
}
|
|
return (*this)[index];
|
|
}
|
|
void SetVal (unsigned int index, T val)
|
|
{
|
|
if ((int)index < 0) return; // These always result in an out of memory condition.
|
|
|
|
if (index >= this->Size())
|
|
{
|
|
this->Resize (index + 1);
|
|
}
|
|
(*this)[index] = val;
|
|
}
|
|
};
|
|
|
|
// TMap ---------------------------------------------------------------------
|
|
// An associative array, similar in concept to the STL extension
|
|
// class hash_map. It is implemented using Lua's table algorithm:
|
|
/*
|
|
** Hash uses a mix of chained scatter table with Brent's variation.
|
|
** A main invariant of these tables is that, if an element is not
|
|
** in its main position (i.e. the `original' position that its hash gives
|
|
** to it), then the colliding element is in its own main position.
|
|
** Hence even when the load factor reaches 100%, performance remains good.
|
|
*/
|
|
/******************************************************************************
|
|
* Copyright (C) 1994-2006 Lua.org, PUC-Rio. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
typedef unsigned int hash_t;
|
|
|
|
template<class KT> struct THashTraits
|
|
{
|
|
// Returns the hash value for a key.
|
|
hash_t Hash(const KT key) { return (hash_t)(intptr_t)key; }
|
|
hash_t Hash(double key)
|
|
{
|
|
hash_t keyhash[2];
|
|
memcpy(&keyhash, &key, sizeof(keyhash));
|
|
return keyhash[0] ^ keyhash[1];
|
|
}
|
|
|
|
// Compares two keys, returning zero if they are the same.
|
|
int Compare(const KT left, const KT right) { return left != right; }
|
|
};
|
|
|
|
template<> struct THashTraits<float>
|
|
{
|
|
// Use all bits when hashing singles instead of converting them to ints.
|
|
hash_t Hash(float key)
|
|
{
|
|
hash_t keyhash;
|
|
memcpy(&keyhash, &key, sizeof(keyhash));
|
|
return keyhash;
|
|
}
|
|
int Compare(float left, float right) { return left != right; }
|
|
};
|
|
|
|
template<> struct THashTraits<double>
|
|
{
|
|
// Use all bits when hashing doubles instead of converting them to ints.
|
|
hash_t Hash(double key)
|
|
{
|
|
hash_t keyhash[2];
|
|
memcpy(&keyhash, &key, sizeof(keyhash));
|
|
return keyhash[0] ^ keyhash[1];
|
|
}
|
|
int Compare(double left, double right) { return left != right; }
|
|
};
|
|
|
|
template<class VT> struct TValueTraits
|
|
{
|
|
// Initializes a value for TMap. If a regular constructor isn't
|
|
// good enough, you can override it.
|
|
void Init(VT &value)
|
|
{
|
|
::new(&value) VT;
|
|
}
|
|
};
|
|
|
|
// Must match layout of TMap
|
|
struct FMap
|
|
{
|
|
void *Nodes;
|
|
void *LastFree;
|
|
hash_t Size;
|
|
hash_t NumUsed;
|
|
};
|
|
|
|
|
|
template<class KT, class VT, class MapType> class TMapIterator;
|
|
template<class KT, class VT, class MapType> class TMapConstIterator;
|
|
|
|
template<class KT, class VT, class HashTraits=THashTraits<KT>, class ValueTraits=TValueTraits<VT> >
|
|
class TMap
|
|
{
|
|
template<class KTa, class VTa, class MTa> friend class TMapIterator;
|
|
template<class KTb, class VTb, class MTb> friend class TMapConstIterator;
|
|
|
|
public:
|
|
typedef class TMap<KT, VT, HashTraits, ValueTraits> MyType;
|
|
typedef class TMapIterator<KT, VT, MyType> Iterator;
|
|
typedef class TMapConstIterator<KT, VT, MyType> ConstIterator;
|
|
typedef struct { const KT Key; VT Value; } Pair;
|
|
typedef const Pair ConstPair;
|
|
|
|
TMap() { NumUsed = 0; SetNodeVector(1); }
|
|
TMap(hash_t size) { NumUsed = 0; SetNodeVector(size); }
|
|
~TMap() { ClearNodeVector(); }
|
|
|
|
TMap(const TMap &o)
|
|
{
|
|
NumUsed = 0;
|
|
SetNodeVector(o.CountUsed());
|
|
CopyNodes(o.Nodes, o.Size);
|
|
}
|
|
|
|
TMap(TMap &&o)
|
|
{
|
|
Nodes = o.Nodes;
|
|
LastFree = o.LastFree; /* any free position is before this position */
|
|
Size = o.Size; /* must be a power of 2 */
|
|
NumUsed = o.NumUsed;
|
|
|
|
o.Size = 0;
|
|
o.NumUsed = 0;
|
|
o.SetNodeVector(1);
|
|
}
|
|
|
|
TMap &operator= (const TMap &o)
|
|
{
|
|
NumUsed = 0;
|
|
ClearNodeVector();
|
|
SetNodeVector(o.CountUsed());
|
|
CopyNodes(o.Nodes, o.Size);
|
|
return *this;
|
|
}
|
|
|
|
TMap &operator= (TMap &&o)
|
|
{
|
|
TransferFrom(o);
|
|
return *this;
|
|
}
|
|
|
|
//=======================================================================
|
|
//
|
|
// TransferFrom
|
|
//
|
|
// Moves the contents from one TMap to another, leaving the TMap moved
|
|
// from empty.
|
|
//
|
|
//=======================================================================
|
|
|
|
void TransferFrom(TMap &o)
|
|
{
|
|
// Clear all our nodes.
|
|
NumUsed = 0;
|
|
ClearNodeVector();
|
|
|
|
// Copy all of o's nodes.
|
|
Nodes = o.Nodes;
|
|
LastFree = o.LastFree;
|
|
Size = o.Size;
|
|
NumUsed = o.NumUsed;
|
|
|
|
// Tell o it doesn't have any nodes.
|
|
o.Nodes = NULL;
|
|
o.Size = 0;
|
|
o.LastFree = NULL;
|
|
o.NumUsed = 0;
|
|
|
|
// Leave o functional with one empty node.
|
|
o.SetNodeVector(1);
|
|
}
|
|
|
|
//=======================================================================
|
|
//
|
|
// Clear
|
|
//
|
|
// Empties out the table and resizes it with room for count entries.
|
|
//
|
|
//=======================================================================
|
|
|
|
void Clear(hash_t count=1)
|
|
{
|
|
ClearNodeVector();
|
|
SetNodeVector(count);
|
|
}
|
|
|
|
//=======================================================================
|
|
//
|
|
// CountUsed
|
|
//
|
|
// Returns the number of entries in use in the table.
|
|
//
|
|
//=======================================================================
|
|
|
|
hash_t CountUsed() const
|
|
{
|
|
#ifdef _DEBUG
|
|
hash_t used = 0;
|
|
hash_t ct = Size;
|
|
for (Node *n = Nodes; ct-- > 0; ++n)
|
|
{
|
|
if (!n->IsNil())
|
|
{
|
|
++used;
|
|
}
|
|
}
|
|
assert (used == NumUsed);
|
|
#endif
|
|
return NumUsed;
|
|
}
|
|
|
|
//=======================================================================
|
|
//
|
|
// operator[]
|
|
//
|
|
// Returns a reference to the value associated with a particular key,
|
|
// creating the pair if the key isn't already in the table.
|
|
//
|
|
//=======================================================================
|
|
|
|
VT &operator[] (const KT key)
|
|
{
|
|
return GetNode(key)->Pair.Value;
|
|
}
|
|
|
|
const VT &operator[] (const KT key) const
|
|
{
|
|
return GetNode(key)->Pair.Value;
|
|
}
|
|
|
|
//=======================================================================
|
|
//
|
|
// CheckKey
|
|
//
|
|
// Returns a pointer to the value associated with a particular key, or
|
|
// NULL if the key isn't in the table.
|
|
//
|
|
//=======================================================================
|
|
|
|
VT *CheckKey (const KT key)
|
|
{
|
|
Node *n = FindKey(key);
|
|
return n != NULL ? &n->Pair.Value : NULL;
|
|
}
|
|
|
|
const VT *CheckKey (const KT key) const
|
|
{
|
|
const Node *n = FindKey(key);
|
|
return n != NULL ? &n->Pair.Value : NULL;
|
|
}
|
|
|
|
//=======================================================================
|
|
//
|
|
// Insert
|
|
//
|
|
// Adds a key/value pair to the table if key isn't in the table, or
|
|
// replaces the value for the existing pair if the key is in the table.
|
|
//
|
|
// This is functionally equivalent to (*this)[key] = value; but can be
|
|
// slightly faster if the pair needs to be created because it doesn't run
|
|
// the constructor on the value part twice.
|
|
//
|
|
//=======================================================================
|
|
|
|
VT &Insert(const KT key, const VT &value)
|
|
{
|
|
Node *n = FindKey(key);
|
|
if (n != NULL)
|
|
{
|
|
n->Pair.Value = value;
|
|
}
|
|
else
|
|
{
|
|
n = NewKey(key);
|
|
::new(&n->Pair.Value) VT(value);
|
|
}
|
|
return n->Pair.Value;
|
|
}
|
|
|
|
VT &Insert(const KT key, VT &&value)
|
|
{
|
|
Node *n = FindKey(key);
|
|
if (n != NULL)
|
|
{
|
|
n->Pair.Value = value;
|
|
}
|
|
else
|
|
{
|
|
n = NewKey(key);
|
|
::new(&n->Pair.Value) VT(value);
|
|
}
|
|
return n->Pair.Value;
|
|
}
|
|
|
|
VT &InsertNew(const KT key)
|
|
{
|
|
Node *n = FindKey(key);
|
|
if (n != NULL)
|
|
{
|
|
n->Pair.Value.~VT();
|
|
}
|
|
else
|
|
{
|
|
n = NewKey(key);
|
|
}
|
|
::new(&n->Pair.Value) VT;
|
|
return n->Pair.Value;
|
|
}
|
|
|
|
//=======================================================================
|
|
//
|
|
// Remove
|
|
//
|
|
// Removes the key/value pair for a particular key if it is in the table.
|
|
//
|
|
//=======================================================================
|
|
|
|
void Remove(const KT key)
|
|
{
|
|
DelKey(key);
|
|
}
|
|
|
|
void Swap(MyType &other)
|
|
{
|
|
std::swap(Nodes, other.Nodes);
|
|
std::swap(LastFree, other.LastFree);
|
|
std::swap(Size, other.Size);
|
|
std::swap(NumUsed, other.NumUsed);
|
|
}
|
|
|
|
protected:
|
|
struct IPair // This must be the same as Pair above, but with a
|
|
{ // non-const Key.
|
|
KT Key;
|
|
VT Value;
|
|
};
|
|
struct Node
|
|
{
|
|
Node *Next;
|
|
IPair Pair;
|
|
void SetNil()
|
|
{
|
|
Next = (Node *)1;
|
|
}
|
|
bool IsNil() const
|
|
{
|
|
return Next == (Node *)1;
|
|
}
|
|
};
|
|
|
|
/* This is used instead of memcpy, because Node is likely to be small,
|
|
* such that the time spent calling a function would eclipse the time
|
|
* spent copying. */
|
|
struct NodeSizedStruct { unsigned char Pads[sizeof(Node)]; };
|
|
|
|
Node *Nodes;
|
|
Node *LastFree; /* any free position is before this position */
|
|
hash_t Size; /* must be a power of 2 */
|
|
hash_t NumUsed;
|
|
|
|
const Node *MainPosition(const KT k) const
|
|
{
|
|
HashTraits Traits;
|
|
return &Nodes[Traits.Hash(k) & (Size - 1)];
|
|
}
|
|
|
|
Node *MainPosition(const KT k)
|
|
{
|
|
HashTraits Traits;
|
|
return &Nodes[Traits.Hash(k) & (Size - 1)];
|
|
}
|
|
|
|
void SetNodeVector(hash_t size)
|
|
{
|
|
// Round size up to nearest power of 2
|
|
for (Size = 1; Size < size; Size <<= 1)
|
|
{ }
|
|
Nodes = (Node *)M_Malloc(Size * sizeof(Node));
|
|
LastFree = &Nodes[Size]; /* all positions are free */
|
|
for (hash_t i = 0; i < Size; ++i)
|
|
{
|
|
Nodes[i].SetNil();
|
|
}
|
|
}
|
|
|
|
void ClearNodeVector()
|
|
{
|
|
for (hash_t i = 0; i < Size; ++i)
|
|
{
|
|
if (!Nodes[i].IsNil())
|
|
{
|
|
Nodes[i].~Node();
|
|
}
|
|
}
|
|
M_Free(Nodes);
|
|
Nodes = NULL;
|
|
Size = 0;
|
|
LastFree = NULL;
|
|
NumUsed = 0;
|
|
}
|
|
|
|
void Resize(hash_t nhsize)
|
|
{
|
|
hash_t i, oldhsize = Size;
|
|
Node *nold = Nodes;
|
|
/* create new hash part with appropriate size */
|
|
SetNodeVector(nhsize);
|
|
/* re-insert elements from hash part */
|
|
NumUsed = 0;
|
|
for (i = 0; i < oldhsize; ++i)
|
|
{
|
|
if (!nold[i].IsNil())
|
|
{
|
|
Node *n = NewKey(nold[i].Pair.Key);
|
|
::new(&n->Pair.Value) VT(std::move(nold[i].Pair.Value));
|
|
nold[i].~Node();
|
|
}
|
|
}
|
|
M_Free(nold);
|
|
}
|
|
|
|
void Rehash()
|
|
{
|
|
Resize (Size << 1);
|
|
}
|
|
|
|
Node *GetFreePos()
|
|
{
|
|
while (LastFree-- > Nodes)
|
|
{
|
|
if (LastFree->IsNil())
|
|
{
|
|
return LastFree;
|
|
}
|
|
}
|
|
return NULL; /* could not find a free place */
|
|
}
|
|
|
|
/*
|
|
** Inserts a new key into a hash table; first, check whether key's main
|
|
** position is free. If not, check whether colliding node is in its main
|
|
** position or not: if it is not, move colliding node to an empty place and
|
|
** put new key in its main position; otherwise (colliding node is in its main
|
|
** position), new key goes to an empty position.
|
|
**
|
|
** The Value field is left unconstructed.
|
|
*/
|
|
Node *NewKey(const KT key)
|
|
{
|
|
Node *mp = MainPosition(key);
|
|
if (!mp->IsNil())
|
|
{
|
|
Node *othern;
|
|
Node *n = GetFreePos(); /* get a free place */
|
|
if (n == NULL) /* cannot find a free place? */
|
|
{
|
|
Rehash(); /* grow table */
|
|
return NewKey(key); /* re-insert key into grown table */
|
|
}
|
|
othern = MainPosition(mp->Pair.Key);
|
|
if (othern != mp) /* is colliding node out of its main position? */
|
|
{ /* yes; move colliding node into free position */
|
|
while (othern->Next != mp) /* find previous */
|
|
{
|
|
othern = othern->Next;
|
|
}
|
|
othern->Next = n; /* redo the chain with 'n' in place of 'mp' */
|
|
CopyNode(n, mp); /* copy colliding node into free pos. (mp->Next also goes) */
|
|
mp->Next = NULL; /* now 'mp' is free */
|
|
}
|
|
else /* colliding node is in its own main position */
|
|
{ /* new node will go into free position */
|
|
n->Next = mp->Next; /* chain new position */
|
|
mp->Next = n;
|
|
mp = n;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mp->Next = NULL;
|
|
}
|
|
++NumUsed;
|
|
::new(&mp->Pair.Key) KT(key);
|
|
return mp;
|
|
}
|
|
|
|
void DelKey(const KT key)
|
|
{
|
|
Node *mp = MainPosition(key), **mpp;
|
|
HashTraits Traits;
|
|
|
|
if (mp->IsNil())
|
|
{
|
|
/* the key is definitely not present, because there is nothing at its main position */
|
|
}
|
|
else if (!Traits.Compare(mp->Pair.Key, key)) /* the key is in its main position */
|
|
{
|
|
if (mp->Next != NULL) /* move next node to its main position */
|
|
{
|
|
Node *n = mp->Next;
|
|
mp->~Node(); /* deconstruct old node */
|
|
CopyNode(mp, n); /* copy next node */
|
|
n->SetNil(); /* next node is now nil */
|
|
}
|
|
else
|
|
{
|
|
mp->~Node();
|
|
mp->SetNil(); /* there is no chain, so main position is nil */
|
|
}
|
|
--NumUsed;
|
|
}
|
|
else /* the key is either not present or not in its main position */
|
|
{
|
|
for (mpp = &mp->Next, mp = *mpp; mp != NULL && Traits.Compare(mp->Pair.Key, key); mpp = &mp->Next, mp = *mpp)
|
|
{ } /* look for the key */
|
|
if (mp != NULL) /* found it */
|
|
{
|
|
*mpp = mp->Next; /* rechain so this node is skipped */
|
|
mp->~Node();
|
|
mp->SetNil(); /* because this node is now nil */
|
|
--NumUsed;
|
|
}
|
|
}
|
|
}
|
|
|
|
Node *FindKey(const KT key)
|
|
{
|
|
HashTraits Traits;
|
|
Node *n = MainPosition(key);
|
|
while (n != NULL && !n->IsNil() && Traits.Compare(n->Pair.Key, key))
|
|
{
|
|
n = n->Next;
|
|
}
|
|
return n == NULL || n->IsNil() ? NULL : n;
|
|
}
|
|
|
|
const Node *FindKey(const KT key) const
|
|
{
|
|
HashTraits Traits;
|
|
const Node *n = MainPosition(key);
|
|
while (n != NULL && !n->IsNil() && Traits.Compare(n->Pair.Key, key))
|
|
{
|
|
n = n->Next;
|
|
}
|
|
return n == NULL || n->IsNil() ? NULL : n;
|
|
}
|
|
|
|
Node *GetNode(const KT key)
|
|
{
|
|
Node *n = FindKey(key);
|
|
if (n != NULL)
|
|
{
|
|
return n;
|
|
}
|
|
n = NewKey(key);
|
|
ValueTraits traits;
|
|
traits.Init(n->Pair.Value);
|
|
return n;
|
|
}
|
|
|
|
/* Perform a bit-wise copy of the node. Used when relocating a node in the table. */
|
|
void CopyNode(Node *dst, const Node *src)
|
|
{
|
|
*(NodeSizedStruct *)dst = *(const NodeSizedStruct *)src;
|
|
}
|
|
|
|
/* Copy all nodes in the node vector to this table. */
|
|
void CopyNodes(const Node *nodes, hash_t numnodes)
|
|
{
|
|
for (; numnodes-- > 0; ++nodes)
|
|
{
|
|
if (!nodes->IsNil())
|
|
{
|
|
Node *n = NewKey(nodes->Pair.Key);
|
|
::new(&n->Pair.Value) VT(nodes->Pair.Value);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// TMapIterator -------------------------------------------------------------
|
|
// A class to iterate over all the pairs in a TMap.
|
|
|
|
template<class KT, class VT, class MapType=TMap<KT,VT> >
|
|
class TMapIterator
|
|
{
|
|
public:
|
|
TMapIterator(MapType &map)
|
|
: Map(map), Position(0)
|
|
{
|
|
}
|
|
|
|
//=======================================================================
|
|
//
|
|
// NextPair
|
|
//
|
|
// Returns false if there are no more entries in the table. Otherwise, it
|
|
// returns true, and pair is filled with a pointer to the pair in the
|
|
// table.
|
|
//
|
|
//=======================================================================
|
|
|
|
bool NextPair(typename MapType::Pair *&pair)
|
|
{
|
|
if (Position >= Map.Size)
|
|
{
|
|
return false;
|
|
}
|
|
do
|
|
{
|
|
if (!Map.Nodes[Position].IsNil())
|
|
{
|
|
pair = reinterpret_cast<typename MapType::Pair *>(&Map.Nodes[Position].Pair);
|
|
Position += 1;
|
|
return true;
|
|
}
|
|
} while (++Position < Map.Size);
|
|
return false;
|
|
}
|
|
|
|
//=======================================================================
|
|
//
|
|
// Reset
|
|
//
|
|
// Restarts the iteration so you can do it all over again.
|
|
//
|
|
//=======================================================================
|
|
|
|
void Reset()
|
|
{
|
|
Position = 0;
|
|
}
|
|
|
|
protected:
|
|
MapType ⤅
|
|
hash_t Position;
|
|
};
|
|
|
|
// TMapConstIterator --------------------------------------------------------
|
|
// Exactly the same as TMapIterator, but it works with a const TMap.
|
|
|
|
template<class KT, class VT, class MapType=TMap<KT,VT> >
|
|
class TMapConstIterator
|
|
{
|
|
public:
|
|
TMapConstIterator(const MapType &map)
|
|
: Map(map), Position(0)
|
|
{
|
|
}
|
|
|
|
bool NextPair(typename MapType::ConstPair *&pair)
|
|
{
|
|
if (Position >= Map.Size)
|
|
{
|
|
return false;
|
|
}
|
|
do
|
|
{
|
|
if (!Map.Nodes[Position].IsNil())
|
|
{
|
|
pair = reinterpret_cast<typename MapType::Pair *>(&Map.Nodes[Position].Pair);
|
|
Position += 1;
|
|
return true;
|
|
}
|
|
} while (++Position < Map.Size);
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
const MapType ⤅
|
|
hash_t Position;
|
|
};
|
|
|
|
|
|
// Pointer wrapper without the unpleasant side effects of std::unique_ptr, mainly the inability to copy it.
|
|
// This class owns the object with no means to release it, and copying the pointer copies the object.
|
|
template <class T>
|
|
class TPointer
|
|
{
|
|
public:
|
|
|
|
////////
|
|
TPointer()
|
|
{
|
|
Ptr = nullptr;
|
|
}
|
|
TPointer(const T& other) = delete;
|
|
/*
|
|
{
|
|
Alloc();
|
|
*Ptr = other;
|
|
}
|
|
*/
|
|
TPointer(T&& other)
|
|
{
|
|
Alloc();
|
|
*Ptr = other;
|
|
}
|
|
TPointer(const TPointer<T>& other) = delete;
|
|
/*
|
|
{
|
|
DoCopy(other);
|
|
}
|
|
*/
|
|
TPointer(TPointer<T>&& other)
|
|
{
|
|
Ptr = other.Ptr;
|
|
other.Ptr = nullptr;
|
|
}
|
|
TPointer<T>& operator= (const T& other)
|
|
{
|
|
if (&other != this)
|
|
{
|
|
Alloc();
|
|
*Ptr = other;
|
|
}
|
|
return *this;
|
|
}
|
|
TPointer<T>& operator= (const TPointer<T>& other)
|
|
{
|
|
if (&other != this)
|
|
{
|
|
DoCopy(other);
|
|
}
|
|
return *this;
|
|
}
|
|
TPointer<T>& operator= (TPointer<T>&& other)
|
|
{
|
|
if (&other != this)
|
|
{
|
|
if (Ptr) delete Ptr;
|
|
Ptr = other.Ptr;
|
|
other.Ptr = nullptr;
|
|
}
|
|
return *this;
|
|
}
|
|
~TPointer()
|
|
{
|
|
if (Ptr) delete Ptr;
|
|
Ptr = nullptr;
|
|
}
|
|
// Check equality of two pointers
|
|
bool operator==(const TPointer<T>& other) const
|
|
{
|
|
return *Ptr == *other.Ptr;
|
|
}
|
|
|
|
T& operator* () const
|
|
{
|
|
assert(Ptr);
|
|
return *Ptr;
|
|
}
|
|
|
|
T* operator->() { return Ptr; }
|
|
|
|
// returns raw pointer
|
|
T* Data() const
|
|
{
|
|
return Ptr;
|
|
}
|
|
|
|
#if 0 // this is too dangerous.
|
|
operator T* () const
|
|
{
|
|
return Ptr;
|
|
}
|
|
#endif
|
|
|
|
void Alloc()
|
|
{
|
|
if (!Ptr) Ptr = new T;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
if (Ptr) delete Ptr;
|
|
Ptr = nullptr;
|
|
}
|
|
|
|
void Swap(TPointer<T>& other)
|
|
{
|
|
std::swap(Ptr, other.Ptr);
|
|
}
|
|
|
|
private:
|
|
T* Ptr;
|
|
|
|
void DoCopy(const TPointer<T>& other)
|
|
{
|
|
if (other.Ptr == nullptr)
|
|
{
|
|
Clear();
|
|
}
|
|
else
|
|
{
|
|
Alloc();
|
|
*Ptr = *other.Ptr;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// an array to hold a small number of unique entries
|
|
//
|
|
//==========================================================================
|
|
|
|
template<class T> class UniqueList
|
|
{
|
|
TArray<T*> Array;
|
|
|
|
public:
|
|
|
|
T * Get(T * t)
|
|
{
|
|
for (unsigned i = 0; i<Array.Size(); i++)
|
|
{
|
|
if (!memcmp(t, Array[i], sizeof(T))) return Array[i];
|
|
}
|
|
auto newo = new T(*t);
|
|
Array.Push(newo);
|
|
return newo;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
for (unsigned i = 0; i<Array.Size(); i++) delete Array[i];
|
|
Array.Clear();
|
|
}
|
|
|
|
~UniqueList()
|
|
{
|
|
Clear();
|
|
}
|
|
};
|
|
|
|
|
|
class BitArray
|
|
{
|
|
TArray<uint8_t> bytes;
|
|
unsigned size;
|
|
|
|
public:
|
|
void Resize(unsigned elem)
|
|
{
|
|
bytes.Resize((elem + 7) / 8);
|
|
size = elem;
|
|
}
|
|
|
|
BitArray() : size(0)
|
|
{
|
|
}
|
|
|
|
BitArray(unsigned elem)
|
|
: bytes((elem + 7) / 8, true), size(elem)
|
|
{
|
|
|
|
}
|
|
|
|
BitArray(const BitArray & arr)
|
|
: bytes(arr.bytes)
|
|
{
|
|
size = arr.size;
|
|
}
|
|
|
|
BitArray &operator=(const BitArray & arr)
|
|
{
|
|
bytes = arr.bytes;
|
|
size = arr.size;
|
|
return *this;
|
|
}
|
|
|
|
BitArray(BitArray && arr)
|
|
: bytes(std::move(arr.bytes))
|
|
{
|
|
size = arr.size;
|
|
arr.size = 0;
|
|
}
|
|
|
|
BitArray &operator=(BitArray && arr)
|
|
{
|
|
bytes = std::move(arr.bytes);
|
|
size = arr.size;
|
|
arr.size = 0;
|
|
return *this;
|
|
}
|
|
|
|
bool operator[](size_t index) const
|
|
{
|
|
return !!(bytes[index >> 3] & (1 << (index & 7)));
|
|
}
|
|
|
|
// for when array syntax cannot be used.
|
|
bool Check(size_t index) const
|
|
{
|
|
return !!(bytes[index >> 3] & (1 << (index & 7)));
|
|
}
|
|
|
|
void Set(size_t index, bool set = true)
|
|
{
|
|
if (!set) Clear(index);
|
|
else bytes[index >> 3] |= (1 << (index & 7));
|
|
}
|
|
|
|
void Clear(size_t index)
|
|
{
|
|
bytes[index >> 3] &= ~(1 << (index & 7));
|
|
}
|
|
|
|
unsigned Size() const
|
|
{
|
|
return size;
|
|
}
|
|
|
|
void Zero()
|
|
{
|
|
memset(&bytes[0], 0, bytes.Size());
|
|
}
|
|
|
|
TArray<uint8_t> &Storage()
|
|
{
|
|
return bytes;
|
|
}
|
|
};
|
|
|
|
|
|
template<int size>
|
|
class FixedBitArray
|
|
{
|
|
uint8_t bytes[(size + 7) / 8];
|
|
|
|
public:
|
|
|
|
FixedBitArray() = default;
|
|
FixedBitArray(bool set)
|
|
{
|
|
memset(bytes, set ? -1 : 0, sizeof(bytes));
|
|
}
|
|
|
|
bool operator[](size_t index) const
|
|
{
|
|
return !!(bytes[index >> 3] & (1 << (index & 7)));
|
|
}
|
|
|
|
void Set(size_t index, bool set = true)
|
|
{
|
|
if (!set) Clear(index);
|
|
else bytes[index >> 3] |= (1 << (index & 7));
|
|
}
|
|
|
|
void Clear(size_t index)
|
|
{
|
|
bytes[index >> 3] &= ~(1 << (index & 7));
|
|
}
|
|
|
|
constexpr unsigned Size() const
|
|
{
|
|
return size;
|
|
}
|
|
|
|
void Zero()
|
|
{
|
|
memset(&bytes[0], 0, sizeof(bytes));
|
|
}
|
|
|
|
void SetAll(bool on)
|
|
{
|
|
memset(&bytes[0], on ? -1 : 0, sizeof(bytes));
|
|
}
|
|
|
|
// These are for utilities that need access to the raw storage. The serializer needs this to do its work, for example.
|
|
uint8_t* Storage()
|
|
{
|
|
return bytes;
|
|
}
|
|
unsigned StorageSize() const
|
|
{
|
|
return sizeof(bytes);
|
|
}
|
|
};
|
|
|
|
// A wrapper to externally stored data.
|
|
// I would have expected something for this in the stl, but std::span is only in C++20.
|
|
template <class T>
|
|
class TArrayView
|
|
{
|
|
public:
|
|
|
|
typedef TIterator<T> iterator;
|
|
typedef TIterator<const T> const_iterator;
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
typedef T value_type;
|
|
|
|
iterator begin()
|
|
{
|
|
return &Array[0];
|
|
}
|
|
const_iterator begin() const
|
|
{
|
|
return &Array[0];
|
|
}
|
|
const_iterator cbegin() const
|
|
{
|
|
return &Array[0];
|
|
}
|
|
|
|
iterator end()
|
|
{
|
|
return &Array[Count];
|
|
}
|
|
const_iterator end() const
|
|
{
|
|
return &Array[Count];
|
|
}
|
|
const_iterator cend() const
|
|
{
|
|
return &Array[Count];
|
|
}
|
|
|
|
reverse_iterator rbegin()
|
|
{
|
|
return reverse_iterator(end());
|
|
}
|
|
const_reverse_iterator rbegin() const
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
const_reverse_iterator crbegin() const
|
|
{
|
|
return const_reverse_iterator(cend());
|
|
}
|
|
|
|
reverse_iterator rend()
|
|
{
|
|
return reverse_iterator(begin());
|
|
}
|
|
const_reverse_iterator rend() const
|
|
{
|
|
return const_reverse_iterator(begin());
|
|
}
|
|
const_reverse_iterator crend() const
|
|
{
|
|
return const_reverse_iterator(cbegin());
|
|
}
|
|
|
|
|
|
////////
|
|
TArrayView() = default; // intended to keep this type trivial.
|
|
TArrayView(T *data, unsigned count = 0)
|
|
{
|
|
Count = count;
|
|
Array = data;
|
|
}
|
|
TArrayView(const TArrayView<T> &other) = default;
|
|
TArrayView<T> &operator= (const TArrayView<T> &other) = default;
|
|
|
|
// Check equality of two arrays
|
|
bool operator==(const TArrayView<T> &other) const
|
|
{
|
|
if (Count != other.Count)
|
|
{
|
|
return false;
|
|
}
|
|
for (unsigned int i = 0; i < Count; ++i)
|
|
{
|
|
if (Array[i] != other.Array[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
// Return a reference to an element
|
|
T &operator[] (size_t index) const
|
|
{
|
|
assert(index < Count);
|
|
return Array[index];
|
|
}
|
|
// Returns a reference to the last element
|
|
T &Last() const
|
|
{
|
|
assert(Count > 0);
|
|
return Array[Count - 1];
|
|
}
|
|
|
|
// returns address of first element
|
|
T *Data() const
|
|
{
|
|
return Array;
|
|
}
|
|
|
|
unsigned Size() const
|
|
{
|
|
return Count;
|
|
}
|
|
|
|
unsigned int Find(const T& item) const
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < Count; ++i)
|
|
{
|
|
if (Array[i] == item)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
void Set(T *data, unsigned count)
|
|
{
|
|
Array = data;
|
|
Count = count;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
Count = 0;
|
|
Array = nullptr;
|
|
}
|
|
private:
|
|
T *Array;
|
|
unsigned int Count;
|
|
};
|
|
|
|
#if !__has_include("m_alloc.h")
|
|
#undef M_Malloc
|
|
#undef M_Realloc
|
|
#undef M_Free
|
|
#endif
|