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