dynamic_array.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. #pragma once
  2. #include "Memory.h"
  3. #include <algorithm> // std::max
  4. #include <memory> // std::uninitialized_fill
  5. // dynamic_array - simplified version of std::vector<T>
  6. //
  7. // features:
  8. // . always uses memcpy for copying elements. Your data structures must be simple and can't have internal pointers / rely on copy constructor.
  9. // . EASTL like push_back(void) implementation
  10. // Existing std STL implementations implement insertion operations by copying from an element.
  11. // For example, resize(size() + 1) creates a throw-away temporary object.
  12. // There is no way in existing std STL implementations to add an element to a container without implicitly or
  13. // explicitly providing one to copy from (aside from some existing POD optimizations).
  14. // For expensive-to-construct objects this creates a potentially serious performance problem.
  15. // . grows X2 on reallocation
  16. // . small code footprint
  17. // . clear actually deallocates memory
  18. // . resize does NOT initialize members!
  19. //
  20. // Changelog:
  21. // Added pop_back()
  22. // Added assign()
  23. // Added clear() - frees the data, use resize(0) to clear w/o freeing
  24. // zero allocation for empty array
  25. //
  26. namespace il2cpp
  27. {
  28. namespace utils
  29. {
  30. template<typename T>
  31. struct AlignOfType
  32. {
  33. enum
  34. {
  35. align = ALIGN_OF(T)
  36. };
  37. };
  38. template<typename T, size_t ALIGN = AlignOfType<T>::align>
  39. struct dynamic_array
  40. {
  41. public:
  42. enum
  43. {
  44. align = ALIGN
  45. };
  46. typedef T *iterator;
  47. typedef const T *const_iterator;
  48. typedef T value_type;
  49. typedef size_t size_type;
  50. typedef size_t difference_type;
  51. typedef T &reference;
  52. typedef const T &const_reference;
  53. public:
  54. dynamic_array() : m_data(NULL), m_size(0), m_capacity(0)
  55. {
  56. }
  57. explicit dynamic_array(size_t size)
  58. : m_size(size), m_capacity(size)
  59. {
  60. m_data = allocate(size);
  61. }
  62. dynamic_array(size_t size, T const &init_value)
  63. : m_size(size), m_capacity(size)
  64. {
  65. m_data = allocate(size);
  66. std::uninitialized_fill(m_data, m_data + size, init_value);
  67. }
  68. ~dynamic_array()
  69. {
  70. if (owns_data())
  71. m_data = deallocate(m_data);
  72. }
  73. dynamic_array(const dynamic_array &other) : m_size(0), m_capacity(0)
  74. {
  75. m_data = NULL;
  76. assign(other.begin(), other.end());
  77. }
  78. dynamic_array &operator=(const dynamic_array &other)
  79. {
  80. // should not allocate memory unless we have to
  81. if (&other != this)
  82. assign(other.begin(), other.end());
  83. return *this;
  84. }
  85. void clear()
  86. {
  87. if (owns_data())
  88. m_data = deallocate(m_data);
  89. m_size = 0;
  90. m_capacity = 0;
  91. }
  92. void assign(const_iterator begin, const_iterator end)
  93. {
  94. Assert(begin <= end);
  95. resize_uninitialized(end - begin);
  96. memcpy(m_data, begin, m_size * sizeof(T));
  97. }
  98. iterator erase(iterator input_begin, iterator input_end)
  99. {
  100. Assert(input_begin <= input_end);
  101. Assert(input_begin >= begin());
  102. Assert(input_end <= end());
  103. size_t leftOverSize = end() - input_end;
  104. memmove(input_begin, input_end, leftOverSize * sizeof(T));
  105. m_size -= input_end - input_begin;
  106. return input_begin;
  107. }
  108. iterator erase(iterator it)
  109. {
  110. return erase(it, it + 1);
  111. }
  112. iterator erase_swap_back(iterator it)
  113. {
  114. m_size--;
  115. memcpy(it, end(), sizeof(T));
  116. return it;
  117. }
  118. iterator insert(iterator insert_before, const_iterator input_begin, const_iterator input_end)
  119. {
  120. Assert(input_begin <= input_end);
  121. Assert(insert_before >= begin());
  122. Assert(insert_before <= end());
  123. // resize (make sure that insertBefore does not get invalid in the meantime because of a reallocation)
  124. size_t insert_before_index = insert_before - begin();
  125. size_t elements_to_be_moved = size() - insert_before_index;
  126. resize_uninitialized((input_end - input_begin) + size(), true);
  127. insert_before = begin() + insert_before_index;
  128. size_t insertsize = input_end - input_begin;
  129. // move to the end of where the inserted data will be
  130. memmove(insert_before + insertsize, insert_before, elements_to_be_moved * sizeof(T));
  131. // inject input data in the hole we just created
  132. memcpy(insert_before, input_begin, insertsize * sizeof(T));
  133. return insert_before;
  134. }
  135. iterator insert(iterator insertBefore, const T &t) { return insert(insertBefore, &t, &t + 1); }
  136. void swap(dynamic_array &other) throw ()
  137. {
  138. std::swap(m_data, other.m_data);
  139. std::swap(m_size, other.m_size);
  140. std::swap(m_capacity, other.m_capacity);
  141. }
  142. // Returns the memory to the object.
  143. // This does not call the constructor for the newly added element.
  144. // You are expected to initialize all member variables of the returned data.
  145. T &push_back()
  146. {
  147. if (++m_size > capacity())
  148. reserve(std::max<size_t>(capacity() * 2, 1));
  149. return back();
  150. }
  151. // push_back but it also calls the constructor for the newly added element.
  152. T &push_back_construct()
  153. {
  154. if (++m_size > capacity())
  155. reserve(std::max<size_t>(capacity() * 2, 1));
  156. // construct
  157. T *ptr = &back();
  158. new(ptr)T;
  159. return *ptr;
  160. }
  161. // push_back but assigns /t/ to the newly added element.
  162. void push_back(const T &t)
  163. {
  164. push_back() = t;
  165. }
  166. void pop_back()
  167. {
  168. Assert(m_size >= 1);
  169. m_size--;
  170. }
  171. void resize_uninitialized(size_t size, bool double_on_resize = false)
  172. {
  173. m_size = size;
  174. if (m_size <= capacity())
  175. return;
  176. if (double_on_resize && size < capacity() * 2)
  177. size = capacity() * 2;
  178. reserve(size);
  179. }
  180. void resize_initialized(size_t size, const T &t = T(), bool double_on_resize = false)
  181. {
  182. if (size > capacity())
  183. {
  184. size_t requested_size = size;
  185. if (double_on_resize && size < capacity() * 2)
  186. requested_size = capacity() * 2;
  187. reserve(requested_size);
  188. }
  189. if (size > m_size)
  190. std::uninitialized_fill(m_data + m_size, m_data + size, t);
  191. m_size = size;
  192. }
  193. void reserve(size_t inCapacity)
  194. {
  195. if (capacity() >= inCapacity)
  196. return;
  197. if (owns_data())
  198. {
  199. Assert((inCapacity & k_reference_bit) == 0 && "Dynamic array capacity overflow");
  200. m_capacity = inCapacity;
  201. m_data = reallocate(m_data, inCapacity);
  202. }
  203. else
  204. {
  205. T *newData = allocate(inCapacity);
  206. memcpy(newData, m_data, m_size * sizeof(T));
  207. // Invalidate old non-owned data, since using the data from two places is most likely a really really bad idea.
  208. #if IL2CPP_DEBUG
  209. memset(m_data, 0xCD, capacity() * sizeof(T));
  210. #endif
  211. m_capacity = inCapacity; // and clear reference bit
  212. m_data = newData;
  213. }
  214. }
  215. void assign_external(T *begin, T *end)
  216. {
  217. if (owns_data())
  218. m_data = deallocate(m_data);
  219. m_size = m_capacity = reinterpret_cast<value_type *>(end) - reinterpret_cast<value_type *>(begin);
  220. Assert(m_size < k_reference_bit);
  221. m_capacity |= k_reference_bit;
  222. m_data = begin;
  223. }
  224. void set_owns_data(bool ownsData)
  225. {
  226. if (ownsData)
  227. m_capacity &= ~k_reference_bit;
  228. else
  229. m_capacity |= k_reference_bit;
  230. }
  231. void shrink_to_fit()
  232. {
  233. if (owns_data())
  234. {
  235. m_capacity = m_size;
  236. m_data = reallocate(m_data, m_size);
  237. }
  238. }
  239. const T &back() const
  240. {
  241. Assert(m_size != 0);
  242. return m_data[m_size - 1];
  243. }
  244. const T &front() const
  245. {
  246. Assert(m_size != 0);
  247. return m_data[0];
  248. }
  249. T &back()
  250. {
  251. Assert(m_size != 0);
  252. return m_data[m_size - 1];
  253. }
  254. T &front()
  255. {
  256. Assert(m_size != 0);
  257. return m_data[0];
  258. }
  259. T *data() { return m_data; }
  260. T const *data() const { return m_data; }
  261. bool empty() const { return m_size == 0; }
  262. size_t size() const { return m_size; }
  263. size_t capacity() const { return m_capacity & ~k_reference_bit; }
  264. T const &operator[](size_t index) const
  265. {
  266. Assert(index < m_size);
  267. return m_data[index];
  268. }
  269. T &operator[](size_t index)
  270. {
  271. Assert(index < m_size);
  272. return m_data[index];
  273. }
  274. T const *begin() const { return m_data; }
  275. T *begin() { return m_data; }
  276. T const *end() const { return m_data + m_size; }
  277. T *end() { return m_data + m_size; }
  278. bool owns_data() { return (m_capacity & k_reference_bit) == 0; }
  279. bool equals(const dynamic_array &other) const
  280. {
  281. if (m_size != other.m_size)
  282. return false;
  283. for (int i = 0; i < m_size; i++)
  284. {
  285. if (!(m_data[i] == other.m_data[i]))
  286. return false;
  287. }
  288. return true;
  289. }
  290. private:
  291. static const size_t k_reference_bit = (size_t)1 << (sizeof(size_t) * 8 - 1);
  292. T *allocate(size_t size)
  293. {
  294. return static_cast<T *>(IL2CPP_MALLOC_ALIGNED(size * sizeof(T), align));
  295. }
  296. T *deallocate(T *data)
  297. {
  298. Assert(owns_data());
  299. IL2CPP_FREE_ALIGNED(data);
  300. return NULL;
  301. }
  302. T *reallocate(T *data, size_t size)
  303. {
  304. Assert(owns_data());
  305. return static_cast<T *>(IL2CPP_REALLOC_ALIGNED(data, size * sizeof(T), align));
  306. }
  307. T *m_data;
  308. size_t m_size;
  309. size_t m_capacity;
  310. };
  311. } //namespace il2cpp
  312. } //namespace utils