~popey/+junk/usd

« back to all changes in this revision

Viewing changes to USD/pxr/base/lib/tf/stl.h

  • Committer: Alan Pope
  • Date: 2016-09-29 12:05:28 UTC
  • Revision ID: alan@popey.com-20160929120528-32j3uk1x0dgaorip
Initial attempt to snap

Show diffs side-by-side

added added

removed removed

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