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 
25 #include "pxr/pxr.h"
26 #include "pxr/usd/sdf/path.h"
27 #include "pxr/usd/sdf/tokens.h"
28 #include "pxr/usd/sdf/instantiatePool.h"
29 #include "pxr/base/tf/bitUtils.h"
30 #include "pxr/base/tf/diagnostic.h"
31 #include "pxr/base/tf/hash.h"
32 #include "pxr/base/tf/iterator.h"
33 #include "pxr/base/tf/mallocTag.h"
34 #include "pxr/base/tf/staticData.h"
35 #include "pxr/base/tf/stl.h"
36 
37 #include "pxr/base/trace/trace.h"
38 
39 #include <boost/functional/hash.hpp>
40 #include <tbb/concurrent_hash_map.h>
41 #include <tbb/spin_mutex.h>
42 
43 #include <atomic>
44 #include <memory>
45 #include <utility>
46 #include <vector>
47 
48 using std::string;
49 using std::vector;
50 
51 PXR_NAMESPACE_OPEN_SCOPE
52 
53 SDF_INSTANTIATE_POOL(Sdf_PathPrimTag, Sdf_SizeofPrimPathNode, /*regionBits=*/8);
54 SDF_INSTANTIATE_POOL(Sdf_PathPropTag, Sdf_SizeofPropPathNode, /*regionBits=*/8);
55 
56 // Size of path nodes is important, so we want the compiler to tell us if it
57 // changes.
58 static_assert(sizeof(Sdf_PrimPathNode) == 3 * sizeof(void *), "");
59 static_assert(sizeof(Sdf_PrimPropertyPathNode) == 3 * sizeof(void *), "");
60 
61 struct Sdf_PathNodePrivateAccess
62 {
63     template <class Handle>
64     static inline tbb::atomic<unsigned int> &
GetRefCountSdf_PathNodePrivateAccess65     GetRefCount(Handle h) {
66         Sdf_PathNode const *p =
67             reinterpret_cast<Sdf_PathNode const *>(h.GetPtr());
68         return p->_refCount;
69     }
70 
71     template <class T, class Pool, class ... Args>
72     static inline typename Pool::Handle
NewSdf_PathNodePrivateAccess73     New(Sdf_PathNode const *parent, Args const & ... args) {
74         typename Pool::Handle h = Pool::Allocate();
75         char *p = h.GetPtr();
76         T *tp = reinterpret_cast<T *>(p);
77         new (tp) T(parent, args...);
78         return h;
79     }
80 };
81 
82 typedef Sdf_PathNodePrivateAccess Access;
83 
84 namespace {
85 
86 template <class T>
87 struct _ParentAnd { const Sdf_PathNode *parent; T value; };
88 
89 // Allow void for 'expression' path case, which has no additional data.
90 template <> struct _ParentAnd<void> { const Sdf_PathNode *parent; };
91 
92 template <class T>
93 inline _ParentAnd<T>
_MakeParentAnd(const Sdf_PathNode * parent,const T & value)94 _MakeParentAnd(const Sdf_PathNode *parent, const T &value) {
95     _ParentAnd<T> ret;
96     ret.parent = parent;
97     ret.value = value;
98     return ret;
99 }
100 
101 inline _ParentAnd<void>
_MakeParentAnd(const Sdf_PathNode * parent)102 _MakeParentAnd(const Sdf_PathNode *parent) {
103     _ParentAnd<void> ret;
104     ret.parent = parent;
105     return ret;
106 }
107 
108 inline size_t
hash_value(const Sdf_PathNode * p)109 hash_value(const Sdf_PathNode *p)
110 {
111     return TfHash()(p);
112 }
113 
114 template <class T>
115 struct _HashParentAnd
116 {
equal__anonb932038f0111::_HashParentAnd117     inline bool equal(const T &l, const T &r) const {
118         return l.parent == r.parent && l.value == r.value;
119     }
120 
hash__anonb932038f0111::_HashParentAnd121     inline size_t hash(const T &t) const {
122         size_t h = reinterpret_cast<uintptr_t>(t.parent) >> 4;
123         boost::hash_combine(h, t.value);
124         return h;
125     }
126  };
127 
128 template <>
129 struct _HashParentAnd<_ParentAnd<void> >
130 {
equal__anonb932038f0111::_HashParentAnd131     inline bool equal(const _ParentAnd<void> &l,
132                       const _ParentAnd<void> &r) const {
133         return l.parent == r.parent;
134     }
135 
hash__anonb932038f0111::_HashParentAnd136     inline size_t hash(const _ParentAnd<void> &t) const {
137         return reinterpret_cast<uintptr_t>(t.parent) >> 4;
138     }
139 };
140 
141 template <class T>
142 struct _PrimTable {
143     using Pool = Sdf_PathPrimPartPool;
144     using PoolHandle = Sdf_PathPrimHandle;
145     using NodeHandle = Sdf_PathPrimNodeHandle;
146     using Type = tbb::concurrent_hash_map<
147         _ParentAnd<T>, PoolHandle, _HashParentAnd<_ParentAnd<T> > >;
148 
149     Type map;
150 };
151 
152 template <class T>
153 struct _PropTable {
154     using Pool = Sdf_PathPropPartPool;
155     using PoolHandle = Sdf_PathPropHandle;
156     using NodeHandle = Sdf_PathPropNodeHandle;
157     using Type = tbb::concurrent_hash_map<
158         _ParentAnd<T>, PoolHandle, _HashParentAnd<_ParentAnd<T> > >;
159 
160     Type map;
161 };
162 
163 using _PrimTokenTable = _PrimTable<TfToken>;
164 using _PropTokenTable = _PropTable<TfToken>;
165 using _PrimVarSelTable = _PrimTable<Sdf_PathNode::VariantSelectionType>;
166 using _PropTargetTable = _PropTable<SdfPath>;
167 using _PropVoidTable = _PropTable<void>;
168 
169 template <class PathNode, class Table, class ... Args>
170 inline typename Table::NodeHandle
_FindOrCreate(Table & table,const Sdf_PathNode * parent,const Args &...args)171 _FindOrCreate(Table &table,
172               const Sdf_PathNode *parent,
173               const Args & ... args)
174 {
175     typename Table::Type::accessor accessor;
176     if (table.map.insert(accessor, _MakeParentAnd(parent, args...)) ||
177         Access::GetRefCount(accessor->second).fetch_and_increment() == 0) {
178         // Either there was no entry in the table, or there was but it had begun
179         // dying (another client dropped its refcount to 0).  We have to create
180         // a new entry in the table.  When the client that is killing the other
181         // node it looks for itself in the table, it will either not find itself
182         // or will find a different node and so won't remove it.
183         typename Table::PoolHandle newNode =
184             Access::New<PathNode, typename Table::Pool>(parent, args...);
185         accessor->second = newNode;
186     }
187     return typename Table::NodeHandle(accessor->second, /* add_ref = */ false);
188 }
189 
190 template <class Table, class ... Args>
191 inline void
_Remove(const Sdf_PathNode * pathNode,Table & table,const Sdf_PathNodeConstRefPtr & parent,const Args &...args)192 _Remove(const Sdf_PathNode *pathNode,
193         Table &table, const Sdf_PathNodeConstRefPtr &parent,
194         const Args & ... args)
195 {
196     // If there's an entry for this key that has pathNode, erase it.  Even if
197     // there's an entry present it may not be pathNode, since another node may
198     // have been created since we decremented our refcount and started being
199     // destroyed.  If it is this node, we remove it.
200     typename Table::Type::accessor accessor;
201     if (table.map.find(accessor, _MakeParentAnd(parent.get(), args...)) &&
202         accessor->second.GetPtr() == reinterpret_cast<char const *>(pathNode)) {
203         table.map.erase(accessor);
204     }
205 }
206 
207 } // anon
208 
209 
210 void
operator delete(void * p)211 Sdf_PrimPartPathNode::operator delete(void *p)
212 {
213     using PoolHandle = Sdf_PathPrimPartPool::Handle;
214     Sdf_PathPrimPartPool::Free(
215         PoolHandle::GetHandle(reinterpret_cast<char *>(p)));
216 }
217 
218 void
operator delete(void * p)219 Sdf_PropPartPathNode::operator delete(void *p)
220 {
221     using PoolHandle = Sdf_PathPropPartPool::Handle;
222     Sdf_PathPropPartPool::Free(
223         PoolHandle::GetHandle(reinterpret_cast<char *>(p)));
224 }
225 
226 // Preallocate some space in the prim and prim property tables.
TF_MAKE_STATIC_DATA(_PropTargetTable,_mapperNodes)227 TF_MAKE_STATIC_DATA(_PropTargetTable, _mapperNodes) {}
TF_MAKE_STATIC_DATA(_PropTargetTable,_targetNodes)228 TF_MAKE_STATIC_DATA(_PropTargetTable, _targetNodes) {}
TF_MAKE_STATIC_DATA(_PropTokenTable,_mapperArgNodes)229 TF_MAKE_STATIC_DATA(_PropTokenTable, _mapperArgNodes) {}
TF_MAKE_STATIC_DATA(_PrimTokenTable,_primNodes)230 TF_MAKE_STATIC_DATA(_PrimTokenTable, _primNodes) {
231     _primNodes->map.rehash(32768); }
TF_MAKE_STATIC_DATA(_PropTokenTable,_primPropertyNodes)232 TF_MAKE_STATIC_DATA(_PropTokenTable, _primPropertyNodes) {
233     _primPropertyNodes->map.rehash(32768); }
TF_MAKE_STATIC_DATA(_PropTokenTable,_relAttrNodes)234 TF_MAKE_STATIC_DATA(_PropTokenTable, _relAttrNodes) {}
TF_MAKE_STATIC_DATA(_PrimVarSelTable,_primVarSelNodes)235 TF_MAKE_STATIC_DATA(_PrimVarSelTable, _primVarSelNodes) {}
TF_MAKE_STATIC_DATA(_PropVoidTable,_expressionNodes)236 TF_MAKE_STATIC_DATA(_PropVoidTable, _expressionNodes) {}
237 
TF_MAKE_STATIC_DATA(Sdf_PathNode const *,_absoluteRootNode)238 TF_MAKE_STATIC_DATA(Sdf_PathNode const *, _absoluteRootNode) {
239     *_absoluteRootNode = Sdf_RootPathNode::New(true);
240     TF_AXIOM((*_absoluteRootNode)->GetCurrentRefCount() == 1);
241 }
TF_MAKE_STATIC_DATA(Sdf_PathNode const *,_relativeRootNode)242 TF_MAKE_STATIC_DATA(Sdf_PathNode const *, _relativeRootNode) {
243     *_relativeRootNode = Sdf_RootPathNode::New(false);
244     TF_AXIOM((*_relativeRootNode)->GetCurrentRefCount() == 1);
245 }
246 
247 Sdf_PathNode const *
New(bool isAbsolute)248 Sdf_RootPathNode::New(bool isAbsolute)
249 {
250     Sdf_PathPrimPartPool::Handle h = Sdf_PathPrimPartPool::Allocate();
251     char *p = h.GetPtr();
252     Sdf_RootPathNode *tp = reinterpret_cast<Sdf_RootPathNode *>(p);
253     new (tp) Sdf_RootPathNode(isAbsolute);
254     return tp;
255 }
256 
257 Sdf_PathNode const *
GetAbsoluteRootNode()258 Sdf_PathNode::GetAbsoluteRootNode() {
259     return *_absoluteRootNode;
260 }
261 
262 Sdf_PathNode const *
GetRelativeRootNode()263 Sdf_PathNode::GetRelativeRootNode() {
264     return *_relativeRootNode;
265 }
266 
267 Sdf_PathPrimNodeHandle
FindOrCreatePrim(Sdf_PathNode const * parent,const TfToken & name)268 Sdf_PathNode::FindOrCreatePrim(Sdf_PathNode const *parent,
269                                const TfToken &name)
270 {
271     return _FindOrCreate<Sdf_PrimPathNode>(*_primNodes, parent, name);
272 }
273 
274 Sdf_PathPropNodeHandle
FindOrCreatePrimProperty(Sdf_PathNode const * parent,const TfToken & name)275 Sdf_PathNode::FindOrCreatePrimProperty(Sdf_PathNode const *parent,
276                                        const TfToken &name)
277 {
278     // NOTE!  We explicitly set the parent to null here in order to create a
279     // separate prefix tree for property-like paths.
280 
281     return _FindOrCreate<Sdf_PrimPropertyPathNode>(
282         *_primPropertyNodes, nullptr, name);
283 }
284 
285 Sdf_PathPrimNodeHandle
FindOrCreatePrimVariantSelection(Sdf_PathNode const * parent,const TfToken & variantSet,const TfToken & variant)286 Sdf_PathNode::FindOrCreatePrimVariantSelection(
287     Sdf_PathNode const *parent,
288     const TfToken &variantSet,
289     const TfToken &variant)
290 {
291     return _FindOrCreate<Sdf_PrimVariantSelectionNode>(
292         *_primVarSelNodes, parent, VariantSelectionType(variantSet, variant));
293 }
294 
295 Sdf_PathPropNodeHandle
FindOrCreateTarget(Sdf_PathNode const * parent,SdfPath const & targetPath)296 Sdf_PathNode::FindOrCreateTarget(Sdf_PathNode const *parent,
297                                  SdfPath const &targetPath)
298 {
299     return _FindOrCreate<Sdf_TargetPathNode>(*_targetNodes, parent, targetPath);
300 }
301 
302 Sdf_PathPropNodeHandle
303 Sdf_PathNode
FindOrCreateRelationalAttribute(Sdf_PathNode const * parent,const TfToken & name)304 ::FindOrCreateRelationalAttribute(Sdf_PathNode const *parent,
305                                   const TfToken &name)
306 {
307     return _FindOrCreate<Sdf_RelationalAttributePathNode>(
308         *_relAttrNodes, parent, name);
309 }
310 
311 Sdf_PathPropNodeHandle
312 Sdf_PathNode
FindOrCreateMapper(Sdf_PathNode const * parent,SdfPath const & targetPath)313 ::FindOrCreateMapper(Sdf_PathNode const *parent,
314                      SdfPath const &targetPath)
315 {
316     return _FindOrCreate<Sdf_MapperPathNode>(*_mapperNodes, parent, targetPath);
317 }
318 
319 Sdf_PathPropNodeHandle
FindOrCreateMapperArg(Sdf_PathNode const * parent,const TfToken & name)320 Sdf_PathNode::FindOrCreateMapperArg(Sdf_PathNode const *parent,
321                                     const TfToken &name)
322 {
323     return _FindOrCreate<Sdf_MapperArgPathNode>(*_mapperArgNodes, parent, name);
324 }
325 
326 Sdf_PathPropNodeHandle
FindOrCreateExpression(Sdf_PathNode const * parent)327 Sdf_PathNode::FindOrCreateExpression(Sdf_PathNode const *parent)
328 {
329     return _FindOrCreate<Sdf_ExpressionPathNode>(*_expressionNodes, parent);
330 }
331 
Sdf_PathNode(bool isAbsolute)332 Sdf_PathNode::Sdf_PathNode(bool isAbsolute) :
333     _refCount(1),
334     _elementCount(0),
335     _nodeType(RootNode),
336     _isAbsolute(isAbsolute),
337     _containsPrimVariantSelection(false),
338     _containsTargetPath(false),
339     _hasToken(false)
340 {
341 }
342 
343 const Sdf_PathNode::VariantSelectionType &
_GetEmptyVariantSelection() const344 Sdf_PathNode::_GetEmptyVariantSelection() const {
345     static VariantSelectionType _emptyVariantSelection;
346     return _emptyVariantSelection;
347 }
348 
349 namespace {
350 // This "table" is a thread-safe mapping from property node to path token.  Each
351 // entry in the _pathTokenTable points to one of these, and will have an entry
352 // for the path string for the prim path itself (which will be keyed with a
353 // nullptr property node pointer) plus all the properties that hang off it.
354 struct _PropToTokenTable
355 {
356     // Note that this function returns a TfToken lvalue reference that needs to
357     // live/be valid as long as this object is around.
358     template <class Fn>
359     TfToken const &
FindOrCreate__anonb932038f0211::_PropToTokenTable360     FindOrCreate(Sdf_PathNode const *prop, Fn &&makeToken) {
361         _Data &d = *_data;
362         // We try first without creating the token -- if that fails we try
363         // again.  This could be made more efficient, but getting strings for
364         // paths shouldn't be a bottleneck for clients.
365         tbb::spin_mutex::scoped_lock lock(d.mutex);
366         auto iter = d.propsToToks.find(prop);
367         if (iter == d.propsToToks.end()) {
368             // No entry yet.  Drop the lock, make the token, and try to insert
369             // it.  We *must* drop the lock since creating the token can
370             // re-enter here (e.g. if there are embedded target paths that have
371             // properties on the same prim).
372             lock.release();
373             TfToken tok = std::forward<Fn>(makeToken)();
374             lock.acquire(d.mutex);
375             // This may or may not actually insert the token, depending on
376             // whether or not a concurrent caller did, but it doesn't really
377             // matter.
378             iter = d.propsToToks.emplace(prop, std::move(tok)).first;
379         }
380         return iter->second;
381     }
382 
383 private:
384     struct _Data {
385         std::map<Sdf_PathNode const *, TfToken> propsToToks;
386         tbb::spin_mutex mutex;
387     };
388 
389     std::shared_ptr<_Data> _data { new _Data };
390 };
391 
392 } // anon
393 
394 using _PrimToPropTokenTables =
395     tbb::concurrent_hash_map<Sdf_PathNode const *, _PropToTokenTable>;
396 
397 static TfStaticData<_PrimToPropTokenTables> _pathTokenTable;
398 
399 const TfToken &
GetPathToken(Sdf_PathNode const * primPart,Sdf_PathNode const * propPart)400 Sdf_PathNode::GetPathToken(Sdf_PathNode const *primPart,
401                            Sdf_PathNode const *propPart)
402 {
403     // Set the cache bit.  We only ever read this during the dtor, and that has
404     // to be exclusive to all other execution.
405     primPart->_hasToken = true;
406 
407     // Attempt to insert.
408     TfAutoMallocTag2 tag("Sdf", "SdfPath");
409     TfAutoMallocTag tag2("Sdf_PathNode::GetPathToken");
410 
411     _PrimToPropTokenTables::accessor primAccessor;
412     _pathTokenTable->insert(
413         primAccessor, std::make_pair(primPart, _PropToTokenTable()));
414     auto &propToTokenTable = primAccessor->second;
415     // Release the primAccessor here, since the call to _CreatePathToken below
416     // can cause reentry here (for embedded target paths).
417     primAccessor.release();
418 
419     return propToTokenTable.FindOrCreate(
420         propPart, [primPart, propPart]() {
421             return Sdf_PathNode::_CreatePathToken(primPart, propPart);
422         });
423 }
424 
425 TfToken
GetPathAsToken(Sdf_PathNode const * primPart,Sdf_PathNode const * propPart)426 Sdf_PathNode::GetPathAsToken(Sdf_PathNode const *primPart,
427                              Sdf_PathNode const *propPart)
428 {
429     return _CreatePathToken(primPart, propPart);
430 }
431 
432 TfToken
_CreatePathToken(Sdf_PathNode const * primPart,Sdf_PathNode const * propPart)433 Sdf_PathNode::_CreatePathToken(Sdf_PathNode const *primPart,
434                                Sdf_PathNode const *propPart)
435 {
436     TRACE_FUNCTION();
437 
438     if (primPart == GetRelativeRootNode() && !propPart) {
439         return SdfPathTokens->relativeRoot;
440     }
441 
442     Sdf_PathNode const * const root = (primPart->IsAbsolutePath() ?
443                                        Sdf_PathNode::GetAbsoluteRootNode() :
444                                        Sdf_PathNode::GetRelativeRootNode());
445 
446     std::vector<const Sdf_PathNode *> nodes;
447     nodes.reserve(primPart->GetElementCount() +
448                   (propPart ? propPart->GetElementCount() : 0));
449 
450     Sdf_PathNode const *curNode = propPart;
451     while (curNode) {
452         nodes.push_back(curNode);
453         curNode = curNode->GetParentNode();
454     }
455     curNode = primPart;
456     while (curNode && (curNode != root)) {
457         nodes.push_back(curNode);
458         curNode = curNode->GetParentNode();
459     }
460 
461     std::string str;
462     if (primPart->IsAbsolutePath()) {
463         // Put the leading / on absolute
464         str.append(SdfPathTokens->absoluteIndicator.GetString());
465     }
466 
467     TfToken prevElem;
468     Sdf_PathNode::NodeType prevNodeType = Sdf_PathNode::NumNodeTypes;
469     TF_REVERSE_FOR_ALL(i, nodes) {
470         const Sdf_PathNode * const node = *i;
471         Sdf_PathNode::NodeType curNodeType = node->GetNodeType();
472         if (prevNodeType == Sdf_PathNode::PrimNode &&
473             (curNodeType == Sdf_PathNode::PrimNode ||
474              // This covers cases like '../.property'
475              prevElem == SdfPathTokens->parentPathElement)) {
476             str.append(SdfPathTokens->childDelimiter.GetString());
477         }
478         TfToken elem = node->GetElement();
479         str.append(elem.GetString());
480         prevElem.Swap(elem);
481         prevNodeType = curNodeType;
482     }
483 
484     return TfToken(str);
485 }
486 
487 void
_RemovePathTokenFromTable() const488 Sdf_PathNode::_RemovePathTokenFromTable() const
489 {
490     _pathTokenTable->erase(this);
491 }
492 
493 // Returns true if \p identifier has at least one namespace delimiter.
494 static inline bool
_HasNamespaceDelimiter(const std::string & identifier)495 _HasNamespaceDelimiter(const std::string& identifier)
496 {
497     return identifier.find(SdfPathTokens->namespaceDelimiter.GetString()[0]) !=
498                                                         std::string::npos;
499 }
500 
501 bool
_IsNamespacedImpl() const502 Sdf_PathNode::_IsNamespacedImpl() const
503 {
504     return _HasNamespaceDelimiter(GetName().GetString());
505 }
506 
507 void
AppendText(std::string * str) const508 Sdf_PathNode::AppendText(std::string *str) const
509 {
510     switch (_nodeType) {
511     case RootNode:
512         return;
513     case PrimNode:
514         str->append(_Downcast<Sdf_PrimPathNode>()->_name.GetString());
515         return;
516     case PrimPropertyNode:
517         str->append(SdfPathTokens->propertyDelimiter.GetString());
518         str->append(_Downcast<Sdf_PrimPropertyPathNode>()->_name.GetString());
519         return;
520     case PrimVariantSelectionNode:
521         _Downcast<Sdf_PrimVariantSelectionNode>()->_AppendText(str);
522         return;
523     case TargetNode:
524         _Downcast<Sdf_TargetPathNode>()->_AppendText(str);
525         return;
526     case RelationalAttributeNode:
527         str->append(SdfPathTokens->propertyDelimiter.GetString());
528         str->append(_Downcast<Sdf_RelationalAttributePathNode>()->
529                     _name.GetString());
530         return;
531     case MapperNode:
532         _Downcast<Sdf_MapperPathNode>()->_AppendText(str);
533         return;
534     case MapperArgNode:
535         _Downcast<Sdf_MapperArgPathNode>()->_AppendText(str);
536         return;
537     case ExpressionNode:
538         _Downcast<Sdf_ExpressionPathNode>()->_AppendText(str);
539         return;
540     default:
541         return;
542     };
543 }
544 
~Sdf_PrimPathNode()545 Sdf_PrimPathNode::~Sdf_PrimPathNode() {
546     _Remove(this, *_primNodes, GetParentNode(), _name);
547 }
548 
~Sdf_PrimPropertyPathNode()549 Sdf_PrimPropertyPathNode::~Sdf_PrimPropertyPathNode() {
550     _Remove(this, *_primPropertyNodes, GetParentNode(), _name);
551 }
552 
553 const TfToken &
_GetNameImpl() const554 Sdf_PrimVariantSelectionNode::_GetNameImpl() const
555 {
556     return _variantSelection->second.IsEmpty()
557             ? _variantSelection->first
558             : _variantSelection->second;
559 }
560 
561 void
_AppendText(std::string * str) const562 Sdf_PrimVariantSelectionNode::_AppendText(std::string *str) const
563 {
564     std::string const &vset = _variantSelection->first.GetString();
565     std::string const &vsel = _variantSelection->second.GetString();
566     str->reserve(str->size() + vset.size() + vsel.size() + 3);
567     str->push_back('{');
568     str->append(vset);
569     str->push_back('=');
570     str->append(vsel);
571     str->push_back('}');
572 }
573 
~Sdf_PrimVariantSelectionNode()574 Sdf_PrimVariantSelectionNode::~Sdf_PrimVariantSelectionNode() {
575     _Remove(this, *_primVarSelNodes, GetParentNode(), *_variantSelection);
576 }
577 
578 void
_AppendText(std::string * str) const579 Sdf_TargetPathNode::_AppendText(std::string *str) const {
580     std::string const &open =
581         SdfPathTokens->relationshipTargetStart.GetString();
582     std::string const &target = _targetPath.GetString();
583     std::string const &close =
584         SdfPathTokens->relationshipTargetEnd.GetString();
585     str->reserve(str->size() + open.size() + target.size() + close.size());
586     str->append(open);
587     str->append(target);
588     str->append(close);
589 }
590 
~Sdf_TargetPathNode()591 Sdf_TargetPathNode::~Sdf_TargetPathNode() {
592     _Remove(this, *_targetNodes, GetParentNode(), _targetPath);
593 }
594 
~Sdf_RelationalAttributePathNode()595 Sdf_RelationalAttributePathNode::~Sdf_RelationalAttributePathNode() {
596     _Remove(this, *_relAttrNodes, GetParentNode(), _name);
597 }
598 
599 void
_AppendText(std::string * str) const600 Sdf_MapperPathNode::_AppendText(std::string *str) const {
601     std::string const &delim = SdfPathTokens->propertyDelimiter.GetString();
602     std::string const &mapperIndicator =
603         SdfPathTokens->mapperIndicator.GetString();
604     std::string const &open =
605         SdfPathTokens->relationshipTargetStart.GetString();
606     std::string const &target = _targetPath.GetString();
607     std::string const &close = SdfPathTokens->relationshipTargetEnd.GetString();
608     str->reserve(str->size() + delim.size() + mapperIndicator.size() +
609                  open.size() + target.size() + close.size());
610     str->append(delim);
611     str->append(mapperIndicator);
612     str->append(open);
613     str->append(target);
614     str->append(close);
615 }
616 
~Sdf_MapperPathNode()617 Sdf_MapperPathNode::~Sdf_MapperPathNode() {
618     _Remove(this, *_mapperNodes, GetParentNode(), _targetPath);
619 }
620 
621 void
_AppendText(std::string * str) const622 Sdf_MapperArgPathNode::_AppendText(std::string *str) const {
623     std::string const &delim = SdfPathTokens->propertyDelimiter.GetString();
624     std::string const &name = _name.GetString();
625     str->reserve(str->size() + delim.size() + name.size());
626     str->append(delim);
627     str->append(name);
628 }
629 
~Sdf_MapperArgPathNode()630 Sdf_MapperArgPathNode::~Sdf_MapperArgPathNode() {
631     _Remove(this, *_mapperArgNodes, GetParentNode(), _name);
632 }
633 
634 void
_AppendText(std::string * str) const635 Sdf_ExpressionPathNode::_AppendText(std::string *str) const {
636     std::string const &delim = SdfPathTokens->propertyDelimiter.GetString();
637     std::string const &expr = SdfPathTokens->expressionIndicator.GetString();
638     str->reserve(str->size() + delim.size() + expr.size());
639     str->append(delim);
640     str->append(expr);
641 }
642 
~Sdf_ExpressionPathNode()643 Sdf_ExpressionPathNode::~Sdf_ExpressionPathNode() {
644     _Remove(this, *_expressionNodes, GetParentNode());
645 }
646 
647 struct Sdf_Stats {
648     // Counts
649     int numNodes;
650     int numNodeRefs;
651 
652     // Histograms
653     vector<int> lengthTable;
654     vector<int> numChildrenTable;
655     size_t typeTable[Sdf_PathNode::NumNodeTypes];
656 };
657 
658 template <class Table>
659 static void
_GatherChildrenFrom(Sdf_PathNode const * parent,Table const & table,vector<Sdf_PathNodeConstRefPtr> * result)660 _GatherChildrenFrom(Sdf_PathNode const *parent,
661                     Table const &table,
662                     vector<Sdf_PathNodeConstRefPtr> *result)
663 {
664     TF_FOR_ALL(i, table.map) {
665         if (i->first.parent == parent)
666             result->emplace_back(
667                 reinterpret_cast<Sdf_PathNode const *>(i->second.GetPtr()));
668     }
669 }
670 
671 static vector<Sdf_PathNodeConstRefPtr>
_GetChildren(Sdf_PathNode const * pathNode)672 _GetChildren(Sdf_PathNode const *pathNode)
673 {
674     // XXX: SLOW.  For path stats debugging only.
675     vector<Sdf_PathNodeConstRefPtr> children;
676     _GatherChildrenFrom(pathNode, *_mapperNodes, &children);
677     _GatherChildrenFrom(pathNode, *_targetNodes, &children);
678     _GatherChildrenFrom(pathNode, *_mapperArgNodes, &children);
679     _GatherChildrenFrom(pathNode, *_primNodes, &children);
680     _GatherChildrenFrom(pathNode, *_primPropertyNodes, &children);
681     _GatherChildrenFrom(pathNode, *_relAttrNodes, &children);
682     _GatherChildrenFrom(pathNode, *_primVarSelNodes, &children);
683     _GatherChildrenFrom(pathNode, *_expressionNodes, &children);
684     return children;
685 }
686 
_Visit(Sdf_PathNode const * path,Sdf_Stats * stats)687 static void _Visit(Sdf_PathNode const *path, Sdf_Stats *stats)
688 {
689     stats->numNodes++;
690     stats->numNodeRefs += path->GetCurrentRefCount();
691     stats->typeTable[path->GetNodeType()] += 1;
692 
693     // Accumulate length histogram
694     const size_t len = path->GetElementCount()+1; // add 1 for abs/rel root
695     while (stats->lengthTable.size() <= len)
696         stats->lengthTable.push_back(0);
697     stats->lengthTable[len]++;
698 
699     const std::vector<Sdf_PathNodeConstRefPtr> children = _GetChildren(path);
700 
701     // Accumulate children count histogram
702     const size_t numChildren = children.size();
703     while (stats->numChildrenTable.size() <= numChildren)
704         stats->numChildrenTable.push_back(0);
705     stats->numChildrenTable[numChildren]++;
706 
707     TF_FOR_ALL(child, children) {
708         _Visit(child->get(), stats);
709     }
710 }
711 
Sdf_DumpPathStats()712 void Sdf_DumpPathStats()
713 {
714     Sdf_Stats stats;
715     stats.numNodes = 0;
716     stats.numNodeRefs = 0;
717     memset(stats.typeTable, 0, sizeof(stats.typeTable));
718 
719     _Visit( Sdf_PathNode::GetAbsoluteRootNode(), &stats );
720     _Visit( Sdf_PathNode::GetRelativeRootNode(), &stats );
721 
722     printf("Sdf_PathNode stats:\n");
723     printf("\tnum node refs: %i\n", stats.numNodeRefs);
724     printf("\tnum nodes:     %i\n", stats.numNodes);
725     printf("\tsizeof(SdfPath), aka node ref:  %zu\n", sizeof(SdfPath));
726     printf("\tsizeof(Sdf_PathNode), aka node: %zu\n", sizeof(Sdf_PathNode));
727 
728     // XXX: Use TfEnum.
729     char const *enumNameMap[Sdf_PathNode::NumNodeTypes];
730     enumNameMap[Sdf_PathNode::RootNode] = "RootNode";
731     enumNameMap[Sdf_PathNode::PrimNode] = "PrimNode";
732     enumNameMap[Sdf_PathNode::PrimPropertyNode] = "PrimPropertyNode";
733     enumNameMap[Sdf_PathNode::PrimVariantSelectionNode] =
734         "PrimVariantSelectionNode";
735     enumNameMap[Sdf_PathNode::TargetNode] = "TargetNode";
736     enumNameMap[Sdf_PathNode::RelationalAttributeNode] =
737         "RelationalAttributeNode";
738     enumNameMap[Sdf_PathNode::MapperNode] = "MapperNode";
739     enumNameMap[Sdf_PathNode::MapperArgNode] = "MapperArgNode";
740     enumNameMap[Sdf_PathNode::ExpressionNode] = "ExpressionNode";
741 
742     printf("------------------------------------------------");
743     printf("-- By Type\n");
744     for (size_t i = 0; i != Sdf_PathNode::NumNodeTypes; ++i) {
745         printf("\t%32ss: %8zu -- %6.2f%%\n",
746                enumNameMap[i], stats.typeTable[i],
747                100.0 * double(stats.typeTable[i]) / double(stats.numNodes));
748     }
749 
750     printf("------------------------------------------------");
751     printf("-- By Length\n");
752     size_t totalLen = 0;
753     for (size_t i=0; i < stats.lengthTable.size();++i) {
754         printf("\tnum nodes with %3zu components : %i\n",
755                i, stats.lengthTable[i] );
756         totalLen += i * stats.lengthTable[i];
757     }
758     printf("\tavg num components: %g\n",
759            totalLen / float(stats.numNodes));
760 
761     printf("------------------------------------------------");
762     printf("-- By Number of Children\n");
763     for (size_t i=0; i < stats.numChildrenTable.size(); ++i) {
764         printf("\tnum nodes with %3zu children : %i\n",
765                i, stats.numChildrenTable[i] );
766     }
767 
768     size_t numChildren = 0;
769     for (size_t i=1; i < stats.numChildrenTable.size(); ++i) {
770         numChildren += i*stats.numChildrenTable[i];
771     }
772     printf("\tavg num children (for nodes with any children): %g\n",
773            numChildren / float(stats.numNodes - stats.numChildrenTable[0]));
774 
775     printf("\n");
776 }
777 
778 PXR_NAMESPACE_CLOSE_SCOPE
779