small_map.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef BASE_CONTAINERS_SMALL_MAP_H_
  5. #define BASE_CONTAINERS_SMALL_MAP_H_
  6. #include <stddef.h>
  7. #include <limits>
  8. #include <map>
  9. #include <new>
  10. #include <string>
  11. #include <unordered_map>
  12. #include <utility>
  13. #include "base/check_op.h"
  14. namespace {
  15. constexpr size_t kUsingFullMapSentinel = std::numeric_limits<size_t>::max();
  16. } // namespace
  17. namespace base {
  18. // small_map is a container with a std::map-like interface. It starts out backed
  19. // by an unsorted array but switches to some other container type if it grows
  20. // beyond this fixed size.
  21. //
  22. // Please see //base/containers/README.md for an overview of which container
  23. // to select.
  24. //
  25. // PROS
  26. //
  27. // - Good memory locality and low overhead for smaller maps.
  28. // - Handles large maps without the degenerate performance of flat_map.
  29. //
  30. // CONS
  31. //
  32. // - Larger code size than the alternatives.
  33. //
  34. // IMPORTANT NOTES
  35. //
  36. // - Iterators are invalidated across mutations.
  37. //
  38. // DETAILS
  39. //
  40. // base::small_map will pick up the comparator from the underlying map type. In
  41. // std::map only a "less" operator is defined, which requires us to do two
  42. // comparisons per element when doing the brute-force search in the simple
  43. // array. std::unordered_map has a key_equal function which will be used.
  44. //
  45. // We define default overrides for the common map types to avoid this
  46. // double-compare, but you should be aware of this if you use your own operator<
  47. // for your map and supply yor own version of == to the small_map. You can use
  48. // regular operator== by just doing:
  49. //
  50. // base::small_map<std::map<MyKey, MyValue>, 4, std::equal_to<KyKey>>
  51. //
  52. //
  53. // USAGE
  54. // -----
  55. //
  56. // NormalMap: The map type to fall back to. This also defines the key and value
  57. // types for the small_map.
  58. // kArraySize: The size of the initial array of results. This will be allocated
  59. // with the small_map object rather than separately on the heap.
  60. // Once the map grows beyond this size, the map type will be used
  61. // instead.
  62. // EqualKey: A functor which tests two keys for equality. If the wrapped map
  63. // type has a "key_equal" member (unordered_map does), then that will
  64. // be used by default. If the wrapped map type has a strict weak
  65. // ordering "key_compare" (std::map does), that will be used to
  66. // implement equality by default.
  67. // MapInit: A functor that takes a NormalMap* and uses it to initialize the map.
  68. // This functor will be called at most once per small_map, when the map
  69. // exceeds the threshold of kArraySize and we are about to copy values
  70. // from the array to the map. The functor *must* initialize the
  71. // NormalMap* argument with placement new, since after it runs we
  72. // assume that the NormalMap has been initialized.
  73. //
  74. // Example:
  75. // base::small_map<std::map<string, int>> days;
  76. // days["sunday" ] = 0;
  77. // days["monday" ] = 1;
  78. // days["tuesday" ] = 2;
  79. // days["wednesday"] = 3;
  80. // days["thursday" ] = 4;
  81. // days["friday" ] = 5;
  82. // days["saturday" ] = 6;
  83. namespace internal {
  84. template <typename NormalMap>
  85. class small_map_default_init {
  86. public:
  87. void operator()(NormalMap* map) const { new (map) NormalMap(); }
  88. };
  89. // has_key_equal<M>::value is true iff there exists a type M::key_equal. This is
  90. // used to dispatch to one of the select_equal_key<> metafunctions below.
  91. template <typename M>
  92. struct has_key_equal {
  93. typedef char sml; // "small" is sometimes #defined so we use an abbreviation.
  94. typedef struct { char dummy[2]; } big;
  95. // Two functions, one accepts types that have a key_equal member, and one that
  96. // accepts anything. They each return a value of a different size, so we can
  97. // determine at compile-time which function would have been called.
  98. template <typename U> static big test(typename U::key_equal*);
  99. template <typename> static sml test(...);
  100. // Determines if M::key_equal exists by looking at the size of the return
  101. // type of the compiler-chosen test() function.
  102. static const bool value = (sizeof(test<M>(0)) == sizeof(big));
  103. };
  104. template <typename M> const bool has_key_equal<M>::value;
  105. // Base template used for map types that do NOT have an M::key_equal member,
  106. // e.g., std::map<>. These maps have a strict weak ordering comparator rather
  107. // than an equality functor, so equality will be implemented in terms of that
  108. // comparator.
  109. //
  110. // There's a partial specialization of this template below for map types that do
  111. // have an M::key_equal member.
  112. template <typename M, bool has_key_equal_value>
  113. struct select_equal_key {
  114. struct equal_key {
  115. bool operator()(const typename M::key_type& left,
  116. const typename M::key_type& right) {
  117. // Implements equality in terms of a strict weak ordering comparator.
  118. typename M::key_compare comp;
  119. return !comp(left, right) && !comp(right, left);
  120. }
  121. };
  122. };
  123. // Partial template specialization handles case where M::key_equal exists, e.g.,
  124. // unordered_map<>.
  125. template <typename M>
  126. struct select_equal_key<M, true> {
  127. typedef typename M::key_equal equal_key;
  128. };
  129. } // namespace internal
  130. template <typename NormalMap,
  131. size_t kArraySize = 4,
  132. typename EqualKey = typename internal::select_equal_key<
  133. NormalMap,
  134. internal::has_key_equal<NormalMap>::value>::equal_key,
  135. typename MapInit = internal::small_map_default_init<NormalMap>>
  136. class small_map {
  137. static_assert(kArraySize > 0, "Initial size must be greater than 0");
  138. static_assert(kArraySize != kUsingFullMapSentinel,
  139. "Initial size out of range");
  140. public:
  141. typedef typename NormalMap::key_type key_type;
  142. typedef typename NormalMap::mapped_type data_type;
  143. typedef typename NormalMap::mapped_type mapped_type;
  144. typedef typename NormalMap::value_type value_type;
  145. typedef EqualKey key_equal;
  146. small_map() : size_(0), functor_(MapInit()) {}
  147. explicit small_map(const MapInit& functor) : size_(0), functor_(functor) {}
  148. // Allow copy-constructor and assignment, since STL allows them too.
  149. small_map(const small_map& src) {
  150. // size_ and functor_ are initted in InitFrom()
  151. InitFrom(src);
  152. }
  153. void operator=(const small_map& src) {
  154. if (&src == this) return;
  155. // This is not optimal. If src and dest are both using the small array, we
  156. // could skip the teardown and reconstruct. One problem to be resolved is
  157. // that the value_type itself is pair<const K, V>, and const K is not
  158. // assignable.
  159. Destroy();
  160. InitFrom(src);
  161. }
  162. ~small_map() { Destroy(); }
  163. class const_iterator;
  164. class iterator {
  165. public:
  166. typedef typename NormalMap::iterator::iterator_category iterator_category;
  167. typedef typename NormalMap::iterator::value_type value_type;
  168. typedef typename NormalMap::iterator::difference_type difference_type;
  169. typedef typename NormalMap::iterator::pointer pointer;
  170. typedef typename NormalMap::iterator::reference reference;
  171. inline iterator() : array_iter_(nullptr) {}
  172. inline iterator& operator++() {
  173. if (array_iter_ != nullptr) {
  174. ++array_iter_;
  175. } else {
  176. ++map_iter_;
  177. }
  178. return *this;
  179. }
  180. inline iterator operator++(int /*unused*/) {
  181. iterator result(*this);
  182. ++(*this);
  183. return result;
  184. }
  185. inline iterator& operator--() {
  186. if (array_iter_ != nullptr) {
  187. --array_iter_;
  188. } else {
  189. --map_iter_;
  190. }
  191. return *this;
  192. }
  193. inline iterator operator--(int /*unused*/) {
  194. iterator result(*this);
  195. --(*this);
  196. return result;
  197. }
  198. inline value_type* operator->() const {
  199. return array_iter_ ? array_iter_ : map_iter_.operator->();
  200. }
  201. inline value_type& operator*() const {
  202. return array_iter_ ? *array_iter_ : *map_iter_;
  203. }
  204. inline bool operator==(const iterator& other) const {
  205. if (array_iter_ != nullptr) {
  206. return array_iter_ == other.array_iter_;
  207. } else {
  208. return other.array_iter_ == nullptr && map_iter_ == other.map_iter_;
  209. }
  210. }
  211. inline bool operator!=(const iterator& other) const {
  212. return !(*this == other);
  213. }
  214. bool operator==(const const_iterator& other) const;
  215. bool operator!=(const const_iterator& other) const;
  216. private:
  217. friend class small_map;
  218. friend class const_iterator;
  219. inline explicit iterator(value_type* init) : array_iter_(init) {}
  220. inline explicit iterator(const typename NormalMap::iterator& init)
  221. : array_iter_(nullptr), map_iter_(init) {}
  222. value_type* array_iter_;
  223. typename NormalMap::iterator map_iter_;
  224. };
  225. class const_iterator {
  226. public:
  227. typedef typename NormalMap::const_iterator::iterator_category
  228. iterator_category;
  229. typedef typename NormalMap::const_iterator::value_type value_type;
  230. typedef typename NormalMap::const_iterator::difference_type difference_type;
  231. typedef typename NormalMap::const_iterator::pointer pointer;
  232. typedef typename NormalMap::const_iterator::reference reference;
  233. inline const_iterator() : array_iter_(nullptr) {}
  234. // Non-explicit constructor lets us convert regular iterators to const
  235. // iterators.
  236. inline const_iterator(const iterator& other)
  237. : array_iter_(other.array_iter_), map_iter_(other.map_iter_) {}
  238. inline const_iterator& operator++() {
  239. if (array_iter_ != nullptr) {
  240. ++array_iter_;
  241. } else {
  242. ++map_iter_;
  243. }
  244. return *this;
  245. }
  246. inline const_iterator operator++(int /*unused*/) {
  247. const_iterator result(*this);
  248. ++(*this);
  249. return result;
  250. }
  251. inline const_iterator& operator--() {
  252. if (array_iter_ != nullptr) {
  253. --array_iter_;
  254. } else {
  255. --map_iter_;
  256. }
  257. return *this;
  258. }
  259. inline const_iterator operator--(int /*unused*/) {
  260. const_iterator result(*this);
  261. --(*this);
  262. return result;
  263. }
  264. inline const value_type* operator->() const {
  265. return array_iter_ ? array_iter_ : map_iter_.operator->();
  266. }
  267. inline const value_type& operator*() const {
  268. return array_iter_ ? *array_iter_ : *map_iter_;
  269. }
  270. inline bool operator==(const const_iterator& other) const {
  271. if (array_iter_ != nullptr) {
  272. return array_iter_ == other.array_iter_;
  273. }
  274. return other.array_iter_ == nullptr && map_iter_ == other.map_iter_;
  275. }
  276. inline bool operator!=(const const_iterator& other) const {
  277. return !(*this == other);
  278. }
  279. private:
  280. friend class small_map;
  281. inline explicit const_iterator(const value_type* init)
  282. : array_iter_(init) {}
  283. inline explicit const_iterator(
  284. const typename NormalMap::const_iterator& init)
  285. : array_iter_(nullptr), map_iter_(init) {}
  286. const value_type* array_iter_;
  287. typename NormalMap::const_iterator map_iter_;
  288. };
  289. iterator find(const key_type& key) {
  290. key_equal compare;
  291. if (UsingFullMap()) {
  292. return iterator(map()->find(key));
  293. }
  294. for (size_t i = 0; i < size_; ++i) {
  295. if (compare(array_[i].first, key)) {
  296. return iterator(array_ + i);
  297. }
  298. }
  299. return iterator(array_ + size_);
  300. }
  301. const_iterator find(const key_type& key) const {
  302. key_equal compare;
  303. if (UsingFullMap()) {
  304. return const_iterator(map()->find(key));
  305. }
  306. for (size_t i = 0; i < size_; ++i) {
  307. if (compare(array_[i].first, key)) {
  308. return const_iterator(array_ + i);
  309. }
  310. }
  311. return const_iterator(array_ + size_);
  312. }
  313. // Invalidates iterators.
  314. data_type& operator[](const key_type& key) {
  315. key_equal compare;
  316. if (UsingFullMap()) {
  317. return map_[key];
  318. }
  319. // Search backwards to favor recently-added elements.
  320. for (size_t i = size_; i > 0; --i) {
  321. const size_t index = i - 1;
  322. if (compare(array_[index].first, key)) {
  323. return array_[index].second;
  324. }
  325. }
  326. if (size_ == kArraySize) {
  327. ConvertToRealMap();
  328. return map_[key];
  329. }
  330. DCHECK(size_ < kArraySize);
  331. new (&array_[size_]) value_type(key, data_type());
  332. return array_[size_++].second;
  333. }
  334. // Invalidates iterators.
  335. std::pair<iterator, bool> insert(const value_type& x) {
  336. key_equal compare;
  337. if (UsingFullMap()) {
  338. std::pair<typename NormalMap::iterator, bool> ret = map_.insert(x);
  339. return std::make_pair(iterator(ret.first), ret.second);
  340. }
  341. for (size_t i = 0; i < size_; ++i) {
  342. if (compare(array_[i].first, x.first)) {
  343. return std::make_pair(iterator(array_ + i), false);
  344. }
  345. }
  346. if (size_ == kArraySize) {
  347. ConvertToRealMap(); // Invalidates all iterators!
  348. std::pair<typename NormalMap::iterator, bool> ret = map_.insert(x);
  349. return std::make_pair(iterator(ret.first), ret.second);
  350. }
  351. DCHECK(size_ < kArraySize);
  352. new (&array_[size_]) value_type(x);
  353. return std::make_pair(iterator(array_ + size_++), true);
  354. }
  355. // Invalidates iterators.
  356. template <class InputIterator>
  357. void insert(InputIterator f, InputIterator l) {
  358. while (f != l) {
  359. insert(*f);
  360. ++f;
  361. }
  362. }
  363. // Invalidates iterators.
  364. template <typename... Args>
  365. std::pair<iterator, bool> emplace(Args&&... args) {
  366. key_equal compare;
  367. if (UsingFullMap()) {
  368. std::pair<typename NormalMap::iterator, bool> ret =
  369. map_.emplace(std::forward<Args>(args)...);
  370. return std::make_pair(iterator(ret.first), ret.second);
  371. }
  372. value_type x(std::forward<Args>(args)...);
  373. for (size_t i = 0; i < size_; ++i) {
  374. if (compare(array_[i].first, x.first)) {
  375. return std::make_pair(iterator(array_ + i), false);
  376. }
  377. }
  378. if (size_ == kArraySize) {
  379. ConvertToRealMap(); // Invalidates all iterators!
  380. std::pair<typename NormalMap::iterator, bool> ret =
  381. map_.emplace(std::move(x));
  382. return std::make_pair(iterator(ret.first), ret.second);
  383. }
  384. DCHECK(size_ < kArraySize);
  385. new (&array_[size_]) value_type(std::move(x));
  386. return std::make_pair(iterator(array_ + size_++), true);
  387. }
  388. iterator begin() {
  389. return UsingFullMap() ? iterator(map_.begin()) : iterator(array_);
  390. }
  391. const_iterator begin() const {
  392. return UsingFullMap() ? const_iterator(map_.begin())
  393. : const_iterator(array_);
  394. }
  395. iterator end() {
  396. return UsingFullMap() ? iterator(map_.end()) : iterator(array_ + size_);
  397. }
  398. const_iterator end() const {
  399. return UsingFullMap() ? const_iterator(map_.end())
  400. : const_iterator(array_ + size_);
  401. }
  402. void clear() {
  403. if (UsingFullMap()) {
  404. map_.~NormalMap();
  405. } else {
  406. for (size_t i = 0; i < size_; ++i) {
  407. array_[i].~value_type();
  408. }
  409. }
  410. size_ = 0;
  411. }
  412. // Invalidates iterators. Returns iterator following the last removed element.
  413. iterator erase(const iterator& position) {
  414. if (UsingFullMap()) {
  415. return iterator(map_.erase(position.map_iter_));
  416. }
  417. size_t i = position.array_iter_ - array_;
  418. // TODO(crbug.com/817982): When we have a checked iterator, this CHECK might
  419. // not be necessary.
  420. CHECK_LE(i, size_);
  421. array_[i].~value_type();
  422. --size_;
  423. if (i != size_) {
  424. new (&array_[i]) value_type(std::move(array_[size_]));
  425. array_[size_].~value_type();
  426. return iterator(array_ + i);
  427. }
  428. return end();
  429. }
  430. size_t erase(const key_type& key) {
  431. iterator iter = find(key);
  432. if (iter == end()) {
  433. return 0;
  434. }
  435. erase(iter);
  436. return 1;
  437. }
  438. size_t count(const key_type& key) const {
  439. return (find(key) == end()) ? 0 : 1;
  440. }
  441. size_t size() const { return UsingFullMap() ? map_.size() : size_; }
  442. bool empty() const { return UsingFullMap() ? map_.empty() : size_ == 0; }
  443. // Returns true if we have fallen back to using the underlying map
  444. // representation.
  445. bool UsingFullMap() const { return size_ == kUsingFullMapSentinel; }
  446. inline NormalMap* map() {
  447. CHECK(UsingFullMap());
  448. return &map_;
  449. }
  450. inline const NormalMap* map() const {
  451. CHECK(UsingFullMap());
  452. return &map_;
  453. }
  454. private:
  455. // When `size_ == kUsingFullMapSentinel`, we have switched storage strategies
  456. // from `array_[kArraySize] to `NormalMap map_`. See ConvertToRealMap and
  457. // UsingFullMap.
  458. size_t size_;
  459. MapInit functor_;
  460. // We want to call constructors and destructors manually, but we don't want
  461. // to allocate and deallocate the memory used for them separately. Since
  462. // array_ and map_ are mutually exclusive, we'll put them in a union.
  463. union {
  464. value_type array_[kArraySize];
  465. NormalMap map_;
  466. };
  467. void ConvertToRealMap() {
  468. // Storage for the elements in the temporary array. This is intentionally
  469. // declared as a union to avoid having to default-construct |kArraySize|
  470. // elements, only to move construct over them in the initial loop.
  471. union Storage {
  472. Storage() {}
  473. ~Storage() {}
  474. value_type array[kArraySize];
  475. } temp;
  476. // Move the current elements into a temporary array.
  477. for (size_t i = 0; i < kArraySize; ++i) {
  478. new (&temp.array[i]) value_type(std::move(array_[i]));
  479. array_[i].~value_type();
  480. }
  481. // Initialize the map.
  482. size_ = kUsingFullMapSentinel;
  483. functor_(&map_);
  484. // Insert elements into it.
  485. for (size_t i = 0; i < kArraySize; ++i) {
  486. map_.insert(std::move(temp.array[i]));
  487. temp.array[i].~value_type();
  488. }
  489. }
  490. // Helpers for constructors and destructors.
  491. void InitFrom(const small_map& src) {
  492. functor_ = src.functor_;
  493. size_ = src.size_;
  494. if (src.UsingFullMap()) {
  495. functor_(&map_);
  496. map_ = src.map_;
  497. } else {
  498. for (size_t i = 0; i < size_; ++i) {
  499. new (&array_[i]) value_type(src.array_[i]);
  500. }
  501. }
  502. }
  503. void Destroy() {
  504. if (UsingFullMap()) {
  505. map_.~NormalMap();
  506. } else {
  507. for (size_t i = 0; i < size_; ++i) {
  508. array_[i].~value_type();
  509. }
  510. }
  511. }
  512. };
  513. template <typename NormalMap,
  514. size_t kArraySize,
  515. typename EqualKey,
  516. typename Functor>
  517. inline bool small_map<NormalMap, kArraySize, EqualKey, Functor>::iterator::
  518. operator==(const const_iterator& other) const {
  519. return other == *this;
  520. }
  521. template <typename NormalMap,
  522. size_t kArraySize,
  523. typename EqualKey,
  524. typename Functor>
  525. inline bool small_map<NormalMap, kArraySize, EqualKey, Functor>::iterator::
  526. operator!=(const const_iterator& other) const {
  527. return other != *this;
  528. }
  529. } // namespace base
  530. #endif // BASE_CONTAINERS_SMALL_MAP_H_