flat_handle_map.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. /*
  2. * flat_handle_map
  3. *
  4. * A container which provides stable handles to inserted elements. Elements are accessed and erased
  5. * in constant time from handles. Insert and emplace occur in amortized constant time.
  6. *
  7. * Handle map operates similar to std::map<handle, T>, however, elements are stored in a vector-like
  8. * underlying container. Handles and iterators remain valid even when other elements are added or
  9. * erased, but valid elements are not necessarily stored contiguously. References to elements are
  10. * invalidated when inserting into the container, but not when erasing.
  11. *
  12. * Internally, a list of occupied elements are kept. When an element is erased, it is destroyed and
  13. * marked as available. During insertion, the first available element is re-used. If they are all
  14. * taken, the underlying container size is increased.
  15. *
  16. * MIT License
  17. *
  18. * Copyright (c) 2022 Michael R. P. Ragazzon
  19. *
  20. * Permission is hereby granted, free of charge, to any person obtaining a copy
  21. * of this software and associated documentation files (the "Software"), to deal
  22. * in the Software without restriction, including without limitation the rights
  23. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  24. * copies of the Software, and to permit persons to whom the Software is
  25. * furnished to do so, subject to the following conditions:
  26. *
  27. * The above copyright notice and this permission notice shall be included in
  28. * all copies or substantial portions of the Software.
  29. *
  30. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  31. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  32. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  33. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  34. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  35. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  36. * THE SOFTWARE.
  37. *
  38. */
  39. #pragma once
  40. #include <cassert>
  41. #include <cstddef>
  42. #include <memory>
  43. #include <type_traits>
  44. #include <vector>
  45. template <typename T, class Alloc = std::allocator<T>>
  46. struct flat_handle_map {
  47. public:
  48. enum class handle : size_t {};
  49. class const_iterator {
  50. private:
  51. const flat_handle_map& container;
  52. size_t i;
  53. public:
  54. using iterator_category = std::forward_iterator_tag;
  55. using value_type = T;
  56. using difference_type = std::ptrdiff_t;
  57. using pointer = const T*;
  58. using reference = const T&;
  59. explicit const_iterator(const flat_handle_map& container, size_t i) : container(container), i(i) {}
  60. reference operator*() const { return *(container.data() + i); }
  61. bool operator==(const const_iterator& other) const { return i == other.i; }
  62. bool operator!=(const const_iterator& other) const { return !(*this == other); }
  63. const_iterator operator++(int)
  64. {
  65. const_iterator retval = *this;
  66. ++(*this);
  67. return retval;
  68. }
  69. const_iterator& operator++()
  70. {
  71. ++i;
  72. i = container.get_next_occupied(i);
  73. return *this;
  74. }
  75. handle get_handle() const { return static_cast<handle>(i); }
  76. };
  77. class iterator {
  78. private:
  79. flat_handle_map& container;
  80. size_t i;
  81. public:
  82. using iterator_category = std::forward_iterator_tag;
  83. using value_type = T;
  84. using difference_type = std::ptrdiff_t;
  85. using pointer = T*;
  86. using reference = T&;
  87. explicit iterator(flat_handle_map& container, size_t i) : container(container), i(i) {}
  88. reference operator*() const { return *(container.data() + i); }
  89. bool operator==(const iterator& other) const { return i == other.i; }
  90. bool operator!=(const iterator& other) const { return !(*this == other); }
  91. iterator operator++(int)
  92. {
  93. iterator retval = *this;
  94. ++(*this);
  95. return retval;
  96. }
  97. iterator& operator++()
  98. {
  99. ++i;
  100. i = container.get_next_occupied(i);
  101. return *this;
  102. }
  103. operator const_iterator() const { return const_iterator{container, i}; }
  104. handle get_handle() const { return static_cast<handle>(i); }
  105. };
  106. using AllocatorTraits = std::allocator_traits<Alloc>;
  107. using allocator_type = Alloc;
  108. using value_type = typename AllocatorTraits::value_type;
  109. using size_type = typename AllocatorTraits::size_type;
  110. using difference_type = typename AllocatorTraits::difference_type;
  111. using reference = typename value_type&;
  112. using const_reference = typename const value_type&;
  113. using pointer = typename AllocatorTraits::pointer;
  114. using const_pointer = typename AllocatorTraits::const_pointer;
  115. flat_handle_map() : flat_handle_map(Alloc()) {}
  116. flat_handle_map(const Alloc& alloc) : m_capacity(0), m_alloc(alloc) { m_begin = m_end = nullptr; }
  117. explicit flat_handle_map(size_t count, const T& value, const Alloc& alloc = Alloc()) : flat_handle_map(alloc) { assign_impl(count, value); }
  118. template <class InputIterator, typename = decltype(*std::declval<InputIterator>())>
  119. flat_handle_map(InputIterator first, InputIterator last, const Alloc& alloc = Alloc()) : flat_handle_map(alloc)
  120. {
  121. assign_impl(first, last);
  122. }
  123. flat_handle_map(std::initializer_list<T> l, const Alloc& alloc = Alloc()) : flat_handle_map(alloc) { assign_impl(l); }
  124. flat_handle_map(const flat_handle_map& v) :
  125. flat_handle_map(v, std::allocator_traits<Alloc>::select_on_container_copy_construction(v.get_allocator()))
  126. {}
  127. flat_handle_map(const flat_handle_map& v, const Alloc& alloc) : flat_handle_map(alloc) { assign_impl(v); }
  128. flat_handle_map(flat_handle_map&& v) :
  129. m_capacity(v.m_capacity), m_alloc(std::move(v.m_alloc)), m_begin(v.m_begin), m_end(v.m_end), m_occupied(std::move(v.m_occupied)),
  130. m_first_available(v.m_first_available)
  131. {
  132. v.m_begin = v.m_end = nullptr;
  133. v.m_capacity = 0;
  134. v.m_occupied.clear();
  135. v.m_first_available = invalid_index;
  136. }
  137. ~flat_handle_map()
  138. {
  139. clear();
  140. if (m_begin)
  141. {
  142. AllocatorTraits::deallocate(m_alloc, m_begin, m_capacity);
  143. }
  144. }
  145. flat_handle_map& operator=(const flat_handle_map& v)
  146. {
  147. if (this == &v)
  148. {
  149. // prevent self usurp
  150. return *this;
  151. }
  152. clear();
  153. assign_impl(v);
  154. return *this;
  155. }
  156. flat_handle_map& operator=(flat_handle_map&& v)
  157. {
  158. clear();
  159. m_occupied = std::move(v.m_occupied);
  160. m_first_available = v.m_first_available;
  161. m_alloc = std::move(v.m_alloc);
  162. m_capacity = v.m_capacity;
  163. m_begin = v.m_begin;
  164. m_end = v.m_end;
  165. v.m_begin = v.m_end = nullptr;
  166. v.m_capacity = 0;
  167. v.m_occupied.clear();
  168. v.m_first_available = invalid_index;
  169. return *this;
  170. }
  171. allocator_type get_allocator() const noexcept { return m_alloc; }
  172. const_reference at(handle h) const noexcept { return *(m_begin + static_cast<size_t>(h)); }
  173. reference at(handle h) noexcept { return *(m_begin + static_cast<size_t>(h)); }
  174. const_reference operator[](handle h) const noexcept { return at(h); }
  175. reference operator[](handle h) noexcept { return at(h); }
  176. const_reference front() const noexcept { return at(handle{get_next_occupied(0)}); }
  177. reference front() noexcept { return at(handle{get_next_occupied(0)}); }
  178. const_reference back() const noexcept { return at(handle{get_previous_occupied(m_occupied.size() - 1)}); }
  179. reference back() noexcept { return at(handle{get_previous_occupied(m_occupied.size() - 1)}); }
  180. const_pointer data() const noexcept { return m_begin; }
  181. pointer data() noexcept { return m_begin; }
  182. // iterators
  183. iterator begin() noexcept { return iterator(*this, get_next_occupied(0)); }
  184. const_iterator begin() const noexcept { return const_iterator(*this, get_next_occupied(0)); }
  185. const_iterator cbegin() const noexcept { return const_iterator(*this, get_next_occupied(0)); }
  186. iterator end() noexcept { return iterator(*this, invalid_index); }
  187. const_iterator end() const noexcept { return const_iterator(*this, invalid_index); }
  188. const_iterator cend() const noexcept { return const_iterator(*this, invalid_index); }
  189. iterator iterate(handle h) noexcept { return iterator(*this, static_cast<size_t>(h)); }
  190. const_iterator iterate(handle h) const noexcept { return const_iterator(*this, static_cast<size_t>(h)); }
  191. // capacity
  192. bool empty() const noexcept { return m_begin == m_end || size() == 0; }
  193. size_t size() const noexcept { return std::count(m_occupied.begin(), m_occupied.end(), true); }
  194. size_t container_size() const noexcept { return m_end - m_begin; }
  195. void reserve(size_t desired_capacity)
  196. {
  197. if (desired_capacity <= m_capacity)
  198. {
  199. return;
  200. }
  201. m_occupied.reserve(desired_capacity);
  202. size_type new_cap = find_capacity(desired_capacity);
  203. auto new_buf = AllocatorTraits::allocate(m_alloc, new_cap);
  204. const auto s = container_size();
  205. // now we need to transfer the existing elements into the new buffer
  206. for (size_type i = 0; i < s; ++i)
  207. {
  208. if (m_occupied[i])
  209. AllocatorTraits::construct(m_alloc, new_buf + i, std::move(*(m_begin + i)));
  210. }
  211. if (m_begin)
  212. {
  213. // free old elements
  214. for (size_type i = 0; i < s; ++i)
  215. {
  216. if (m_occupied[i])
  217. AllocatorTraits::destroy(m_alloc, m_begin + i);
  218. }
  219. AllocatorTraits::deallocate(m_alloc, m_begin, m_capacity);
  220. }
  221. m_begin = new_buf;
  222. m_end = new_buf + s;
  223. m_capacity = new_cap;
  224. }
  225. size_t capacity() const noexcept { return m_capacity; }
  226. // modifiers
  227. void clear() noexcept
  228. {
  229. for (size_t i = 0; i < m_occupied.size(); i++)
  230. {
  231. if (m_occupied[i])
  232. AllocatorTraits::destroy(m_alloc, m_begin + i);
  233. }
  234. m_occupied.clear();
  235. m_first_available = invalid_index;
  236. m_end = m_begin;
  237. }
  238. handle insert(const value_type& val)
  239. {
  240. handle result = static_cast<handle>(invalid_index);
  241. if (m_first_available >= container_size())
  242. {
  243. result = static_cast<handle>(container_size());
  244. auto pos = grow_end(1);
  245. AllocatorTraits::construct(m_alloc, pos, val);
  246. m_occupied.push_back(true);
  247. m_first_available = invalid_index;
  248. }
  249. else
  250. {
  251. result = static_cast<handle>(m_first_available);
  252. AllocatorTraits::construct(m_alloc, m_begin + m_first_available, val);
  253. m_occupied[m_first_available] = true;
  254. m_first_available = get_next_available(m_first_available + 1);
  255. }
  256. return result;
  257. }
  258. handle insert(value_type&& val)
  259. {
  260. handle result{invalid_index};
  261. if (m_first_available >= container_size())
  262. {
  263. result = static_cast<handle>(container_size());
  264. auto pos = grow_end(1);
  265. AllocatorTraits::construct(m_alloc, pos, std::move(val));
  266. m_occupied.push_back(true);
  267. m_first_available = invalid_index;
  268. }
  269. else
  270. {
  271. result = static_cast<handle>(m_first_available);
  272. AllocatorTraits::construct(m_alloc, m_begin + m_first_available, std::move(val));
  273. m_occupied[m_first_available] = true;
  274. m_first_available = get_next_available(m_first_available + 1);
  275. }
  276. return result;
  277. }
  278. void insert(size_type count, const value_type& val)
  279. {
  280. reserve(size() + count);
  281. for (size_type i = 0; i < count; ++i)
  282. insert(val);
  283. }
  284. template <typename InputIterator, typename = decltype(*std::declval<InputIterator>())>
  285. void insert(InputIterator first, InputIterator last)
  286. {
  287. reserve(size() + last - first);
  288. for (auto p = first; p != last; ++p)
  289. insert(*p);
  290. }
  291. void insert(std::initializer_list<T> ilist)
  292. {
  293. reserve(size() + ilist.size());
  294. for (auto& elem : ilist)
  295. insert(elem);
  296. }
  297. template <typename... Args>
  298. handle emplace(Args&&... args)
  299. {
  300. handle result{invalid_index};
  301. if (m_first_available >= container_size())
  302. {
  303. result = static_cast<handle>(container_size());
  304. auto pos = grow_end(1);
  305. AllocatorTraits::construct(m_alloc, pos, std::forward<Args>(args)...);
  306. m_occupied.push_back(true);
  307. m_first_available = invalid_index;
  308. }
  309. else
  310. {
  311. result = static_cast<handle>(m_first_available);
  312. AllocatorTraits::construct(m_alloc, m_begin + m_first_available, std::forward<Args>(args)...);
  313. m_occupied[m_first_available] = true;
  314. m_first_available = get_next_available(m_first_available + 1);
  315. }
  316. return result;
  317. }
  318. iterator erase(handle h)
  319. {
  320. auto i = static_cast<size_t>(h);
  321. assert(i < m_occupied.size() && m_occupied[i]);
  322. AllocatorTraits::destroy(m_alloc, m_begin + i);
  323. m_occupied[i] = false;
  324. if (i < m_first_available)
  325. m_first_available = i;
  326. return iterator(*this, get_next_occupied(i + 1));
  327. }
  328. iterator erase(const_iterator it) { return erase(it.get_handle()); }
  329. private:
  330. size_t get_next_available(size_t i) const noexcept
  331. {
  332. for (size_t j = i; j < m_occupied.size(); j++)
  333. if (!m_occupied[j])
  334. return j;
  335. return invalid_index;
  336. }
  337. size_t get_next_occupied(size_t i) const noexcept
  338. {
  339. for (size_t j = i; j < m_occupied.size(); j++)
  340. if (m_occupied[j])
  341. return j;
  342. return invalid_index;
  343. }
  344. size_t get_previous_occupied(size_t i) const noexcept
  345. {
  346. static_assert(size_t(0) < size_t(0) - 1, "size_t must underflow when decrementing from zero");
  347. for (size_t j = i; j < m_occupied.size(); j--)
  348. if (m_occupied[j])
  349. return j;
  350. return invalid_index;
  351. }
  352. size_type find_capacity(size_type desired_capacity) const
  353. {
  354. if (m_capacity == 0)
  355. {
  356. return desired_capacity;
  357. }
  358. else
  359. {
  360. auto new_cap = m_capacity;
  361. while (new_cap < desired_capacity)
  362. {
  363. // grow by roughly 1.5
  364. new_cap *= 3;
  365. ++new_cap;
  366. new_cap /= 2;
  367. }
  368. return new_cap;
  369. }
  370. }
  371. T* grow_end(size_t num)
  372. {
  373. const auto s = container_size();
  374. reserve(s + num);
  375. m_end = m_begin + s + num;
  376. return m_begin + s;
  377. }
  378. // grows buffer only on empty
  379. void grow_empty(size_t capacity)
  380. {
  381. assert(m_begin == m_end);
  382. if (capacity <= m_capacity)
  383. return;
  384. if (m_begin)
  385. {
  386. AllocatorTraits::deallocate(m_alloc, m_begin, m_capacity);
  387. }
  388. m_capacity = find_capacity(capacity);
  389. m_begin = m_end = AllocatorTraits::allocate(m_alloc, m_capacity);
  390. }
  391. void assign_impl(size_type count, const T& value)
  392. {
  393. grow_empty(count);
  394. for (size_type i = 0; i < count; ++i)
  395. {
  396. insert(value);
  397. }
  398. }
  399. template <class InputIterator>
  400. void assign_impl(InputIterator first, InputIterator last)
  401. {
  402. auto isize = last - first;
  403. grow_empty(isize);
  404. for (auto p = first; p != last; ++p)
  405. {
  406. insert(*p);
  407. }
  408. }
  409. void assign_impl(std::initializer_list<T> ilist)
  410. {
  411. grow_empty(ilist.size());
  412. for (auto& elem : ilist)
  413. {
  414. insert(elem);
  415. }
  416. }
  417. void assign_impl(const flat_handle_map& v)
  418. {
  419. grow_empty(v.container_size());
  420. m_occupied = v.m_occupied;
  421. m_first_available = v.m_first_available;
  422. for (size_t i = 0; i < v.container_size(); i++)
  423. if (m_occupied[i])
  424. AllocatorTraits::construct(m_alloc, m_begin + i, *(v.m_begin + i));
  425. m_end = m_begin + v.container_size();
  426. }
  427. template <typename T, class Alloc>
  428. friend bool operator==(const flat_handle_map<T, Alloc>& a, const flat_handle_map<T, Alloc>& b);
  429. pointer m_begin;
  430. pointer m_end;
  431. size_t m_capacity;
  432. Alloc m_alloc;
  433. std::vector<bool> m_occupied;
  434. size_t m_first_available = invalid_index;
  435. static constexpr size_t invalid_index = size_t(-1);
  436. };
  437. template <typename T, class Alloc>
  438. bool operator==(const flat_handle_map<T, Alloc>& a, const flat_handle_map<T, Alloc>& b)
  439. {
  440. using handle = typename flat_handle_map<T, Alloc>::handle;
  441. if (a.size() != b.size())
  442. {
  443. return false;
  444. }
  445. for (size_t i = 0; i < a.m_occupied.size(); ++i)
  446. {
  447. auto h = static_cast<handle>(i);
  448. if (a.m_occupied[i] != b.m_occupied[i])
  449. return false;
  450. if (a.m_occupied[i] && (a[h] != b[h]))
  451. return false;
  452. }
  453. return true;
  454. }
  455. template <typename T, class Alloc>
  456. bool operator!=(const flat_handle_map<T, Alloc>& a, const flat_handle_map<T, Alloc>& b)
  457. {
  458. return !(a == b);
  459. }
  460. #ifdef HANDLEMAP_TEST
  461. void test_stable_vector()
  462. {
  463. flat_handle_map<string> m;
  464. flat_handle_map<string> m_copy1 = m;
  465. assert(m == m_copy1);
  466. auto h_hello = m.insert("hello");
  467. auto h_carl = m.insert("carl");
  468. auto h_wood = m.insert("wood");
  469. auto h_space = m.insert(" ");
  470. m.insert({"red", "green", "blue", "alpha", "none"});
  471. auto h_long = m.insert(
  472. "A REAAAALLY long string.........................................................:::::::::::::::::::::::...................................."s);
  473. auto h_world = m.insert("world");
  474. m.erase(h_long);
  475. m.insert("short");
  476. m.insert({"s", "p", "a", "m"});
  477. string s = "s";
  478. m.insert(s);
  479. m.emplace("p");
  480. m.insert("a");
  481. m.emplace("m");
  482. for (auto it = m.iterate(h_carl); it != m.end(); ++it)
  483. {
  484. if (*it == "blue")
  485. m.erase(it);
  486. }
  487. m.erase(h_carl);
  488. auto h_jane = m.insert("jane");
  489. auto h_black = m.insert("black");
  490. m.erase(h_wood);
  491. auto ss = MakeString();
  492. for (auto it = m.begin(); it != m.end(); ++it)
  493. {
  494. ss << *it << ", ";
  495. }
  496. string str = (string)ss;
  497. auto str2 = m[h_hello] + m[h_space] + m[h_world];
  498. assert(str == "hello, jane, , red, green, black, alpha, none, short, world, s, p, a, m, s, p, a, m, ");
  499. assert(str2 == "hello world");
  500. flat_handle_map<string> m_copy2{m};
  501. assert(m == m_copy2);
  502. assert(m_copy1 != m_copy2);
  503. }
  504. #endif