1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_STL_H
25 #define PXR_BASE_TF_STL_H
26 
27 /// \file tf/stl.h
28 /// \ingroup group_tf_Stl
29 
30 #include "pxr/pxr.h"
31 
32 #include "pxr/base/tf/api.h"
33 #include "pxr/base/tf/tf.h"
34 #include "pxr/base/tf/hashmap.h"
35 #include "pxr/base/tf/hashset.h"
36 #include "pxr/base/tf/iterator.h"
37 
38 #include <boost/call_traits.hpp>
39 
40 #include <algorithm>
41 #include <iterator>
42 #include <map>
43 #include <set>
44 #include <utility>
45 
46 PXR_NAMESPACE_OPEN_SCOPE
47 
48 // Helper for TfMapLookup().  Uses std::map API to get a value by key.
49 template <class T>
50 struct Tf_MapLookupHelper {
51     typedef T Container;
52 
53     template <class Key, class Result>
LookupTf_MapLookupHelper54     static bool Lookup(Container const& map, Key const &key, Result* valuePtr)
55     {
56         typename Container::const_iterator i = map.find(key);
57         if (i == map.end()) {
58             return false;
59         }
60         else {
61             *valuePtr = i->second;
62             return true;
63         }
64     }
65 };
66 
67 /// Checks if an item exists in a \c map or a \c TfHashMap.
68 ///
69 /// If \p key exists in \p map, then this function returns \c true and the
70 /// value indexed by \p key is copied into \p *valuePtr.  Otherwise,
71 /// \p *valuePtr is not modified, and \c false is returned.
72 ///
73 /// Example:
74 /// \code
75 ///    TfHashMap<string, int, TfHash> m = ...;
76 ///    int value;
77 ///
78 ///
79 ///    if (TfMapLookup(m, "someKey", &value))
80 ///        printf("Value found: %d\n", value);
81 ///    else
82 ///        printf("Value not found\n");
83 ///        ...
84 /// \endcode
85 ///
86 /// \ingroup group_tf_Stl
87 template <class Container, class Key, class Result>
TfMapLookup(Container const & map,Key const & key,Result * valuePtr)88 bool TfMapLookup(Container const &map, Key const &key, Result* valuePtr)
89 {
90     return Tf_MapLookupHelper<Container>::Lookup(map, key, valuePtr);
91 }
92 
93 /// Checks if an item exists in a \c map or a \c TfHashMap.
94 ///
95 /// If \p key exists in \p map, then this function returns the value index by
96 /// \p key.  Otherwise, \p defaultValue is returned.  Note that the result is
97 /// returned by value, so this is best used for types that are quick to copy.
98 ///
99 /// Example:
100 /// \code
101 ///    TfHashMap<string, int, TfHash> m;
102 ///    m["foo"] = 1;
103 ///
104 ///    int value = TfMapLookupByValue(m, "someKey", -1);
105 ///    TF_AXIOM(value == -1);
106 ///
107 ///    int value = TfMapLookupByValue(m, "foo", -1);
108 ///    TF_AXIOM(value == 1);
109 ///
110 /// \endcode
111 ///
112 /// \ingroup group_tf_Stl
113 template <class Container, class Key, class Result>
TfMapLookupByValue(Container const & map,Key const & key,const Result & defaultValue)114 const Result TfMapLookupByValue(Container const &map,
115                  Key const &key, const Result &defaultValue)
116 {
117     typename Container::const_iterator i = map.find(key);
118     if (i == map.end()) {
119         return defaultValue;
120     } else {
121         return i->second;
122     }
123 }
124 
125 /// Checks if an item exists in a \c map or \c TfHashMap, without copying it.
126 ///
127 /// If \p key exists in the \p map, then this function returns a pointer to
128 /// the value indexed by \p key.  Otherwise, NULL is returned.
129 ///
130 /// Example:
131 /// \code
132 ///    TfHashMap<string, BigData, TfHash> m = ...;
133 ///
134 ///    if (BigData* bigPtr = TfMapLookupPtr(m, "someKey"))
135 ///        bigPtr->ModifyStuff();
136 ///    else
137 ///        printf("Value not found\n");
138 /// \endcode
139 ///
140 /// \ingroup group_tf_Stl
141 template <class Container, class Key>
142 typename Container::mapped_type *
TfMapLookupPtr(Container & map,Key const & key)143 TfMapLookupPtr(Container &map, Key const &key)
144 {
145     typename Container::iterator i = map.find(key);
146     return (i != map.end()) ? &(i->second) : NULL;
147 }
148 
149 template <class Container, class Key>
150 typename Container::mapped_type const *
TfMapLookupPtr(Container const & map,Key const & key)151 TfMapLookupPtr(Container const &map, Key const &key)
152 {
153     typename Container::const_iterator i = map.find(key);
154     return (i != map.end()) ? &(i->second) : NULL;
155 }
156 
157 /// Return an \c std::pair in sorted order.
158 ///
159 /// This call is a useful helper for maps whose key is an unordered pair of
160 /// elements.  One can either define a new data type such that (a,b) is deemed
161 /// equivalent to (b,a), or more simply, adopt the convention that a key is
162 /// always written (a,b) with a < b.
163 ///
164 /// \ingroup group_tf_Stl
165 template <typename T>
166 inline std::pair<T,T>
TfOrderedPair(T a,T b)167 TfOrderedPair(T a, T b) {
168     return (a < b) ? std::pair<T,T>(a,b) : std::pair<T,T>(b,a);
169 }
170 
171 /// Reset \a obj to be an empty, space-optimized object.
172 ///
173 /// This can be used to clear c++ containers and reclaim their memory.  For
174 /// instance, std::vector::clear() will not reclaim any memory, even if the
175 /// vector previously had a large number of elements.  Often, this is what you
176 /// want because the vector is later filled again.  But sometimes you want to
177 /// reclaim the memory, and this function will do that.
178 ///
179 /// As another example, gcc's hash_map and hash_set do not clear their bucket
180 /// lists when they themselves are cleared.  This can lead to poor performance
181 /// due to ever-growing bucket lists for hashes that are repeatedly filled,
182 /// cleared, and filled again.  TfReset will avoid this by effectively
183 /// clearing the bucket list.
184 ///
185 /// This function requires that the expression T().swap(obj) where obj is of
186 /// type T& be valid.  This is true for many classes, including the standard
187 /// containers.
188 template <class T>
TfReset(T & obj)189 inline void TfReset(T &obj) {
190     T().swap(obj);
191 }
192 
193 TF_API size_t Tf_GetEmptyHashMapBucketCount();
194 
195 /// Specialize for TfHashMap to make minimally sized hashes.
196 template <class Key, class Value, class Hash, class Equal, class Alloc>
TfReset(TfHashMap<Key,Value,Hash,Equal,Alloc> & hash)197 inline void TfReset(TfHashMap<Key, Value, Hash, Equal, Alloc> &hash){
198     // If the implementation of the hash map allocates buckets when
199     // constructed asking for zero then only swap a constructed object
200     // if \p hash has more than that many buckets already, otherwise
201     // we just clear().  Note that this assumes that the number of
202     // buckets does not depend on the template parameter types which
203     // is reasonable.
204     static size_t emptyCount = Tf_GetEmptyHashMapBucketCount();
205 
206     if (hash.bucket_count() > emptyCount)
207         TfHashMap<Key, Value, Hash, Equal, Alloc>(0).swap(hash);
208     else if (!hash.empty())
209         hash.clear();
210 }
211 
212 TF_API size_t Tf_GetEmptyHashSetBucketCount();
213 
214 /// Specialize for TfHashSet to make minimally sized hashes.
215 template <class Value, class Hash, class Equal, class Alloc>
TfReset(TfHashSet<Value,Hash,Equal,Alloc> & hash)216 inline void TfReset(TfHashSet<Value, Hash, Equal, Alloc> &hash) {
217     static size_t emptyCount = Tf_GetEmptyHashSetBucketCount();
218 
219     // See comment above about issues with TfHashSet(0).
220     if (hash.bucket_count() > emptyCount)
221         TfHashSet<Value, Hash, Equal, Alloc>(0).swap(hash);
222     else if (!hash.empty())
223         hash.clear();
224 }
225 
226 
227 /// An unary function that represents the identity function; it takes a single
228 /// argument \a arg, and returns \a arg.
229 ///
230 /// This is similar to the sgi extension std::identity<T>.
231 template <class T>
232 inline typename boost::call_traits<T>::param_type
TfIdentity(typename boost::call_traits<T>::param_type arg)233 TfIdentity(typename boost::call_traits<T>::param_type arg) {
234     return arg;
235 }
236 
237 /// Produce a sequence consisting of the set difference of [\a first1, \a
238 /// last1) and [\a first2, \a last2), while maintaining the relative order of
239 /// the first sequence. No particular order is required for either range, but
240 /// elements must have a strict weak order provided by operator<.
241 ///
242 /// If an element appears multiple times in either the first or second
243 /// sequence, the number of occurrences in the output is the difference
244 /// between the first sequence and the second (or zero if there are more
245 /// occurrences in the second sequence).  For example, if the first sequence
246 /// is (1, 3, 3, 1) and the second sequence is (2, 3, 2), the result will be
247 /// (1, 3, 1).
248 template <class InputIterator1, class InputIterator2, class OutputIterator>
249 void
TfOrderedSetDifference(InputIterator1 first1,InputIterator1 last1,InputIterator2 first2,InputIterator2 last2,OutputIterator result)250 TfOrderedSetDifference(InputIterator1 first1, InputIterator1 last1,
251                        InputIterator2 first2, InputIterator2 last2,
252                        OutputIterator result)
253 {
254     typedef std::multiset<typename InputIterator2::value_type> SetType;
255     SetType set2(first2, last2);
256 
257     // Walk [first1, last1).  If the element is in set2, skip it, and remove one
258     // of those elements from set2, otherwise output it.
259     for (InputIterator1 i = first1; i != last1; ++i) {
260         typename SetType::iterator j = set2.find(*i);
261         if (j != set2.end())
262             set2.erase(j);
263         else
264             *result++ = *i;
265     }
266 }
267 
268 /// Produce a sequence consisting of the set difference of [\a first1, \a
269 /// last1) and [\a first2, \a last2), while maintaining the relative order of
270 /// the first sequence.  No particular order is required for either range, but
271 /// elements must have a strict weak order provided by operator<.
272 ///
273 /// If an element appears multiple times in either the first or second
274 /// sequence, the number of occurrences in the output is the difference
275 /// between the first sequence and the second (or zero if there are more
276 /// occurrences in the second sequence).  For example, if the first sequence
277 /// is (1, 3, 3, 1) and the second sequence is (2, 3, 2), the result will be
278 /// (1, 3, 1).
279 template <class BackInsertionSequence,
280           class InputIterator1, class InputIterator2>
281 BackInsertionSequence
TfOrderedSetDifferenceToContainer(InputIterator1 first1,InputIterator1 last1,InputIterator2 first2,InputIterator2 last2)282 TfOrderedSetDifferenceToContainer(InputIterator1 first1, InputIterator1 last1,
283                                   InputIterator2 first2, InputIterator2 last2)
284 {
285     BackInsertionSequence result;
286     TfOrderedSetDifference(first1, last1, first2, last2,
287                            std::back_inserter(result));
288     return result;
289 }
290 
291 /// Produce a sequence consisting of the set difference of the unique elements
292 /// in [\a first1, \a last1) and [\a first2, \a last2), while maintaining the
293 /// relative order of the first sequence.  No particular order is required for
294 /// either range, but elements must have a strict weak order provided by
295 /// operator<.
296 ///
297 /// If an element appears multiple times in the first sequence, it appears
298 /// either zero or one times in the output.  It appears zero times if it
299 /// appears in the second sequence, and one time if it does not.  For example,
300 /// if the first sequence is (1, 3, 3, 1) and the second sequence is (2, 3,
301 /// 2), the result will be (1).
302 template <class InputIterator1, class InputIterator2, class OutputIterator>
303 void
TfOrderedUniquingSetDifference(InputIterator1 first1,InputIterator1 last1,InputIterator2 first2,InputIterator2 last2,OutputIterator result)304 TfOrderedUniquingSetDifference(InputIterator1 first1, InputIterator1 last1,
305                                InputIterator2 first2, InputIterator2 last2,
306                                OutputIterator result)
307 {
308     typedef std::set<typename InputIterator1::value_type> Set1Type;
309     typedef std::set<typename InputIterator2::value_type> Set2Type;
310 
311     Set1Type set1;
312     Set2Type set2(first2, last2);
313 
314     // Walk [first1, last1).  If the element is in set1, skip it.  Else insert
315     // it into set1, and if the element is not in set2, output it.
316     for (InputIterator1 i = first1; i != last1; ++i)
317         if (set1.insert(*i).second && !set2.count(*i))
318             *result++ = *i;
319 }
320 
321 /// Produce a sequence consisting of the set difference of the unique elements
322 /// in [\a first1, \a last1) and [\a first2, \a last2), while maintaining the
323 /// relative order of the first sequence.  No particular order is required for
324 /// either range, but elements must have a strict weak order provided by
325 /// operator<.
326 ///
327 /// If an element appears multiple times in the first sequence, it appears
328 /// either zero or one times in the output.  It appears zero times if it
329 /// appears in the second sequence, and one time if it does not.  For example,
330 /// if the first sequence is (1, 3, 3, 1) and the second sequence is (2, 3,
331 /// 2), the result will be (1).
332 template <class BackInsertionSequence,
333           class InputIterator1, class InputIterator2>
334 BackInsertionSequence
TfOrderedUniquingSetDifferenceToContainer(InputIterator1 first1,InputIterator1 last1,InputIterator2 first2,InputIterator2 last2)335 TfOrderedUniquingSetDifferenceToContainer(InputIterator1 first1,
336                                           InputIterator1 last1,
337                                           InputIterator2 first2,
338                                           InputIterator2 last2)
339 {
340     BackInsertionSequence result;
341     TfOrderedUniquingSetDifference(first1, last1, first2, last2,
342                                    std::back_inserter(result));
343     return result;
344 }
345 
346 /// A version of binary search that finds the boundary in a partitioned
347 /// sequence.  The Predicate pred should return true for objects on the
348 /// 'first' side (or left-hand) side of the boundary.
349 ///
350 /// More precisely, given a range [first, last) and a Predicate pred for which
351 /// there is exactly one iterator called mid in that range such that pred(x)
352 /// is true for every x in [first, mid) and false for every x in [mid, last),
353 /// return mid.
354 template <class ForwardIterator, class Predicate>
355 static inline ForwardIterator
TfFindBoundary(ForwardIterator first,ForwardIterator last,Predicate const & pred)356 TfFindBoundary(ForwardIterator first, ForwardIterator last,
357                Predicate const &pred)
358 {
359     size_t len = std::distance(first, last);
360     size_t half;
361     ForwardIterator middle;
362 
363     while (len > 0) {
364         half = len >> 1;
365         middle = first;
366         std::advance(middle, half);
367         if (pred(*middle)) {
368             first = middle;
369             ++first;
370             len = len - half - 1;
371         }
372         else
373             len = half;
374     }
375     return first;
376 }
377 
378 /// Function object for retrieving the N'th element of a std::pair
379 /// or std::tuple. This is similar to std::get<N>, but wrapped up in a
380 /// function object suitable for use with STL algorithms.
381 ///
382 /// Example:
383 /// \code
384 ///    const std::vector<std::pair<int, std::string>> pairs = { ... }
385 ///    std::vector<int> intsOnly(pairs.size());
386 ///    std::transform(pairs.begin(), pairs.end(), intsOnly.begin(), TfGet<0>());
387 /// \endcode
388 ///
389 /// \ingroup group_tf_Stl
390 template <size_t N>
391 class TfGet
392 {
393 public:
394     template <class PairOrTuple>
395     using return_type = typename std::tuple_element<N, PairOrTuple>::type;
396 
397     template <class PairOrTuple>
operator()398     constexpr return_type<PairOrTuple>& operator()(PairOrTuple& p) const
399     {
400         return std::get<N>(p);
401     }
402 
403     template <class PairOrTuple>
operator()404     constexpr const return_type<PairOrTuple>& operator()(
405         const PairOrTuple& p) const
406     {
407         return std::get<N>(p);
408     }
409 
410     template <class PairOrTuple>
operator()411     constexpr return_type<PairOrTuple>&& operator()(PairOrTuple&& p) const
412     {
413         return std::get<N>(std::move(p));
414     }
415 };
416 
417 PXR_NAMESPACE_CLOSE_SCOPE
418 
419 #endif // PXR_BASE_TF_STL_H
420