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