|
6 | 6 | #include "Differentiator.h" |
7 | 7 |
|
8 | 8 | #include <better/map.h> |
| 9 | +#include <better/small_vector.h> |
9 | 10 | #include <react/core/LayoutableShadowNode.h> |
10 | 11 | #include <react/debug/SystraceSection.h> |
11 | 12 | #include "ShadowView.h" |
12 | 13 |
|
13 | 14 | namespace facebook { |
14 | 15 | namespace react { |
15 | 16 |
|
| 17 | +/* |
| 18 | + * Extremely simple and naive implementation of a map. |
| 19 | + * The map is simple but it's optimized for particular constraints that we have |
| 20 | + * here. |
| 21 | + * |
| 22 | + * A regular map implementation (e.g. `std::unordered_map`) has some basic |
| 23 | + * performance guarantees like constant average insertion and lookup complexity. |
| 24 | + * This is nice, but it's *average* complexity measured on a non-trivial amount |
| 25 | + * of data. The regular map is a very complex data structure that using hashing, |
| 26 | + * buckets, multiple comprising operations, multiple allocations and so on. |
| 27 | + * |
| 28 | + * In our particular case, we need a map for `int` to `void *` with a dozen |
| 29 | + * values. In these conditions, nothing can beat a naive implementation using a |
| 30 | + * stack-allocated vector. And this implementation is exactly this: no |
| 31 | + * allocation, no hashing, no complex branching, no buckets, no iterators, no |
| 32 | + * rehashing, no other guarantees. It's crazy limited, unsafe, and performant on |
| 33 | + * a trivial amount of data. |
| 34 | + * |
| 35 | + * Besides that, we also need to optimize for insertion performance (the case |
| 36 | + * where a bunch of views appears on the screen first time); in this |
| 37 | + * implementation, this is as performant as vector `push_back`. |
| 38 | + */ |
| 39 | +template <typename KeyT, typename ValueT, int DefaultSize = 16> |
| 40 | +class TinyMap final { |
| 41 | + public: |
| 42 | + using Pair = std::pair<KeyT, ValueT>; |
| 43 | + using Iterator = Pair *; |
| 44 | + |
| 45 | + inline Iterator begin() { |
| 46 | + return (Pair *)vector_; |
| 47 | + } |
| 48 | + |
| 49 | + inline Iterator end() { |
| 50 | + return nullptr; |
| 51 | + } |
| 52 | + |
| 53 | + inline Iterator find(KeyT key) { |
| 54 | + for (auto &item : vector_) { |
| 55 | + if (item.first == key) { |
| 56 | + return &item; |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + return end(); |
| 61 | + } |
| 62 | + |
| 63 | + inline void insert(Pair pair) { |
| 64 | + assert(pair.first != 0); |
| 65 | + vector_.push_back(pair); |
| 66 | + } |
| 67 | + |
| 68 | + inline void erase(Iterator iterator) { |
| 69 | + static_assert( |
| 70 | + std::is_same<KeyT, Tag>::value, |
| 71 | + "The collection is designed to store only `Tag`s as keys."); |
| 72 | + // Zero is a invalid tag. |
| 73 | + iterator->first = 0; |
| 74 | + } |
| 75 | + |
| 76 | + private: |
| 77 | + better::small_vector<Pair, DefaultSize> vector_; |
| 78 | +}; |
| 79 | + |
16 | 80 | static void sliceChildShadowNodeViewPairsRecursively( |
17 | 81 | ShadowViewNodePair::List &pairList, |
18 | 82 | Point layoutOffset, |
@@ -70,7 +134,7 @@ static void calculateShadowViewMutations( |
70 | 134 | auto index = int{0}; |
71 | 135 |
|
72 | 136 | // Maps inserted node tags to pointers to them in `newChildPairs`. |
73 | | - auto insertedPairs = better::map<Tag, ShadowViewNodePair const *>{}; |
| 137 | + auto insertedPairs = TinyMap<Tag, ShadowViewNodePair const *>{}; |
74 | 138 |
|
75 | 139 | // Lists of mutations |
76 | 140 | auto createMutations = ShadowViewMutation::List{}; |
|
0 commit comments