• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "base/optional.h"
26 
27 namespace base {
28 
29 namespace internal {
30 
31 // Calls erase on iterators of matching elements.
32 template <typename Container, typename Predicate>
IterateAndEraseIf(Container & container,Predicate pred)33 void IterateAndEraseIf(Container& container, Predicate pred) {
34   for (auto it = container.begin(); it != container.end();) {
35     if (pred(*it))
36       it = container.erase(it);
37     else
38       ++it;
39   }
40 }
41 
42 }  // namespace internal
43 
44 // C++14 implementation of C++17's std::size():
45 // http://en.cppreference.com/w/cpp/iterator/size
46 template <typename Container>
47 constexpr auto size(const Container& c) -> decltype(c.size()) {
48   return c.size();
49 }
50 
51 template <typename T, size_t N>
size(const T (& array)[N])52 constexpr size_t size(const T (&array)[N]) noexcept {
53   return N;
54 }
55 
56 // C++14 implementation of C++17's std::empty():
57 // http://en.cppreference.com/w/cpp/iterator/empty
58 template <typename Container>
59 constexpr auto empty(const Container& c) -> decltype(c.empty()) {
60   return c.empty();
61 }
62 
63 template <typename T, size_t N>
empty(const T (& array)[N])64 constexpr bool empty(const T (&array)[N]) noexcept {
65   return false;
66 }
67 
68 template <typename T>
empty(std::initializer_list<T> il)69 constexpr bool empty(std::initializer_list<T> il) noexcept {
70   return il.size() == 0;
71 }
72 
73 // C++14 implementation of C++17's std::data():
74 // http://en.cppreference.com/w/cpp/iterator/data
75 template <typename Container>
76 constexpr auto data(Container& c) -> decltype(c.data()) {
77   return c.data();
78 }
79 
80 // std::basic_string::data() had no mutable overload prior to C++17 [1].
81 // Hence this overload is provided.
82 // Note: str[0] is safe even for empty strings, as they are guaranteed to be
83 // null-terminated [2].
84 //
85 // [1] http://en.cppreference.com/w/cpp/string/basic_string/data
86 // [2] http://en.cppreference.com/w/cpp/string/basic_string/operator_at
87 template <typename CharT, typename Traits, typename Allocator>
data(std::basic_string<CharT,Traits,Allocator> & str)88 CharT* data(std::basic_string<CharT, Traits, Allocator>& str) {
89   return std::addressof(str[0]);
90 }
91 
92 template <typename Container>
93 constexpr auto data(const Container& c) -> decltype(c.data()) {
94   return c.data();
95 }
96 
97 template <typename T, size_t N>
data(T (& array)[N])98 constexpr T* data(T (&array)[N]) noexcept {
99   return array;
100 }
101 
102 template <typename T>
data(std::initializer_list<T> il)103 constexpr const T* data(std::initializer_list<T> il) noexcept {
104   return il.begin();
105 }
106 
107 // Returns a const reference to the underlying container of a container adapter.
108 // Works for std::priority_queue, std::queue, and std::stack.
109 template <class A>
GetUnderlyingContainer(const A & adapter)110 const typename A::container_type& GetUnderlyingContainer(const A& adapter) {
111   struct ExposedAdapter : A {
112     using A::c;
113   };
114   return adapter.*&ExposedAdapter::c;
115 }
116 
117 // Clears internal memory of an STL object.
118 // STL clear()/reserve(0) does not always free internal memory allocated
119 // This function uses swap/destructor to ensure the internal memory is freed.
120 template<class T>
STLClearObject(T * obj)121 void STLClearObject(T* obj) {
122   T tmp;
123   tmp.swap(*obj);
124   // Sometimes "T tmp" allocates objects with memory (arena implementation?).
125   // Hence using additional reserve(0) even if it doesn't always work.
126   obj->reserve(0);
127 }
128 
129 // Counts the number of instances of val in a container.
130 template <typename Container, typename T>
131 typename std::iterator_traits<
132     typename Container::const_iterator>::difference_type
STLCount(const Container & container,const T & val)133 STLCount(const Container& container, const T& val) {
134   return std::count(container.begin(), container.end(), val);
135 }
136 
137 // Test to see if a set or map contains a particular key.
138 // Returns true if the key is in the collection.
139 template <typename Collection, typename Key>
ContainsKey(const Collection & collection,const Key & key)140 bool ContainsKey(const Collection& collection, const Key& key) {
141   return collection.find(key) != collection.end();
142 }
143 
144 namespace internal {
145 
146 template <typename Collection>
147 class HasKeyType {
148   template <typename C>
149   static std::true_type test(typename C::key_type*);
150   template <typename C>
151   static std::false_type test(...);
152 
153  public:
154   static constexpr bool value = decltype(test<Collection>(nullptr))::value;
155 };
156 
157 }  // namespace internal
158 
159 // Test to see if a collection like a vector contains a particular value.
160 // Returns true if the value is in the collection.
161 // Don't use this on collections such as sets or maps. This is enforced by
162 // disabling this method if the collection defines a key_type.
163 template <typename Collection,
164           typename Value,
165           typename std::enable_if<!internal::HasKeyType<Collection>::value,
166                                   int>::type = 0>
ContainsValue(const Collection & collection,const Value & value)167 bool ContainsValue(const Collection& collection, const Value& value) {
168   return std::find(std::begin(collection), std::end(collection), value) !=
169          std::end(collection);
170 }
171 
172 // Returns true if the container is sorted.
173 template <typename Container>
STLIsSorted(const Container & cont)174 bool STLIsSorted(const Container& cont) {
175   // Note: Use reverse iterator on container to ensure we only require
176   // value_type to implement operator<.
177   return std::adjacent_find(cont.rbegin(), cont.rend(),
178                             std::less<typename Container::value_type>())
179       == cont.rend();
180 }
181 
182 // Returns a new ResultType containing the difference of two sorted containers.
183 template <typename ResultType, typename Arg1, typename Arg2>
STLSetDifference(const Arg1 & a1,const Arg2 & a2)184 ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) {
185   DCHECK(STLIsSorted(a1));
186   DCHECK(STLIsSorted(a2));
187   ResultType difference;
188   std::set_difference(a1.begin(), a1.end(),
189                       a2.begin(), a2.end(),
190                       std::inserter(difference, difference.end()));
191   return difference;
192 }
193 
194 // Returns a new ResultType containing the union of two sorted containers.
195 template <typename ResultType, typename Arg1, typename Arg2>
STLSetUnion(const Arg1 & a1,const Arg2 & a2)196 ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) {
197   DCHECK(STLIsSorted(a1));
198   DCHECK(STLIsSorted(a2));
199   ResultType result;
200   std::set_union(a1.begin(), a1.end(),
201                  a2.begin(), a2.end(),
202                  std::inserter(result, result.end()));
203   return result;
204 }
205 
206 // Returns a new ResultType containing the intersection of two sorted
207 // containers.
208 template <typename ResultType, typename Arg1, typename Arg2>
STLSetIntersection(const Arg1 & a1,const Arg2 & a2)209 ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) {
210   DCHECK(STLIsSorted(a1));
211   DCHECK(STLIsSorted(a2));
212   ResultType result;
213   std::set_intersection(a1.begin(), a1.end(),
214                         a2.begin(), a2.end(),
215                         std::inserter(result, result.end()));
216   return result;
217 }
218 
219 // Returns true if the sorted container |a1| contains all elements of the sorted
220 // container |a2|.
221 template <typename Arg1, typename Arg2>
STLIncludes(const Arg1 & a1,const Arg2 & a2)222 bool STLIncludes(const Arg1& a1, const Arg2& a2) {
223   DCHECK(STLIsSorted(a1));
224   DCHECK(STLIsSorted(a2));
225   return std::includes(a1.begin(), a1.end(),
226                        a2.begin(), a2.end());
227 }
228 
229 // Erase/EraseIf are based on library fundamentals ts v2 erase/erase_if
230 // http://en.cppreference.com/w/cpp/experimental/lib_extensions_2
231 // They provide a generic way to erase elements from a container.
232 // The functions here implement these for the standard containers until those
233 // functions are available in the C++ standard.
234 // For Chromium containers overloads should be defined in their own headers
235 // (like standard containers).
236 // Note: there is no std::erase for standard associative containers so we don't
237 // have it either.
238 
239 template <typename CharT, typename Traits, typename Allocator, typename Value>
Erase(std::basic_string<CharT,Traits,Allocator> & container,const Value & value)240 void Erase(std::basic_string<CharT, Traits, Allocator>& container,
241            const Value& value) {
242   container.erase(std::remove(container.begin(), container.end(), value),
243                   container.end());
244 }
245 
246 template <typename CharT, typename Traits, typename Allocator, class Predicate>
EraseIf(std::basic_string<CharT,Traits,Allocator> & container,Predicate pred)247 void EraseIf(std::basic_string<CharT, Traits, Allocator>& container,
248              Predicate pred) {
249   container.erase(std::remove_if(container.begin(), container.end(), pred),
250                   container.end());
251 }
252 
253 template <class T, class Allocator, class Value>
Erase(std::deque<T,Allocator> & container,const Value & value)254 void Erase(std::deque<T, Allocator>& container, const Value& value) {
255   container.erase(std::remove(container.begin(), container.end(), value),
256                   container.end());
257 }
258 
259 template <class T, class Allocator, class Predicate>
EraseIf(std::deque<T,Allocator> & container,Predicate pred)260 void EraseIf(std::deque<T, Allocator>& container, Predicate pred) {
261   container.erase(std::remove_if(container.begin(), container.end(), pred),
262                   container.end());
263 }
264 
265 template <class T, class Allocator, class Value>
Erase(std::vector<T,Allocator> & container,const Value & value)266 void Erase(std::vector<T, Allocator>& container, const Value& value) {
267   container.erase(std::remove(container.begin(), container.end(), value),
268                   container.end());
269 }
270 
271 template <class T, class Allocator, class Predicate>
EraseIf(std::vector<T,Allocator> & container,Predicate pred)272 void EraseIf(std::vector<T, Allocator>& container, Predicate pred) {
273   container.erase(std::remove_if(container.begin(), container.end(), pred),
274                   container.end());
275 }
276 
277 template <class T, class Allocator, class Value>
Erase(std::forward_list<T,Allocator> & container,const Value & value)278 void Erase(std::forward_list<T, Allocator>& container, const Value& value) {
279   // Unlike std::forward_list::remove, this function template accepts
280   // heterogeneous types and does not force a conversion to the container's
281   // value type before invoking the == operator.
282   container.remove_if([&](const T& cur) { return cur == value; });
283 }
284 
285 template <class T, class Allocator, class Predicate>
EraseIf(std::forward_list<T,Allocator> & container,Predicate pred)286 void EraseIf(std::forward_list<T, Allocator>& container, Predicate pred) {
287   container.remove_if(pred);
288 }
289 
290 template <class T, class Allocator, class Value>
Erase(std::list<T,Allocator> & container,const Value & value)291 void Erase(std::list<T, Allocator>& container, const Value& value) {
292   // Unlike std::list::remove, this function template accepts heterogeneous
293   // types and does not force a conversion to the container's value type before
294   // invoking the == operator.
295   container.remove_if([&](const T& cur) { return cur == value; });
296 }
297 
298 template <class T, class Allocator, class Predicate>
EraseIf(std::list<T,Allocator> & container,Predicate pred)299 void EraseIf(std::list<T, Allocator>& container, Predicate pred) {
300   container.remove_if(pred);
301 }
302 
303 template <class Key, class T, class Compare, class Allocator, class Predicate>
EraseIf(std::map<Key,T,Compare,Allocator> & container,Predicate pred)304 void EraseIf(std::map<Key, T, Compare, Allocator>& container, Predicate pred) {
305   internal::IterateAndEraseIf(container, pred);
306 }
307 
308 template <class Key, class T, class Compare, class Allocator, class Predicate>
EraseIf(std::multimap<Key,T,Compare,Allocator> & container,Predicate pred)309 void EraseIf(std::multimap<Key, T, Compare, Allocator>& container,
310              Predicate pred) {
311   internal::IterateAndEraseIf(container, pred);
312 }
313 
314 template <class Key, class Compare, class Allocator, class Predicate>
EraseIf(std::set<Key,Compare,Allocator> & container,Predicate pred)315 void EraseIf(std::set<Key, Compare, Allocator>& container, Predicate pred) {
316   internal::IterateAndEraseIf(container, pred);
317 }
318 
319 template <class Key, class Compare, class Allocator, class Predicate>
EraseIf(std::multiset<Key,Compare,Allocator> & container,Predicate pred)320 void EraseIf(std::multiset<Key, Compare, Allocator>& container,
321              Predicate pred) {
322   internal::IterateAndEraseIf(container, pred);
323 }
324 
325 template <class Key,
326           class T,
327           class Hash,
328           class KeyEqual,
329           class Allocator,
330           class Predicate>
EraseIf(std::unordered_map<Key,T,Hash,KeyEqual,Allocator> & container,Predicate pred)331 void EraseIf(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& container,
332              Predicate pred) {
333   internal::IterateAndEraseIf(container, pred);
334 }
335 
336 template <class Key,
337           class T,
338           class Hash,
339           class KeyEqual,
340           class Allocator,
341           class Predicate>
EraseIf(std::unordered_multimap<Key,T,Hash,KeyEqual,Allocator> & container,Predicate pred)342 void EraseIf(
343     std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& container,
344     Predicate pred) {
345   internal::IterateAndEraseIf(container, pred);
346 }
347 
348 template <class Key,
349           class Hash,
350           class KeyEqual,
351           class Allocator,
352           class Predicate>
EraseIf(std::unordered_set<Key,Hash,KeyEqual,Allocator> & container,Predicate pred)353 void EraseIf(std::unordered_set<Key, Hash, KeyEqual, Allocator>& container,
354              Predicate pred) {
355   internal::IterateAndEraseIf(container, pred);
356 }
357 
358 template <class Key,
359           class Hash,
360           class KeyEqual,
361           class Allocator,
362           class Predicate>
EraseIf(std::unordered_multiset<Key,Hash,KeyEqual,Allocator> & container,Predicate pred)363 void EraseIf(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& container,
364              Predicate pred) {
365   internal::IterateAndEraseIf(container, pred);
366 }
367 
368 // A helper class to be used as the predicate with |EraseIf| to implement
369 // in-place set intersection. Helps implement the algorithm of going through
370 // each container an element at a time, erasing elements from the first
371 // container if they aren't in the second container. Requires each container be
372 // sorted. Note that the logic below appears inverted since it is returning
373 // whether an element should be erased.
374 template <class Collection>
375 class IsNotIn {
376  public:
IsNotIn(const Collection & collection)377   explicit IsNotIn(const Collection& collection)
378       : i_(collection.begin()), end_(collection.end()) {}
379 
operator()380   bool operator()(const typename Collection::value_type& x) {
381     while (i_ != end_ && *i_ < x)
382       ++i_;
383     if (i_ == end_)
384       return true;
385     if (*i_ == x) {
386       ++i_;
387       return false;
388     }
389     return true;
390   }
391 
392  private:
393   typename Collection::const_iterator i_;
394   const typename Collection::const_iterator end_;
395 };
396 
397 // Helper for returning the optional value's address, or nullptr.
398 template <class T>
OptionalOrNullptr(base::Optional<T> & optional)399 T* OptionalOrNullptr(base::Optional<T>& optional) {
400   return optional.has_value() ? &optional.value() : nullptr;
401 }
402 
403 template <class T>
OptionalOrNullptr(const base::Optional<T> & optional)404 const T* OptionalOrNullptr(const base::Optional<T>& optional) {
405   return optional.has_value() ? &optional.value() : nullptr;
406 }
407 
408 }  // namespace base
409 
410 #endif  // BASE_STL_UTIL_H_
411