SdfChangeList::_MoveEntry(SdfPath const &oldPath, SdfPath const &newPath)
{
TF_DEV_AXIOM(!oldPath.IsEmpty() && !newPath.IsEmpty());
+ if (oldPath == newPath) {
+ TF_DEV_AXIOM("oldPath and newPath are equal");
+ return _GetEntry(newPath);
+ }
+
+ // Move the old entry to the tmp space then reset, if it exists.
+ // By resetting the old entry we're leaving an empty marker where
+ // in the entry list the object was moved from. This is needed when
+ // replaying changes to create the prim at the right time so the
+ // prim order is reproduced. For example, if we create /A then /B
+ // then rename /A to /C we'll get an empty /A entry, a creation /B
+ // entry, and a rename /C entry. If we didn't keep the /A then
+ // replaying would create /B then /C and the prim order would be
+ // [B, C]. The prim order should be [C, B] since we created A
+ // first.
Entry tmp;
auto constIter = FindEntry(oldPath);
if (constIter != _entries.end()) {
- // Move the old entry to the tmp space, then erase.
auto iter = _MakeNonConstIterator(constIter);
- tmp = std::move(iter->second);
- // Erase the element and rebuild the accelerator if needed.
- _entries.erase(iter);
- _RebuildAccel();
+ std::swap(tmp, iter->second);
+
+ // If the object wasn't created then we don't need to keep it.
+ // If the object was itself due to a rename then we don't need
+ // to keep it.
+ const auto& flags = tmp.flags;
+ if (!(flags.didAddInertPrim ||
+ flags.didAddNonInertPrim ||
+ flags.didAddProperty ||
+ flags.didAddPropertyWithOnlyRequiredFields) ||
+ !tmp.oldPath.IsEmpty()) {
+ _entries.erase(iter);
+ _RebuildAccel();
+ }
}
+
// Find or create the new entry, and move tmp over it. This either
// populates the new entry with the old entry (if one existed) or it clears
// out the new entry.
Entry &newEntry = _GetEntry(newPath);
newEntry = std::move(tmp);
+
+ // Indicate that a rename occurred.
+ newEntry.flags.didRename = true;
+
+ // Record the source path, but only if it has not already been set
+ // by a prior rename during this round of change processing.
+ if (newEntry.oldPath.IsEmpty()) {
+ newEntry.oldPath = oldPath;
+ }
+
return newEntry;
}
}
}
else {
- // Linear search the unsorted range.
- iter = std::find_if(_entries.begin(), _entries.end(),
- [&path](EntryList::value_type const &e) {
- return e.first == path;
- });
+ // Reverse linear search the "unsorted" range. Entries are added
+ // sequentially so the order mostly reflects the order changes
+ // happened. We can accumulate changes into an entry that isn't
+ // last in _entries (thus the "mostly" above) but it's always the
+ // last entry for a given path. By doing a reverse search we get
+ // that last entry.
+ auto riter = std::find_if(_entries.rbegin(), _entries.rend(),
+ [&path](EntryList::value_type const &e) {
+ return e.first == path;
+ });
+ if (riter == _entries.rend()) {
+ iter = _entries.end();
+ }
+ else {
+ // Reverse iterator is off-by-one from normal iterator.
+ iter = std::prev(riter.base());
+ }
}
return iter;
}
_entries.emplace_back(std::piecewise_construct,
std::tie(path), std::tuple<>());
if (_entriesAccel) {
- _entriesAccel->emplace(path, _entries.size()-1);
+ _entriesAccel->insert_or_assign(path, _entries.size()-1);
}
else if (ARCH_UNLIKELY(_entries.size() >= _AccelThreshold)) {
_RebuildAccel();
SdfPath, size_t, SdfPath::Hash>(_entries.size()));
size_t idx = 0;
for (auto const &p: _entries) {
- _entriesAccel->emplace(p.first, idx++);
+ _entriesAccel->insert_or_assign(p.first, idx++);
}
}
else {
{
Entry &newEntry = _GetEntry(newPath);
- if (newEntry.flags.didRemoveNonInertPrim) {
- // We've already removed a spec at the target, so we can't simply
- // overwrite the newPath entries with the ones from oldPath.
- // Nor is it clear how to best merge the edits, while retaining
- // the didRename hints. Instead, we simply fall back to treating
- // this case as though newPath and oldPath were both removed,
- // and a new spec added at newPath.
- newEntry = Entry();
- newEntry.flags.didRemoveNonInertPrim = true;
- newEntry.flags.didAddNonInertPrim = true;
-
- // Fetch oldEntry -- note that this can invalidate 'newEntry'!
- Entry &oldEntry = _GetEntry(oldPath);
- // Clear out existing edits.
- oldEntry = Entry();
- oldEntry.flags.didRemoveNonInertPrim = true;
- } else {
- // Transfer accumulated changes about oldPath to apply to newPath.
- Entry &moved = _MoveEntry(oldPath, newPath);
-
- // Indicate that a rename occurred.
- moved.flags.didRename = true;
-
- // Record the source path, but only if it has not already been set
- // by a prior rename during this round of change processing.
- if (moved.oldPath.IsEmpty())
- moved.oldPath = oldPath;
+ // If the prim at newPath was previously removed then create a new
+ // entry for the move so we keep a separate record of the removal.
+ if (newEntry.flags.didRemoveInertPrim ||
+ newEntry.flags.didRemoveNonInertPrim) {
+ _AddNewEntry(newPath);
}
+
+ // Transfer accumulated changes about oldPath to apply to newPath.
+ _MoveEntry(oldPath, newPath);
}
void
void
SdfChangeList::DidAddPrim(const SdfPath &path, bool inert)
{
- if (inert)
- _GetEntry(path).flags.didAddInertPrim = true;
- else
- _GetEntry(path).flags.didAddNonInertPrim = true;
+ auto* entry = &_GetEntry(path);
+
+ // If this prim was previously removed then create a new entry for the
+ // add so we keep a separate record of the removal. This avoids a
+ // which-came-first ambiguity when both add and remove flags are set.
+ if (entry->flags.didRemoveInertPrim || entry->flags.didRemoveNonInertPrim) {
+ entry = &_AddNewEntry(path);
+ }
+
+ if (inert) {
+ entry->flags.didAddInertPrim = true;
+ }
+ else {
+ entry->flags.didAddNonInertPrim = true;
+ }
}
void
SdfChangeList::DidRemovePrim(const SdfPath &path, bool inert)
{
- if (inert)
- _GetEntry(path).flags.didRemoveInertPrim = true;
- else
- _GetEntry(path).flags.didRemoveNonInertPrim = true;
+ auto* entry = &_GetEntry(path);
+
+ // If this prim was previously added then create a new entry for the
+ // remove so we keep a separate record of the addition. This avoids
+ // a which-came-first ambiguity when both add and remove flags are
+ // set.
+ if (entry->flags.didAddInertPrim || entry->flags.didAddNonInertPrim) {
+ entry = &_AddNewEntry(path);
+ }
+
+ if (inert) {
+ entry->flags.didRemoveInertPrim = true;
+ }
+ else {
+ entry->flags.didRemoveNonInertPrim = true;
+ }
}
void
SdfChangeList::DidMovePrim(const SdfPath &oldPath, const SdfPath &newPath)
{
- Entry &oldEntry = _GetEntry(oldPath);
- oldEntry.flags.didRemoveNonInertPrim = true;
-
- Entry &newEntry = _GetEntry(newPath);
- newEntry.flags.didAddNonInertPrim = true;
- newEntry.oldPath = oldPath;
+ DidRemovePrim(oldPath, false);
+ DidAddPrim(newPath, false);
+ _GetEntry(newPath).oldPath = oldPath;
}
void
{
Entry &newEntry = _GetEntry(newPath);
- if (newEntry.flags.didRemoveProperty) {
- // We've already removed a spec at the target, so we can't simply
- // overrwrite the newPath entries with the ones from oldPath.
- // Nor is it clear how to best merge the edits, while retaining
- // the didRename hints. Instead, we simply fall back to treating
- // this case as though newPath and oldPath were both removed,
- // and a new spec added at newPath.
- newEntry = Entry();
- newEntry.flags.didRemoveProperty = true;
- newEntry.flags.didAddProperty = true;
-
- // Note that fetching oldEntry may create its entry and invalidate
- // 'newEntry'!
- Entry &oldEntry = _GetEntry(oldPath);
- oldEntry = Entry();
- _GetEntry(oldPath).flags.didRemoveProperty = true;
- } else {
- // Transfer accumulated changes about oldPath to apply to newPath.
- Entry &moved = _MoveEntry(oldPath, newPath);
-
- // Indicate that ac rename occurred.
- moved.flags.didRename = true;
-
- // Record the source path, but only if it has not already been set
- // by a prior rename during this round of change processing.
- if (moved.oldPath.IsEmpty())
- moved.oldPath = oldPath;
+ // If the property at newPath was previously removed then create a new
+ // entry for the move so we keep a separate record of the removal.
+ if (newEntry.flags.didRemovePropertyWithOnlyRequiredFields ||
+ newEntry.flags.didRemoveProperty) {
+ _AddNewEntry(newPath);
}
+
+ // Transfer accumulated changes about oldPath to apply to newPath.
+ _MoveEntry(oldPath, newPath);
}
void
void
SdfChangeList::DidAddProperty(const SdfPath &path, bool hasOnlyRequiredFields)
{
- if (hasOnlyRequiredFields)
- _GetEntry(path).flags.didAddPropertyWithOnlyRequiredFields = true;
- else
- _GetEntry(path).flags.didAddProperty = true;
+ auto* entry = &_GetEntry(path);
+
+ // If this property was previously removed then create a new entry for
+ // the move so we keep a separate record of the addition. This avoids
+ // a which-came-first ambiguity when both add and remove flags are set.
+ if (entry->flags.didRemovePropertyWithOnlyRequiredFields ||
+ entry->flags.didRemoveProperty) {
+ entry = &_AddNewEntry(path);
+ }
+
+ if (hasOnlyRequiredFields) {
+ entry->flags.didAddPropertyWithOnlyRequiredFields = true;
+ }
+ else {
+ entry->flags.didAddProperty = true;
+ }
}
void
SdfChangeList::DidRemoveProperty(const SdfPath &path, bool hasOnlyRequiredFields)
{
- if (hasOnlyRequiredFields)
- _GetEntry(path).flags.didRemovePropertyWithOnlyRequiredFields = true;
- else
- _GetEntry(path).flags.didRemoveProperty = true;
+ auto* entry = &_GetEntry(path);
+
+ // If this property was previously added then create a new entry for
+ // the remove so we keep a separate record of the removal. This avoids
+ // a which-came-first ambiguity when both add and remove flags are set..
+ if (entry->flags.didAddPropertyWithOnlyRequiredFields ||
+ entry->flags.didAddProperty) {
+ entry = &_AddNewEntry(path);
+ }
+
+ if (hasOnlyRequiredFields) {
+ entry->flags.didRemovePropertyWithOnlyRequiredFields = true;
+ }
+ else {
+ entry->flags.didRemoveProperty = true;
+ }
}
void
void
SdfChangeList::DidAddTarget(const SdfPath &targetPath)
{
- _GetEntry(targetPath).flags.didAddTarget = true;
+ auto* entry = &_GetEntry(targetPath);
+
+ // If this target was previously removed then create a new entry for
+ // the add so we keep a separate record of the addition. This avoids
+ // a which-came-first ambiguity when both add and remove flags are set.
+ if (entry->flags.didRemoveTarget) {
+ entry = &_AddNewEntry(targetPath);
+ }
+
+ entry->flags.didAddTarget = true;
}
void
SdfChangeList::DidRemoveTarget(const SdfPath &targetPath)
{
- _GetEntry(targetPath).flags.didRemoveTarget = true;
+ auto* entry = &_GetEntry(targetPath);
+
+ // If this target was previously added then create a new entry for
+ // the remove so we keep a separate record of the removal. This avoids
+ // a which-came-first ambiguity when both add and remove flags are set.
+ if (entry->flags.didAddTarget) {
+ entry = &_AddNewEntry(targetPath);
+ }
+
+ entry->flags.didRemoveTarget = true;
}
PXR_NAMESPACE_CLOSE_SCOPE