1 #pragma once
2
3 #include <mbgl/util/immutable.hpp>
4 #include <mbgl/util/optional.hpp>
5
6 #include <memory>
7 #include <string>
8
9 namespace mbgl {
10 namespace style {
11
12 /*
13 Manages an ordered collection of elements and their `Immutable<Impl>`s. The latter is
14 itself stored in an Immutable container. Using immutability at the collection level
15 allows us to short-circuit significant portions of the RenderStyle update logic via
16 a simple pointer equality check, greatly improving performance.
17
18 Element types are required to have:
19
20 * An `Impl` inner class type
21 * An `Immutable<Impl> baseImpl` member
22 * A `std::string getID() const` method
23 */
24 template <class T>
25 class Collection {
26 public:
27 using Impl = typename T::Impl;
28 using WrapperVector = std::vector<std::unique_ptr<T>>;
29 using ImmutableVector = Immutable<std::vector<Immutable<Impl>>>;
30
31 Collection();
32
33 std::size_t size() const;
34 T* get(const std::string&) const;
35
36 std::vector<T*> getWrappers() const;
getImpls() const37 ImmutableVector getImpls() const { return impls; }
38
begin() const39 auto begin() const { return wrappers.begin(); }
end() const40 auto end() const { return wrappers.end(); }
41
42 void clear();
43
44 T* add(std::unique_ptr<T>, const optional<std::string>& = {});
45 std::unique_ptr<T> remove(const std::string&);
46
47 // Must be called whenever an element of the collection is internally mutated.
48 // Typically, each element permits registration of an observer, and the observer
49 // should call this method.
50 void update(const T&);
51
52 private:
53 std::size_t index(const std::string&) const;
54
55 WrapperVector wrappers;
56 ImmutableVector impls;
57 };
58
59 template <class T>
Collection()60 Collection<T>::Collection()
61 : impls(makeMutable<std::vector<Immutable<Impl>>>()) {
62 }
63
64 template <class T>
size() const65 std::size_t Collection<T>::size() const {
66 return wrappers.size();
67 }
68
69 template <class T>
index(const std::string & id) const70 std::size_t Collection<T>::index(const std::string& id) const {
71 return std::find_if(wrappers.begin(), wrappers.end(), [&](const auto& e) {
72 return e->getID() == id;
73 }) - wrappers.begin();
74 }
75
76 template <class T>
get(const std::string & id) const77 T* Collection<T>::get(const std::string& id) const {
78 std::size_t i = index(id);
79 return i < size() ? wrappers[i].get() : nullptr;
80 }
81
82 template <class T>
getWrappers() const83 std::vector<T*> Collection<T>::getWrappers() const {
84 std::vector<T*> result;
85 result.reserve(wrappers.size());
86
87 for (auto& wrapper : wrappers) {
88 result.push_back(wrapper.get());
89 }
90
91 return result;
92 }
93
94 template <class T>
clear()95 void Collection<T>::clear() {
96 mutate(impls, [&] (auto& impls_) {
97 impls_.clear();
98 });
99
100 wrappers.clear();
101 }
102
103 template <class T>
add(std::unique_ptr<T> wrapper,const optional<std::string> & before)104 T* Collection<T>::add(std::unique_ptr<T> wrapper, const optional<std::string>& before) {
105 std::size_t i = before ? index(*before) : size();
106
107 mutate(impls, [&] (auto& impls_) {
108 impls_.emplace(impls_.begin() + i, wrapper->baseImpl);
109 });
110
111 return wrappers.emplace(wrappers.begin() + i, std::move(wrapper))->get();
112 }
113
114 template <class T>
remove(const std::string & id)115 std::unique_ptr<T> Collection<T>::remove(const std::string& id) {
116 std::size_t i = index(id);
117
118 if (i >= size()) {
119 return nullptr;
120 }
121
122 auto source = std::move(wrappers[i]);
123
124 mutate(impls, [&] (auto& impls_) {
125 impls_.erase(impls_.begin() + i);
126 });
127
128 wrappers.erase(wrappers.begin() + i);
129
130 return source;
131 }
132
133 template <class T>
update(const T & wrapper)134 void Collection<T>::update(const T& wrapper) {
135 mutate(impls, [&] (auto& impls_) {
136 impls_.at(this->index(wrapper.getID())) = wrapper.baseImpl;
137 });
138 }
139
140 } // namespace style
141 } // namespace mbgl
142