summaryrefslogtreecommitdiff
path: root/Runtime/Utilities/dynamic_array.h
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Utilities/dynamic_array.h
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Utilities/dynamic_array.h')
-rw-r--r--Runtime/Utilities/dynamic_array.h340
1 files changed, 340 insertions, 0 deletions
diff --git a/Runtime/Utilities/dynamic_array.h b/Runtime/Utilities/dynamic_array.h
new file mode 100644
index 0000000..90573d9
--- /dev/null
+++ b/Runtime/Utilities/dynamic_array.h
@@ -0,0 +1,340 @@
+#pragma once
+
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Utilities/StaticAssert.h"
+
+#include <memory> // std::uninitialized_fill
+
+// dynamic_array - simplified version of std::vector<T>
+//
+// features:
+// . always uses memcpy for copying elements. Your data structures must be simple and can't have internal pointers / rely on copy constructor.
+// . EASTL like push_back(void) implementation
+// Existing std STL implementations implement insertion operations by copying from an element.
+// For example, resize(size() + 1) creates a throw-away temporary object.
+// There is no way in existing std STL implementations to add an element to a container without implicitly or
+// explicitly providing one to copy from (aside from some existing POD optimizations).
+// For expensive-to-construct objects this creates a potentially serious performance problem.
+// . grows X2 on reallocation
+// . small code footprint
+// . clear actually deallocates memory
+// . resize does NOT initialize members!
+//
+// Changelog:
+// Added pop_back()
+// Added assign()
+// Added clear() - frees the data, use resize(0) to clear w/o freeing
+// zero allocation for empty array
+//
+//
+template<typename T>
+struct AlignOfType
+{
+ enum { align = ALIGN_OF(T) };
+};
+
+
+template <typename T, size_t align = AlignOfType<T>::align, MemLabelIdentifier defaultLabel = kMemDynamicArrayId>
+struct dynamic_array
+{
+public:
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ typedef T value_type;
+ typedef size_t size_type;
+ typedef size_t difference_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+
+public:
+
+ dynamic_array() : m_data(NULL), m_label(defaultLabel, NULL), m_size(0), m_capacity(0)
+ {
+ m_label = MemLabelId(defaultLabel, GET_CURRENT_ALLOC_ROOT_HEADER());
+ }
+
+ dynamic_array(MemLabelRef label) : m_data(NULL), m_label(label), m_size(0), m_capacity(0)
+ {
+ }
+
+ explicit dynamic_array (size_t size, MemLabelRef label)
+ : m_label(label), m_size(size), m_capacity (size)
+ {
+ m_data = allocate (size);
+ }
+
+ dynamic_array (size_t size, T const& init_value, MemLabelRef label)
+ : m_label(label), m_size (size), m_capacity (size)
+ {
+ m_data = allocate (size);
+ std::uninitialized_fill (m_data, m_data + size, init_value);
+ }
+
+ ~dynamic_array()
+ {
+ if (owns_data())
+ m_data = deallocate(m_data);
+ }
+
+ dynamic_array(const dynamic_array& other) : m_capacity(0), m_size(0), m_label(other.m_label)
+ {
+ //m_label.SetRootHeader(GET_CURRENT_ALLOC_ROOT_HEADER());
+ m_data = NULL;
+ assign(other.begin(), other.end());
+ }
+
+ dynamic_array& operator=(const dynamic_array& other)
+ {
+ // should not allocate memory unless we have to
+ assign(other.begin(), other.end());
+ return *this;
+ }
+
+ void clear()
+ {
+ if (owns_data())
+ m_data = deallocate(m_data);
+ m_size = 0;
+ m_capacity = 0;
+ }
+
+ void assign(const_iterator begin, const_iterator end)
+ {
+ Assert(begin<=end);
+
+ resize_uninitialized(end-begin);
+ memcpy(m_data, begin, m_size * sizeof(T));
+ }
+
+ void erase(iterator input_begin, iterator input_end)
+ {
+ Assert(input_begin <= input_end);
+ Assert(input_begin >= begin());
+ Assert(input_end <= end());
+
+ size_t leftOverSize = end() - input_end;
+ memmove(input_begin, input_end, leftOverSize * sizeof(T));
+ m_size -= input_end - input_begin;
+ }
+
+ iterator erase(iterator position)
+ {
+ Assert(position >= begin());
+ Assert(position < end());
+
+ size_t leftOverSize = end() - position - 1;
+ memmove(position, position+1, leftOverSize * sizeof(T));
+ m_size -= 1;
+
+ return position;
+ }
+
+ iterator insert(iterator insert_before, const_iterator input_begin, const_iterator input_end)
+ {
+ Assert(input_begin <= input_end);
+ Assert(insert_before >= begin());
+ Assert(insert_before <= end());
+
+ // resize (make sure that insertBefore does not get invalid in the meantime because of a reallocation)
+ size_t insert_before_index = insert_before - begin();
+ size_t elements_to_be_moved = size() - insert_before_index;
+ resize_uninitialized((input_end - input_begin) + size());
+ insert_before = begin() + insert_before_index;
+
+ size_t insertsize = input_end - input_begin;
+ // move to the end of where the inserted data will be
+ memmove(insert_before + insertsize, insert_before, elements_to_be_moved * sizeof(T));
+ // inject input data in the hole we just created
+ memcpy(insert_before, input_begin, insertsize * sizeof(T));
+
+ return insert_before;
+ }
+
+ iterator insert(iterator insertBefore, const T& t) { return insert(insertBefore, &t, &t + 1); }
+
+ void swap(dynamic_array& other) throw()
+ {
+ if (m_data) UNITY_TRANSFER_OWNERSHIP_TO_HEADER(m_data, m_label, other.m_label.GetRootHeader());
+ if (other.m_data) UNITY_TRANSFER_OWNERSHIP_TO_HEADER(other.m_data, other.m_label, m_label.GetRootHeader());
+ std::swap(m_data, other.m_data);
+ std::swap(m_size, other.m_size);
+ std::swap(m_capacity, other.m_capacity);
+ std::swap(m_label, other.m_label);
+ }
+
+ T& push_back()
+ {
+ if (++m_size > capacity())
+ reserve(std::max<size_t>(capacity()*2, 1));
+ return back();
+ }
+
+ void push_back(const T& t)
+ {
+ push_back() = t;
+ }
+
+ void pop_back()
+ {
+ Assert(m_size >= 1);
+ m_size--;
+ }
+
+ void resize_uninitialized(size_t size, bool double_on_resize = false)
+ {
+ m_size = size;
+ if (m_size <= capacity())
+ return;
+
+ if(double_on_resize && size < capacity()*2)
+ size = capacity()*2;
+ reserve(size);
+ }
+
+ void resize_initialized(size_t size, const T& t = T(), bool double_on_resize = false)
+ {
+ if (size > capacity())
+ {
+ size_t requested_size = size;
+ if(double_on_resize && size < capacity()*2)
+ requested_size = capacity()*2;
+ reserve(requested_size);
+ }
+
+ if (size > m_size)
+ std::uninitialized_fill (m_data + m_size, m_data + size, t);
+ m_size = size;
+ }
+
+ void reserve(size_t inCapacity)
+ {
+ if (capacity() >= inCapacity)
+ return;
+
+ if (owns_data())
+ {
+ m_capacity = inCapacity;
+ m_data = reallocate(m_data, inCapacity);
+ }
+ else
+ {
+ T* newData = allocate(inCapacity);
+ memcpy(newData, m_data, m_size * sizeof(T));
+
+ // Invalidate old non-owned data, since using the data from two places is most likely a really really bad idea.
+ #if DEBUGMODE
+ memset(m_data, 0xCD, capacity() * sizeof(T));
+ #endif
+
+ m_capacity = inCapacity; // and clear reference bit
+ m_data = newData;
+ }
+ }
+
+ void assign_external (T* begin, T* end)
+ {
+ if (owns_data())
+ m_data = deallocate(m_data);
+ m_size = m_capacity = reinterpret_cast<value_type*> (end) - reinterpret_cast<value_type*> (begin);
+ Assert(m_size < k_reference_bit);
+ m_capacity |= k_reference_bit;
+ m_data = begin;
+ }
+
+ void set_owns_data (bool ownsData)
+ {
+ if (ownsData)
+ m_capacity &= ~k_reference_bit;
+ else
+ m_capacity |= k_reference_bit;
+ }
+
+ void shrink_to_fit()
+ {
+ if (owns_data())
+ {
+ m_capacity = m_size;
+ m_data = reallocate(m_data, m_size);
+ }
+ }
+
+ const T& back() const { Assert (m_size != 0); return m_data[m_size - 1]; }
+ const T& front() const { Assert (m_size != 0); return m_data[0]; }
+
+ T& back() { Assert (m_size != 0); return m_data[m_size - 1]; }
+ T& front() { Assert (m_size != 0); return m_data[0]; }
+
+ T* data () { return m_data; }
+ T const* data () const { return m_data; }
+
+ bool empty () const { return m_size == 0; }
+ size_t size() const { return m_size; }
+ size_t capacity() const { return m_capacity & ~k_reference_bit; }
+
+ T const& operator[] (size_t index) const { DebugAssert(index < m_size); return m_data[index]; }
+ T& operator[] (size_t index) { DebugAssert(index < m_size); return m_data[index]; }
+
+ T const* begin() const { return m_data; }
+ T* begin() { return m_data; }
+
+ T const* end() const { return m_data + m_size; }
+ T* end() { return m_data + m_size; }
+
+ bool owns_data() { return (m_capacity & k_reference_bit) == 0; }
+
+ bool equals(const dynamic_array& other)
+ {
+ if(m_size != other.m_size)
+ return false;
+
+ for( int i = 0; i < m_size; i++)
+ {
+ if (m_data[i] != other.m_data[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ void set_memory_label (MemLabelRef label)
+ {
+ Assert(m_data == NULL);
+ m_label = label;
+ }
+
+private:
+
+ static const size_t k_reference_bit = (size_t)1 << (sizeof (size_t) * 8 - 1);
+
+ T* allocate (size_t size)
+ {
+ // If you are getting this error then you are trying to allocate memory for an incomplete type
+ CompileTimeAssert(sizeof(T) != 0, "incomplete type");
+ CompileTimeAssert(align != 0, "incomplete type");
+
+ return static_cast<T*> (UNITY_MALLOC_ALIGNED (m_label, size * sizeof(T), align));
+ }
+
+ T* deallocate (T* data)
+ {
+ Assert(owns_data());
+ UNITY_FREE (m_label, data);
+ return NULL;
+ }
+
+ T* reallocate (T* data, size_t size)
+ {
+ // If you are getting this error then you are trying to allocate memory for an incomplete type
+ CompileTimeAssert(sizeof(T) != 0, "incomplete type");
+ CompileTimeAssert(align != 0, "incomplete type");
+
+ Assert(owns_data());
+ int alignof = static_cast<int>(align);
+ return static_cast<T*> (UNITY_REALLOC_ALIGNED(m_label, data, size * sizeof(T), alignof));
+ }
+
+ T* m_data;
+ MemLabelId m_label;
+ size_t m_size;
+ size_t m_capacity;
+};