PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/util/compact-vector.h

http://github.com/facebook/hiphop-php
C Header | 477 lines | 384 code | 59 blank | 34 comment | 61 complexity | b1f46f67a6b57b7d3d1cf8de170dd6bf MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT, LGPL-2.0, Apache-2.0
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #ifndef incl_HPHP_COMPACT_VECTOR_H_
  17. #define incl_HPHP_COMPACT_VECTOR_H_
  18. #include <stdlib.h>
  19. #include <cstring>
  20. #include <type_traits>
  21. #include <initializer_list>
  22. #include "hphp/util/assertions.h"
  23. #include "hphp/util/safe-cast.h"
  24. namespace HPHP {
  25. ///////////////////////////////////////////////////////////////////////////////
  26. /**
  27. * During its lifetime, an instance of CompactVector can transition
  28. * between 2 states:
  29. * State 0: m_val == 0
  30. * This is the initial state for a newly constructed CompactVector.
  31. * There are no elements and no allocated block of memory.
  32. * State 1: m_val != 0
  33. * In this state, m_data points to a malloced block of memory. The
  34. * number of elements, the capacity of the block, and the values of
  35. * all the elements reside in the allocated block of memory.
  36. */
  37. template <typename T, typename Alloc = std::allocator<char>>
  38. struct CompactVector : private Alloc::template rebind<char>::other {
  39. using size_type = std::size_t;
  40. using value_type = T;
  41. using iterator = T*;
  42. using const_iterator = const T*;
  43. using Allocator = typename Alloc::template rebind<char>::other;
  44. friend iterator begin(CompactVector& v) { return v.begin(); }
  45. friend iterator end(CompactVector& v) { return v.end(); }
  46. friend const_iterator begin(const CompactVector& v) { return v.begin(); }
  47. friend const_iterator end(const CompactVector& v) { return v.end(); }
  48. CompactVector();
  49. explicit CompactVector(size_type n);
  50. CompactVector(size_type n, const T&);
  51. CompactVector(CompactVector&& other) noexcept;
  52. CompactVector(const CompactVector& other);
  53. CompactVector(std::initializer_list<T> init);
  54. CompactVector& operator=(CompactVector&&);
  55. CompactVector& operator=(const CompactVector&);
  56. ~CompactVector();
  57. void swap(CompactVector& other) noexcept;
  58. bool operator==(const CompactVector& other) const;
  59. bool operator!=(const CompactVector& other) const {
  60. return !(*this == other);
  61. }
  62. iterator begin() { return m_data ? elems() : nullptr; }
  63. iterator end() { return m_data ? elems() + size() : nullptr; }
  64. const_iterator begin() const { return m_data ? elems() : nullptr; }
  65. const_iterator end() const { return m_data ? elems() + size() : nullptr; }
  66. T* data() { return m_data ? elems() : nullptr; }
  67. const T* data() const { return m_data ? elems() : nullptr; }
  68. bool empty() const;
  69. size_type size() const;
  70. size_type capacity();
  71. void clear();
  72. void push_back(const T& val);
  73. void push_back(T&& val);
  74. template <class... Args>
  75. void emplace_back(Args&&... args);
  76. void pop_back();
  77. void erase(iterator);
  78. void erase(iterator, iterator);
  79. iterator insert(iterator p, const T& v) { return insert_impl(p, 1, v); }
  80. iterator insert(iterator p, T&& v) { return insert_impl(p, 1, std::move(v)); };
  81. iterator insert(iterator p, size_t num, const T& v) {
  82. return insert_impl(p, num, v);
  83. };
  84. template<typename U>
  85. iterator insert(iterator p, U i1, U i2);
  86. void resize(size_type sz);
  87. void resize(size_type sz, const value_type& value);
  88. void shrink_to_fit();
  89. T& operator[](size_type index) { return *get(index); }
  90. const T& operator[](size_type index) const { return *get(index); }
  91. T& front() { return *get(0); }
  92. const T& front() const { return *get(0); }
  93. T& back() { return *get(m_data->m_len - 1); }
  94. const T& back() const { return *get(m_data->m_len - 1); }
  95. void reserve(size_type sz);
  96. private:
  97. using Allocator::allocate;
  98. using Allocator::deallocate;
  99. struct CompactVectorData {
  100. uint32_t m_len;
  101. uint32_t m_capacity;
  102. };
  103. iterator insert_elems(iterator, size_t num);
  104. template <class U>
  105. iterator insert_impl(iterator, size_t num, U&&);
  106. void assign(const CompactVector& other);
  107. void grow();
  108. T* get(size_type index) const;
  109. T* elems() const;
  110. static size_t required_mem(size_type n);
  111. void reserve_impl(size_type sz);
  112. bool resize_helper(size_type sz);
  113. CompactVectorData* m_data;
  114. static constexpr size_type initial_capacity = 4;
  115. /* We mainly want this so pretty.py can figure out the alignment */
  116. static constexpr size_type elems_offset =
  117. alignof(T) >= sizeof(CompactVectorData) ?
  118. alignof(T) : sizeof(CompactVectorData);
  119. /* And we need this to prevent gcc from throwing away elems_offset */
  120. using elems_offset_type = char[elems_offset];
  121. };
  122. template <typename T, typename A>
  123. CompactVector<T, A>::CompactVector() {
  124. m_data = nullptr;
  125. }
  126. template <typename T, typename A>
  127. CompactVector<T, A>::CompactVector(size_type n) {
  128. m_data = nullptr;
  129. resize(n);
  130. }
  131. template <typename T, typename A>
  132. CompactVector<T, A>::CompactVector(size_type n, const T& val) {
  133. m_data = nullptr;
  134. resize(n, val);
  135. }
  136. template <typename T, typename A>
  137. CompactVector<T, A>::CompactVector(CompactVector&& other) noexcept
  138. : m_data(other.m_data) {
  139. other.m_data = nullptr;
  140. }
  141. template <typename T, typename A>
  142. CompactVector<T, A>::CompactVector(const CompactVector& other)
  143. : m_data(nullptr) {
  144. assign(other);
  145. }
  146. template <typename T, typename A>
  147. CompactVector<T, A>&
  148. CompactVector<T, A>::operator=(const CompactVector& other) {
  149. if (this == &other) return *this;
  150. clear();
  151. assign(other);
  152. return *this;
  153. }
  154. template <typename T, typename A>
  155. CompactVector<T, A>::CompactVector(std::initializer_list<T> init) :
  156. m_data(nullptr) {
  157. reserve_impl(init.size());
  158. for (auto const& e : init) {
  159. push_back(e);
  160. }
  161. }
  162. template <typename T, typename A>
  163. void CompactVector<T, A>::assign(const CompactVector& other) {
  164. assert(!m_data);
  165. if (!other.size()) return;
  166. reserve_impl(other.m_data->m_len);
  167. auto const sz = other.m_data->m_len;
  168. for (size_type i = 0; i < sz; ++i) {
  169. push_back(other[i]);
  170. }
  171. }
  172. template <typename T, typename A>
  173. CompactVector<T, A>& CompactVector<T, A>::operator=(CompactVector&& other) {
  174. std::swap(m_data, other.m_data);
  175. return *this;
  176. }
  177. template <typename T, typename A>
  178. void CompactVector<T, A>::swap(CompactVector& other) noexcept {
  179. std::swap(m_data, other.m_data);
  180. }
  181. template <typename T, typename A>
  182. bool CompactVector<T, A>::operator==(const CompactVector& other) const {
  183. auto const sz = size();
  184. if (sz != other.size()) return false;
  185. for (size_type i = 0; i < sz; ++i) {
  186. if (!(*get(i) == other[i])) return false;
  187. }
  188. return true;
  189. }
  190. template <typename T, typename A>
  191. CompactVector<T, A>::~CompactVector() {
  192. clear();
  193. }
  194. template <typename T, typename A>
  195. T* CompactVector<T, A>::elems() const {
  196. assert(m_data);
  197. return (T*)((char*)m_data + elems_offset);
  198. }
  199. template <typename T, typename A>
  200. size_t CompactVector<T, A>::required_mem(size_type n) {
  201. return elems_offset + sizeof(T) * n;
  202. }
  203. template <typename T, typename A>
  204. T* CompactVector<T, A>::get(size_type index) const {
  205. // Index into the allocated block of memory
  206. auto e = elems();
  207. assert(index < m_data->m_len);
  208. return e + index;
  209. }
  210. template <typename T, typename A>
  211. bool CompactVector<T, A>::empty() const {
  212. return size() == 0;
  213. }
  214. template <typename T, typename A>
  215. typename CompactVector<T, A>::size_type CompactVector<T, A>::size() const {
  216. return m_data ? m_data->m_len : 0;
  217. }
  218. template <typename T, typename A>
  219. typename CompactVector<T, A>::size_type CompactVector<T, A>::capacity() {
  220. return m_data ? m_data->m_capacity : 0;
  221. }
  222. template <typename T, typename A>
  223. void CompactVector<T, A>::erase(iterator elm) {
  224. assert(elm - elems() < size());
  225. auto const e = end();
  226. while (++elm != e) {
  227. elm[-1] = std::move(*elm);
  228. }
  229. elm[-1].~T();
  230. m_data->m_len--;
  231. }
  232. template <typename T, typename A>
  233. void CompactVector<T, A>::erase(iterator elm1, iterator elm2) {
  234. if (elm1 == elm2) return;
  235. assert(elems() <= elm1 && elm1 <= elm2 && elm2 <= end());
  236. auto const e = end();
  237. while (elm2 != e) {
  238. *elm1++ = std::move(*elm2++);
  239. }
  240. m_data->m_len -= elm2 - elm1;
  241. while (elm1 != e) {
  242. elm1++->~T();
  243. }
  244. }
  245. template <typename T, typename A>
  246. bool CompactVector<T, A>::resize_helper(size_type sz) {
  247. auto const old_size = size();
  248. if (sz == old_size) return true;
  249. if (sz > old_size) {
  250. reserve_impl(sz);
  251. return false;
  252. }
  253. auto elm = get(sz);
  254. m_data->m_len = sz;
  255. do {
  256. elm++->~T();
  257. } while (++sz < old_size);
  258. return true;
  259. }
  260. template <typename T, typename A>
  261. void CompactVector<T, A>::resize(size_type sz, const value_type& v) {
  262. if (resize_helper(sz)) return;
  263. while (m_data->m_len < sz) {
  264. push_back(v);
  265. }
  266. }
  267. template <typename T, typename A>
  268. void CompactVector<T, A>::resize(size_type sz) {
  269. if (resize_helper(sz)) return;
  270. while (m_data->m_len < sz) {
  271. push_back(T{});
  272. }
  273. }
  274. template <typename T, typename A>
  275. void CompactVector<T, A>::shrink_to_fit() {
  276. if (!m_data || m_data->m_capacity == m_data->m_len) return;
  277. if (!m_data->m_len) {
  278. clear();
  279. return;
  280. }
  281. reserve_impl(m_data->m_len);
  282. }
  283. template <typename T, typename A>
  284. void copy(CompactVector<T, A>& dest, const std::vector<T>& src) {
  285. dest.clear();
  286. dest.reserve(src.size());
  287. for (auto const& v : src) dest.push_back(v);
  288. }
  289. template <typename T, typename A>
  290. void CompactVector<T, A>::clear() {
  291. if (!m_data) return;
  292. if (!std::is_trivially_destructible<T>::value) {
  293. if (auto sz = size()) {
  294. auto elm = elems();
  295. do { elm++->~T(); } while (--sz);
  296. }
  297. }
  298. deallocate(reinterpret_cast<char*>(m_data),
  299. required_mem(m_data->m_capacity));
  300. m_data = nullptr;
  301. }
  302. template <typename T, typename A>
  303. void CompactVector<T, A>::grow() {
  304. reserve_impl(m_data ? m_data->m_capacity * 2LL : initial_capacity);
  305. }
  306. template <typename T, typename A>
  307. void CompactVector<T, A>::reserve_impl(size_type new_capacity) {
  308. auto new_data = (CompactVectorData*)allocate(required_mem(new_capacity));
  309. if (!new_data) throw std::bad_alloc{};
  310. if (m_data) {
  311. auto len = m_data->m_len;
  312. auto old_data = m_data;
  313. auto const old_capacity = old_data->m_capacity;
  314. auto old_elems = elems();
  315. m_data = new_data;
  316. auto new_elems = elems();
  317. m_data->m_len = len;
  318. m_data->m_capacity = safe_cast<uint32_t>(new_capacity);
  319. while (len--) {
  320. new (new_elems++) T(std::move(*old_elems));
  321. old_elems++->~T();
  322. }
  323. deallocate(reinterpret_cast<char*>(old_data), required_mem(old_capacity));
  324. } else {
  325. // If there are currently no elements, all we have to do is allocate a
  326. // block of memory and initialize m_len and m_capacity.
  327. m_data = new_data;
  328. m_data->m_len = 0;
  329. m_data->m_capacity = new_capacity;
  330. }
  331. }
  332. template <typename T, typename A>
  333. void CompactVector<T, A>::reserve(size_type new_capacity) {
  334. if (new_capacity > capacity()) reserve_impl(new_capacity);
  335. }
  336. template <typename T, typename A>
  337. typename CompactVector<T, A>::iterator
  338. CompactVector<T, A>::insert_elems(iterator before, size_t num) {
  339. if (!num) return before;
  340. auto const sz = size();
  341. assert(sz <= capacity());
  342. auto cap = capacity();
  343. if (sz + num > cap) {
  344. auto const pos = sz ? before - elems() : 0;
  345. assert(pos <= sz);
  346. cap <<= 1;
  347. if (sz + num > cap) {
  348. cap = sz + num;
  349. if (cap < initial_capacity) cap = initial_capacity;
  350. }
  351. reserve(cap);
  352. before = elems() + pos;
  353. }
  354. auto e = end();
  355. m_data->m_len += num;
  356. while (e != before) {
  357. --e;
  358. new (e + num) T(std::move(*e));
  359. e->~T();
  360. }
  361. return e;
  362. }
  363. template <typename T, typename A>
  364. template <typename U>
  365. typename CompactVector<T, A>::iterator
  366. CompactVector<T, A>::insert_impl(iterator before, size_t num, U&& val) {
  367. auto e = insert_elems(before, num);
  368. while (num--) {
  369. new (e + num) T(std::forward<U>(val));
  370. }
  371. return e;
  372. }
  373. template <typename T, typename A>
  374. template <typename U>
  375. typename CompactVector<T, A>::iterator
  376. CompactVector<T, A>::insert(iterator before, U i1, U i2) {
  377. auto e = insert_elems(before, i2 - i1);
  378. auto i = e;
  379. while (i1 != i2) {
  380. new (i++) T(*i1++);
  381. }
  382. return e;
  383. }
  384. template <typename T, typename A>
  385. void CompactVector<T, A>::push_back(const T& val) {
  386. auto const sz = size();
  387. assert(sz <= capacity());
  388. if (sz == capacity()) grow();
  389. ++(m_data->m_len);
  390. new (get(sz)) T(val);
  391. }
  392. template <typename T, typename A>
  393. void CompactVector<T, A>::push_back(T&& val) {
  394. auto const sz = size();
  395. assert(sz <= capacity());
  396. if (sz == capacity()) grow();
  397. ++(m_data->m_len);
  398. new (get(sz)) T(std::move(val));
  399. }
  400. template <typename T, typename A>
  401. template <class... Args>
  402. void CompactVector<T, A>::emplace_back(Args&&... args) {
  403. auto const sz = size();
  404. assert(sz <= capacity());
  405. if (sz == capacity()) grow();
  406. ++(m_data->m_len);
  407. new (get(sz)) T(std::forward<Args>(args)...);
  408. }
  409. template <typename T, typename A>
  410. void CompactVector<T, A>::pop_back() {
  411. if (m_data && m_data->m_len > 0) {
  412. // Otherwise, we just decrement the length
  413. --(m_data->m_len);
  414. }
  415. }
  416. ///////////////////////////////////////////////////////////////////////////////
  417. }
  418. #endif