1 // Copyright (c) 2011 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
5 // Derived from google3/util/gtl/stl_util.h
6
7 #ifndef BASE_STL_UTIL_H_
8 #define BASE_STL_UTIL_H_
9
10 #include <algorithm>
11 #include <deque>
12 #include <forward_list>
13 #include <functional>
14 #include <initializer_list>
15 #include <iterator>
16 #include <list>
17 #include <map>
18 #include <set>
19 #include <string>
20 #include <unordered_map>
21 #include <unordered_set>
22 #include <vector>
23
24 #include "base/logging.h"
25
26 namespace base {
27
28 namespace internal {
29
30 // Calls erase on iterators of matching elements.
31 template <typename Container, typename Predicate>
IterateAndEraseIf(Container & container,Predicate pred)32 void IterateAndEraseIf(Container& container, Predicate pred) {
33 for (auto it = container.begin(); it != container.end();) {
34 if (pred(*it))
35 it = container.erase(it);
36 else
37 ++it;
38 }
39 }
40
41 } // namespace internal
42
43 // Test to see if a set or map contains a particular key.
44 // Returns true if the key is in the collection.
45 template <typename Collection, typename Key>
ContainsKey(const Collection & collection,const Key & key)46 bool ContainsKey(const Collection& collection, const Key& key) {
47 return collection.find(key) != collection.end();
48 }
49
50 namespace internal {
51
52 template <typename Collection>
53 class HasKeyType {
54 template <typename C>
55 static std::true_type test(typename C::key_type*);
56 template <typename C>
57 static std::false_type test(...);
58
59 public:
60 static constexpr bool value = decltype(test<Collection>(nullptr))::value;
61 };
62
63 } // namespace internal
64
65 // Test to see if a collection like a vector contains a particular value.
66 // Returns true if the value is in the collection.
67 // Don't use this on collections such as sets or maps. This is enforced by
68 // disabling this method if the collection defines a key_type.
69 template <typename Collection,
70 typename Value,
71 typename std::enable_if<!internal::HasKeyType<Collection>::value,
72 int>::type = 0>
ContainsValue(const Collection & collection,const Value & value)73 bool ContainsValue(const Collection& collection, const Value& value) {
74 return std::find(std::begin(collection), std::end(collection), value) !=
75 std::end(collection);
76 }
77
78 // Returns true if the container is sorted.
79 template <typename Container>
STLIsSorted(const Container & cont)80 bool STLIsSorted(const Container& cont) {
81 // Note: Use reverse iterator on container to ensure we only require
82 // value_type to implement operator<.
83 return std::adjacent_find(cont.rbegin(), cont.rend(),
84 std::less<typename Container::value_type>()) ==
85 cont.rend();
86 }
87
88 // Returns a new ResultType containing the difference of two sorted containers.
89 template <typename ResultType, typename Arg1, typename Arg2>
STLSetDifference(const Arg1 & a1,const Arg2 & a2)90 ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) {
91 DCHECK(STLIsSorted(a1));
92 DCHECK(STLIsSorted(a2));
93 ResultType difference;
94 std::set_difference(a1.begin(), a1.end(), a2.begin(), a2.end(),
95 std::inserter(difference, difference.end()));
96 return difference;
97 }
98
99 // Returns a new ResultType containing the union of two sorted containers.
100 template <typename ResultType, typename Arg1, typename Arg2>
STLSetUnion(const Arg1 & a1,const Arg2 & a2)101 ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) {
102 DCHECK(STLIsSorted(a1));
103 DCHECK(STLIsSorted(a2));
104 ResultType result;
105 std::set_union(a1.begin(), a1.end(), a2.begin(), a2.end(),
106 std::inserter(result, result.end()));
107 return result;
108 }
109
110 // Returns a new ResultType containing the intersection of two sorted
111 // containers.
112 template <typename ResultType, typename Arg1, typename Arg2>
STLSetIntersection(const Arg1 & a1,const Arg2 & a2)113 ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) {
114 DCHECK(STLIsSorted(a1));
115 DCHECK(STLIsSorted(a2));
116 ResultType result;
117 std::set_intersection(a1.begin(), a1.end(), a2.begin(), a2.end(),
118 std::inserter(result, result.end()));
119 return result;
120 }
121
122 // Returns true if the sorted container |a1| contains all elements of the sorted
123 // container |a2|.
124 template <typename Arg1, typename Arg2>
STLIncludes(const Arg1 & a1,const Arg2 & a2)125 bool STLIncludes(const Arg1& a1, const Arg2& a2) {
126 DCHECK(STLIsSorted(a1));
127 DCHECK(STLIsSorted(a2));
128 return std::includes(a1.begin(), a1.end(), a2.begin(), a2.end());
129 }
130
131 // Erase/EraseIf are based on library fundamentals ts v2 erase/erase_if
132 // http://en.cppreference.com/w/cpp/experimental/lib_extensions_2
133 // They provide a generic way to erase elements from a container.
134 // The functions here implement these for the standard containers until those
135 // functions are available in the C++ standard.
136 // For Chromium containers overloads should be defined in their own headers
137 // (like standard containers).
138 // Note: there is no std::erase for standard associative containers so we don't
139 // have it either.
140
141 template <typename CharT, typename Traits, typename Allocator, typename Value>
Erase(std::basic_string<CharT,Traits,Allocator> & container,const Value & value)142 void Erase(std::basic_string<CharT, Traits, Allocator>& container,
143 const Value& value) {
144 container.erase(std::remove(container.begin(), container.end(), value),
145 container.end());
146 }
147
148 template <typename CharT, typename Traits, typename Allocator, class Predicate>
EraseIf(std::basic_string<CharT,Traits,Allocator> & container,Predicate pred)149 void EraseIf(std::basic_string<CharT, Traits, Allocator>& container,
150 Predicate pred) {
151 container.erase(std::remove_if(container.begin(), container.end(), pred),
152 container.end());
153 }
154
155 template <class T, class Allocator, class Value>
Erase(std::deque<T,Allocator> & container,const Value & value)156 void Erase(std::deque<T, Allocator>& container, const Value& value) {
157 container.erase(std::remove(container.begin(), container.end(), value),
158 container.end());
159 }
160
161 template <class T, class Allocator, class Predicate>
EraseIf(std::deque<T,Allocator> & container,Predicate pred)162 void EraseIf(std::deque<T, Allocator>& container, Predicate pred) {
163 container.erase(std::remove_if(container.begin(), container.end(), pred),
164 container.end());
165 }
166
167 template <class T, class Allocator, class Value>
Erase(std::vector<T,Allocator> & container,const Value & value)168 void Erase(std::vector<T, Allocator>& container, const Value& value) {
169 container.erase(std::remove(container.begin(), container.end(), value),
170 container.end());
171 }
172
173 template <class T, class Allocator, class Predicate>
EraseIf(std::vector<T,Allocator> & container,Predicate pred)174 void EraseIf(std::vector<T, Allocator>& container, Predicate pred) {
175 container.erase(std::remove_if(container.begin(), container.end(), pred),
176 container.end());
177 }
178
179 template <class T, class Allocator, class Value>
Erase(std::forward_list<T,Allocator> & container,const Value & value)180 void Erase(std::forward_list<T, Allocator>& container, const Value& value) {
181 // Unlike std::forward_list::remove, this function template accepts
182 // heterogeneous types and does not force a conversion to the container's
183 // value type before invoking the == operator.
184 container.remove_if([&](const T& cur) { return cur == value; });
185 }
186
187 template <class T, class Allocator, class Predicate>
EraseIf(std::forward_list<T,Allocator> & container,Predicate pred)188 void EraseIf(std::forward_list<T, Allocator>& container, Predicate pred) {
189 container.remove_if(pred);
190 }
191
192 template <class T, class Allocator, class Value>
Erase(std::list<T,Allocator> & container,const Value & value)193 void Erase(std::list<T, Allocator>& container, const Value& value) {
194 // Unlike std::list::remove, this function template accepts heterogeneous
195 // types and does not force a conversion to the container's value type before
196 // invoking the == operator.
197 container.remove_if([&](const T& cur) { return cur == value; });
198 }
199
200 template <class T, class Allocator, class Predicate>
EraseIf(std::list<T,Allocator> & container,Predicate pred)201 void EraseIf(std::list<T, Allocator>& container, Predicate pred) {
202 container.remove_if(pred);
203 }
204
205 template <class Key, class T, class Compare, class Allocator, class Predicate>
EraseIf(std::map<Key,T,Compare,Allocator> & container,Predicate pred)206 void EraseIf(std::map<Key, T, Compare, Allocator>& container, Predicate pred) {
207 internal::IterateAndEraseIf(container, pred);
208 }
209
210 template <class Key, class T, class Compare, class Allocator, class Predicate>
EraseIf(std::multimap<Key,T,Compare,Allocator> & container,Predicate pred)211 void EraseIf(std::multimap<Key, T, Compare, Allocator>& container,
212 Predicate pred) {
213 internal::IterateAndEraseIf(container, pred);
214 }
215
216 template <class Key, class Compare, class Allocator, class Predicate>
EraseIf(std::set<Key,Compare,Allocator> & container,Predicate pred)217 void EraseIf(std::set<Key, Compare, Allocator>& container, Predicate pred) {
218 internal::IterateAndEraseIf(container, pred);
219 }
220
221 template <class Key, class Compare, class Allocator, class Predicate>
EraseIf(std::multiset<Key,Compare,Allocator> & container,Predicate pred)222 void EraseIf(std::multiset<Key, Compare, Allocator>& container,
223 Predicate pred) {
224 internal::IterateAndEraseIf(container, pred);
225 }
226
227 template <class Key,
228 class T,
229 class Hash,
230 class KeyEqual,
231 class Allocator,
232 class Predicate>
EraseIf(std::unordered_map<Key,T,Hash,KeyEqual,Allocator> & container,Predicate pred)233 void EraseIf(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& container,
234 Predicate pred) {
235 internal::IterateAndEraseIf(container, pred);
236 }
237
238 template <class Key,
239 class T,
240 class Hash,
241 class KeyEqual,
242 class Allocator,
243 class Predicate>
EraseIf(std::unordered_multimap<Key,T,Hash,KeyEqual,Allocator> & container,Predicate pred)244 void EraseIf(
245 std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& container,
246 Predicate pred) {
247 internal::IterateAndEraseIf(container, pred);
248 }
249
250 template <class Key,
251 class Hash,
252 class KeyEqual,
253 class Allocator,
254 class Predicate>
EraseIf(std::unordered_set<Key,Hash,KeyEqual,Allocator> & container,Predicate pred)255 void EraseIf(std::unordered_set<Key, Hash, KeyEqual, Allocator>& container,
256 Predicate pred) {
257 internal::IterateAndEraseIf(container, pred);
258 }
259
260 template <class Key,
261 class Hash,
262 class KeyEqual,
263 class Allocator,
264 class Predicate>
EraseIf(std::unordered_multiset<Key,Hash,KeyEqual,Allocator> & container,Predicate pred)265 void EraseIf(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& container,
266 Predicate pred) {
267 internal::IterateAndEraseIf(container, pred);
268 }
269
270 } // namespace base
271
272 #endif // BASE_STL_UTIL_H_
273