From 4ee942d9a6749a3c9cfbcf17bdeaf60ce43d8072 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 12 May 2025 19:20:21 +1000 Subject: [PATCH 01/18] Add `diff` method to `TreeTraversalState`. --- .../CesiumUtility/TreeTraversalState.h | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index d0fe2b2a0..08ef643d3 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -313,6 +313,69 @@ template class TreeTraversalState { return this->slowlyGetStates(this->_previousTraversal); } + /** + * @brief Compares the current traversal against the previous one, invoking a + * callback for each case where a node had a different state in the two + * traversals. + * + * @tparam TCallback The type of the callback. + * @param callback The callback to be invoked for each difference. The + * function is given the node pointer, the old state, and the new state, in + * that order. + */ + template void diff(TCallback&& callback) { + size_t previousIndex = 0; + size_t currentIndex = 0; + + while (previousIndex < this->_previousTraversal.size() && + currentIndex > this->_currentTraversal.size()) { + const TraversalData& previousData = + this->_previousTraversal[previousIndex]; + const TraversalData& currentData = this->_currentTraversal[currentIndex]; + + CESIUM_ASSERT(previousData.pNode == currentData.pNode); + if (previousData.pNode != currentData.pNode) { + // This shouldn't happen. + break; + } + + if (previousData.state != currentData.state) { + // Different state - report it + callback(previousData.pNode, previousData.state, currentData.state); + } + + bool previousTraversalVisitedChildren = + previousData.nextSiblingIndex > previousIndex + 1; + bool currentTraversalVisitedChildren = + currentData.nextSiblingIndex > currentIndex + 1; + + if (previousTraversalVisitedChildren == currentTraversalVisitedChildren) { + ++previousIndex; + ++currentIndex; + } else if (previousTraversalVisitedChildren) { + while (previousIndex < previousData.nextSiblingIndex) { + const TraversalData& skipped = + this->_previousTraversal[previousIndex]; + callback(skipped.pNode, skipped.state, TState()); + ++previousIndex; + } + + ++currentIndex; + } else { + while (currentIndex < currentData.nextSiblingIndex) { + const TraversalData& skipped = this->_currentTraversal[currentIndex]; + callback(skipped.pNode, TState(), skipped.state); + ++currentIndex; + } + + ++previousIndex; + } + } + + CESIUM_ASSERT(previousIndex == this->_previousTraversal.size()); + CESIUM_ASSERT(currentIndex == this->_currentTraversal.size()); + } + private: struct TraversalData { TNodePointer pNode; From e4118f5ac0986af2ed7b084cf75c1fc4f5dfbbd8 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 12 May 2025 20:31:38 +1000 Subject: [PATCH 02/18] Tests and fixes for `diff`. --- .../CesiumUtility/TreeTraversalState.h | 31 ++++++++-------- CesiumUtility/test/TestTreeTraversalState.cpp | 36 +++++++++++++++++++ 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 08ef643d3..497179a27 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -323,12 +323,12 @@ template class TreeTraversalState { * function is given the node pointer, the old state, and the new state, in * that order. */ - template void diff(TCallback&& callback) { - size_t previousIndex = 0; - size_t currentIndex = 0; + template void diff(TCallback&& callback) const { + int64_t previousIndex = 0; + int64_t currentIndex = 0; - while (previousIndex < this->_previousTraversal.size() && - currentIndex > this->_currentTraversal.size()) { + while (previousIndex < int64_t(this->_previousTraversal.size()) && + currentIndex < int64_t(this->_currentTraversal.size())) { const TraversalData& previousData = this->_previousTraversal[previousIndex]; const TraversalData& currentData = this->_currentTraversal[currentIndex]; @@ -349,31 +349,30 @@ template class TreeTraversalState { bool currentTraversalVisitedChildren = currentData.nextSiblingIndex > currentIndex + 1; - if (previousTraversalVisitedChildren == currentTraversalVisitedChildren) { - ++previousIndex; - ++currentIndex; - } else if (previousTraversalVisitedChildren) { + ++previousIndex; + ++currentIndex; + + if (previousTraversalVisitedChildren && + !currentTraversalVisitedChildren) { while (previousIndex < previousData.nextSiblingIndex) { const TraversalData& skipped = this->_previousTraversal[previousIndex]; callback(skipped.pNode, skipped.state, TState()); ++previousIndex; } - - ++currentIndex; - } else { + } else if ( + currentTraversalVisitedChildren && + !previousTraversalVisitedChildren) { while (currentIndex < currentData.nextSiblingIndex) { const TraversalData& skipped = this->_currentTraversal[currentIndex]; callback(skipped.pNode, TState(), skipped.state); ++currentIndex; } - - ++previousIndex; } } - CESIUM_ASSERT(previousIndex == this->_previousTraversal.size()); - CESIUM_ASSERT(currentIndex == this->_currentTraversal.size()); + CESIUM_ASSERT(previousIndex == int64_t(this->_previousTraversal.size())); + CESIUM_ASSERT(currentIndex == int64_t(this->_currentTraversal.size())); } private: diff --git a/CesiumUtility/test/TestTreeTraversalState.cpp b/CesiumUtility/test/TestTreeTraversalState.cpp index a0c484fbe..1e5c57ee6 100644 --- a/CesiumUtility/test/TestTreeTraversalState.cpp +++ b/CesiumUtility/test/TestTreeTraversalState.cpp @@ -12,6 +12,16 @@ struct Node { std::string name; }; +std::vector> +getDifferences(const TreeTraversalState& traversalState) { + std::vector> differences; + traversalState.diff( + [&differences](Node* pNode, int previousState, int currentState) { + differences.emplace_back(pNode, previousState, currentState); + }); + return differences; +} + } // namespace TEST_CASE("TreeTraversalState") { @@ -81,24 +91,29 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&a); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 1); + traversalState.currentState() = 1; traversalState.beginNode(&b); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 2); + traversalState.currentState() = 2; traversalState.finishNode(&b); traversalState.beginNode(&c); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 3); + traversalState.currentState() = 3; traversalState.beginNode(&e); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 5); + traversalState.currentState() = 5; traversalState.finishNode(&e); traversalState.beginNode(&f); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 6); + traversalState.currentState() = 6; traversalState.finishNode(&f); traversalState.finishNode(&c); @@ -106,10 +121,15 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&d); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 4); + traversalState.currentState() = 4; traversalState.finishNode(&d); traversalState.finishNode(&a); // clang-format on + + SUBCASE("and diff reports no differences") { + CHECK(getDifferences(traversalState).empty()); + } } SUBCASE("Second traversal can skip children") { @@ -119,24 +139,40 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&a); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 1); + traversalState.currentState() = 1; traversalState.beginNode(&b); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 2); + traversalState.currentState() = 2; traversalState.finishNode(&b); traversalState.beginNode(&c); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 3); + traversalState.currentState() = 3; traversalState.finishNode(&c); traversalState.beginNode(&d); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 4); + traversalState.currentState() = 4; traversalState.finishNode(&d); traversalState.finishNode(&a); // clang-format on + + SUBCASE("and diff reports the differences") { + std::vector> differences = + getDifferences(traversalState); + REQUIRE(differences.size() == 2); + CHECK(std::get<0>(differences[0]) == &e); + CHECK(std::get<1>(differences[0]) == 5); + CHECK(std::get<2>(differences[0]) == int()); + CHECK(std::get<0>(differences[1]) == &f); + CHECK(std::get<1>(differences[1]) == 6); + CHECK(std::get<2>(differences[1]) == int()); + } } SUBCASE("Second traversal can visit new children") { From 8d0623830aaef77ee71105ddcf79df8bf2ed432c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 12 May 2025 21:17:39 +1000 Subject: [PATCH 03/18] More tests of `diff`. --- CesiumUtility/test/TestTreeTraversalState.cpp | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/CesiumUtility/test/TestTreeTraversalState.cpp b/CesiumUtility/test/TestTreeTraversalState.cpp index 1e5c57ee6..063a86bcd 100644 --- a/CesiumUtility/test/TestTreeTraversalState.cpp +++ b/CesiumUtility/test/TestTreeTraversalState.cpp @@ -184,19 +184,23 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&a); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 1); + traversalState.currentState() = 1; traversalState.beginNode(&b); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 2); + traversalState.currentState() = 2; traversalState.finishNode(&b); traversalState.beginNode(&c); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 3); + traversalState.currentState() = 3; traversalState.beginNode(&e); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 5); + traversalState.currentState() = 5; traversalState.beginNode(&g); CHECK(traversalState.previousState() == nullptr); @@ -208,6 +212,7 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&f); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 6); + traversalState.currentState() = 6; traversalState.finishNode(&f); traversalState.finishNode(&c); @@ -215,10 +220,20 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&d); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 4); + traversalState.currentState() = 4; traversalState.finishNode(&d); traversalState.finishNode(&a); // clang-format on + + SUBCASE("and diff reports the differences") { + std::vector> differences = + getDifferences(traversalState); + REQUIRE(differences.size() == 1); + CHECK(std::get<0>(differences[0]) == &g); + CHECK(std::get<1>(differences[0]) == int()); + CHECK(std::get<2>(differences[0]) == 7); + } } SUBCASE("Second traversal can add two new levels") { @@ -230,19 +245,23 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&a); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 1); + traversalState.currentState() = 1; traversalState.beginNode(&b); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 2); + traversalState.currentState() = 2; traversalState.finishNode(&b); traversalState.beginNode(&c); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 3); + traversalState.currentState() = 3; traversalState.beginNode(&e); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 5); + traversalState.currentState() = 5; traversalState.beginNode(&g); CHECK(traversalState.previousState() == nullptr); @@ -259,6 +278,7 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&f); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 6); + traversalState.currentState() = 6; traversalState.finishNode(&f); traversalState.finishNode(&c); @@ -266,10 +286,23 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&d); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 4); + traversalState.currentState() = 4; traversalState.finishNode(&d); traversalState.finishNode(&a); // clang-format on + + SUBCASE("and diff reports the differences") { + std::vector> differences = + getDifferences(traversalState); + REQUIRE(differences.size() == 2); + CHECK(std::get<0>(differences[0]) == &g); + CHECK(std::get<1>(differences[0]) == int()); + CHECK(std::get<2>(differences[0]) == 7); + CHECK(std::get<0>(differences[1]) == &h); + CHECK(std::get<1>(differences[1]) == int()); + CHECK(std::get<2>(differences[1]) == 8); + } } SUBCASE( @@ -396,24 +429,46 @@ TEST_CASE("TreeTraversalState") { traversalState.beginNode(&a); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 1); + traversalState.currentState() = 1; traversalState.beginNode(&b); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 2); + traversalState.currentState() = 2; traversalState.finishNode(&b); traversalState.beginNode(&c); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 3); + traversalState.currentState() = 3; traversalState.finishNode(&c); traversalState.beginNode(&d); REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 4); + traversalState.currentState() = 4; traversalState.finishNode(&d); traversalState.finishNode(&a); // clang-format on + + SUBCASE("and diff reports the differences") { + std::vector> differences = + getDifferences(traversalState); + REQUIRE(differences.size() == 4); + CHECK(std::get<0>(differences[0]) == &e); + CHECK(std::get<1>(differences[0]) == 5); + CHECK(std::get<2>(differences[0]) == int()); + CHECK(std::get<0>(differences[1]) == &g); + CHECK(std::get<1>(differences[1]) == 7); + CHECK(std::get<2>(differences[1]) == int()); + CHECK(std::get<0>(differences[2]) == &h); + CHECK(std::get<1>(differences[2]) == 8); + CHECK(std::get<2>(differences[2]) == int()); + CHECK(std::get<0>(differences[3]) == &f); + CHECK(std::get<1>(differences[3]) == 6); + CHECK(std::get<2>(differences[3]) == int()); + } } } @@ -474,11 +529,13 @@ TEST_CASE("TreeTraversalState") { traversalState.currentState() = 1; REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 1); + traversalState.currentState() = 1; traversalState.beginNode(&b); traversalState.currentState() = 2; REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 2); + traversalState.currentState() = 2; traversalState.beginNode(&f); traversalState.currentState() = 6; @@ -496,22 +553,37 @@ TEST_CASE("TreeTraversalState") { traversalState.currentState() = 3; REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 3); + traversalState.currentState() = 3; traversalState.beginNode(&d); traversalState.currentState() = 4; REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 4); + traversalState.currentState() = 4; traversalState.finishNode(&d); traversalState.beginNode(&e); traversalState.currentState() = 5; REQUIRE(traversalState.previousState() != nullptr); CHECK(*traversalState.previousState() == 5); + traversalState.currentState() = 5; traversalState.finishNode(&e); traversalState.finishNode(&c); traversalState.finishNode(&a); // clang-format on + + SUBCASE("and diff reports the differences") { + std::vector> differences = + getDifferences(traversalState); + REQUIRE(differences.size() == 2); + CHECK(std::get<0>(differences[0]) == &f); + CHECK(std::get<1>(differences[0]) == int()); + CHECK(std::get<2>(differences[0]) == 6); + CHECK(std::get<0>(differences[1]) == &g); + CHECK(std::get<1>(differences[1]) == int()); + CHECK(std::get<2>(differences[1]) == 7); + } } } From c80520e2af0d5519f7257e70cdf2c1f8f7178f5b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 12 May 2025 21:20:13 +1000 Subject: [PATCH 04/18] Update CHANGES.md. --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index c21ec8d23..5080d22cf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### ? - ? + +##### Additions :tada: + +- Added `diff` method to `TreeTraversalState`. + ### v0.48.0 - 2025-06-02 ##### Breaking Changes :mega: From a1c49352a68375890c621a0eed0b5e51841dbb41 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 12 May 2025 21:22:24 +1000 Subject: [PATCH 05/18] Fix clang/gcc warnings. --- .../include/CesiumUtility/TreeTraversalState.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 497179a27..6041673ed 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -330,8 +330,9 @@ template class TreeTraversalState { while (previousIndex < int64_t(this->_previousTraversal.size()) && currentIndex < int64_t(this->_currentTraversal.size())) { const TraversalData& previousData = - this->_previousTraversal[previousIndex]; - const TraversalData& currentData = this->_currentTraversal[currentIndex]; + this->_previousTraversal[size_t(previousIndex)]; + const TraversalData& currentData = + this->_currentTraversal[size_t(currentIndex)]; CESIUM_ASSERT(previousData.pNode == currentData.pNode); if (previousData.pNode != currentData.pNode) { @@ -356,7 +357,7 @@ template class TreeTraversalState { !currentTraversalVisitedChildren) { while (previousIndex < previousData.nextSiblingIndex) { const TraversalData& skipped = - this->_previousTraversal[previousIndex]; + this->_previousTraversal[size_t(previousIndex)]; callback(skipped.pNode, skipped.state, TState()); ++previousIndex; } @@ -364,7 +365,8 @@ template class TreeTraversalState { currentTraversalVisitedChildren && !previousTraversalVisitedChildren) { while (currentIndex < currentData.nextSiblingIndex) { - const TraversalData& skipped = this->_currentTraversal[currentIndex]; + const TraversalData& skipped = + this->_currentTraversal[size_t(currentIndex)]; callback(skipped.pNode, TState(), skipped.state); ++currentIndex; } From 27d665fedf5ca700cba07fd243c7d954ae4b067d Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 12 May 2025 21:46:40 +1000 Subject: [PATCH 06/18] Improve doc, add assertion. --- .../include/CesiumUtility/TreeTraversalState.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 6041673ed..dd3a826a2 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -318,12 +318,27 @@ template class TreeTraversalState { * callback for each case where a node had a different state in the two * traversals. * + * The callback is also invoked: + * + * * For each node that was visited previously but was not visited in the + * current traversal. In this case, the current state provided to the callback + * is a default-constructed `TState` instance. + * * For each node that was not visited previously but was visited in the + * current traversal. In this case, the previous state provided to the + * callback is a default-constructed `TState` instance. + * + * This method should only be called after the {@link finishNode} for the + * root node, and before {@link beginTraversal}. In other words, it should + * not be called while a traversal is in progress. + * * @tparam TCallback The type of the callback. * @param callback The callback to be invoked for each difference. The * function is given the node pointer, the old state, and the new state, in * that order. */ template void diff(TCallback&& callback) const { + CESIUM_ASSERT(this->_parentIndices.empty()); + int64_t previousIndex = 0; int64_t currentIndex = 0; From b45830aa8cd5310048cb3b08b1e478c2ee4f27f6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 13 May 2025 21:02:28 +1000 Subject: [PATCH 07/18] Iterator-based TreeTraversalState diffing. --- .../CesiumUtility/TreeTraversalState.h | 325 ++++++++++++++++++ CesiumUtility/test/TestTreeTraversalState.cpp | 12 +- 2 files changed, 333 insertions(+), 4 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index dd3a826a2..5bdbc626b 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -3,11 +3,99 @@ #include #include +#include #include #include namespace CesiumUtility { +template +struct TreeTraversalStateDifference { + const TNodePointer& pNode; + const TState& previousState; + const TState& currentState; + + TreeTraversalStateDifference* operator->() { return this; } + const TreeTraversalStateDifference* operator->() const { return this; } +}; + +template class TreeTraversalState; + +template +class TreeTraversalStateDiffIterator { +public: + /** + * @brief The iterator category tag denoting this is a forward iterator. + */ + using iterator_category = std::forward_iterator_tag; + /** + * @brief The type of value that is being iterated over. + */ + using value_type = TreeTraversalStateDifference; + /** + * @brief The type used to identify distance between iterators. + * + * This is `void` because there is no meaningful measure of distance between + * tiles. + */ + using difference_type = void; + /** + * @brief A pointer to the type being iterated over. + */ + using pointer = const value_type*; + /** + * @brief A reference to the type being iterated over. + */ + using reference = const value_type&; + + /** + * @brief Returns a reference to the current difference being iterated. + */ + value_type operator*() const noexcept; + /** + * @brief Returns a pointer to the current difference being iterated. + */ + value_type operator->() const noexcept; + + /** + * @brief Advances the iterator to the next difference (pre-incrementing). + */ + TreeTraversalStateDiffIterator& operator++() noexcept; + /** + * @brief Advances the iterator to the next difference (post-incrementing). + */ + TreeTraversalStateDiffIterator operator++(int) noexcept; + + /** @brief Checks if two iterators are at the same difference. */ + bool operator==(const TreeTraversalStateDiffIterator& rhs) const noexcept; + /** @brief Checks if two iterators are not at the same difference. */ + bool operator!=(const TreeTraversalStateDiffIterator& rhs) const noexcept; + +private: + explicit TreeTraversalStateDiffIterator( + const TreeTraversalState* pState, + int64_t previousIndex, + int64_t currentIndex) noexcept; + void advanceToNextDifference() noexcept; + + const TreeTraversalState* _pState; + // The index of the current difference in _previousTraversal. + int64_t _previousIndex; + // The index of the current difference in _currentTraversal. + int64_t _currentIndex; + // The next sibling index if we're currently skipping siblings. -1 if we're + // not currently skipping siblings. + int64_t _nextSiblingIndex; + // If we're skipping siblings, this value is true when the skipped siblings + // are from the previous traversal, or false if the skipped siblings are from + // the current traversal. + bool _skippingPrevious; + + static const inline TState DEFAULT_STATE{}; + + friend class TreeTraversalState; +}; + /** * @brief Associates state (arbitrary data) with each node during partial, * depth-first traversal of a tree. Then, during a later traversal of a @@ -313,6 +401,20 @@ template class TreeTraversalState { return this->slowlyGetStates(this->_previousTraversal); } + using difference_iterator = + TreeTraversalStateDiffIterator; + + difference_iterator beginDiff() const noexcept { + return difference_iterator(this, 0, 0); + } + + difference_iterator endDiff() const noexcept { + return difference_iterator( + this, + int64_t(this->_previousTraversal.size()), + int64_t(this->_currentTraversal.size())); + } + /** * @brief Compares the current traversal against the previous one, invoking a * callback for each case where a node had a different state in the two @@ -480,6 +582,229 @@ template class TreeTraversalState { // new node is added to the end of `_currentTraversal`. In // `_previousTraversal`, if it exists at all, it will be found at this index. int64_t _previousTraversalNextNodeIndex = 0; + + template + friend class TreeTraversalStateDiffIterator; }; +template +TreeTraversalStateDiffIterator::value_type +TreeTraversalStateDiffIterator::operator*() + const noexcept { + if (this->_nextSiblingIndex >= 0) { + if (this->_skippingPrevious) { + CESIUM_ASSERT( + this->_previousIndex >= 0 && + size_t(this->_previousIndex) < + this->_pState->_previousTraversal.size()); + + const TreeTraversalState::TraversalData& data = + this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + return value_type{ + .pNode = data.pNode, + .previousState = data.state, + .currentState = DEFAULT_STATE}; + } else { + CESIUM_ASSERT( + this->_currentIndex >= 0 && + size_t(this->_currentIndex) < + this->_pState->_currentTraversal.size()); + + const TreeTraversalState::TraversalData& data = + this->_pState->_currentTraversal[size_t(this->_currentIndex)]; + return value_type{ + .pNode = data.pNode, + .previousState = DEFAULT_STATE, + .currentState = data.state}; + } + } else { + CESIUM_ASSERT( + this->_previousIndex >= 0 && + size_t(this->_previousIndex) < + this->_pState->_previousTraversal.size()); + CESIUM_ASSERT( + this->_currentIndex >= 0 && + size_t(this->_currentIndex) < this->_pState->_currentTraversal.size()); + + const TreeTraversalState::TraversalData& + previousData = + this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + const TreeTraversalState::TraversalData& currentData = + this->_pState->_currentTraversal[size_t(this->_currentIndex)]; + + CESIUM_ASSERT(previousData.pNode == currentData.pNode); + + return value_type{ + .pNode = previousData.pNode, + .previousState = previousData.state, + .currentState = currentData.state}; + } +} + +template +TreeTraversalStateDiffIterator::value_type +TreeTraversalStateDiffIterator::operator->() + const noexcept { + return this->operator*(); +} + +template +TreeTraversalStateDiffIterator& +TreeTraversalStateDiffIterator::operator++() noexcept { + this->advanceToNextDifference(); + return *this; +} + +template +TreeTraversalStateDiffIterator +TreeTraversalStateDiffIterator::operator++(int) noexcept { + TreeTraversalStateDiffIterator result = *this; + return ++result; +} + +template +bool TreeTraversalStateDiffIterator::operator==( + const TreeTraversalStateDiffIterator& rhs) const noexcept { + return this->_previousIndex == rhs._previousIndex && + this->_currentIndex == rhs._currentIndex && + this->_pState == rhs._pState; +} + +template +bool TreeTraversalStateDiffIterator::operator!=( + const TreeTraversalStateDiffIterator& rhs) const noexcept { + return !(*this == rhs); +} + +template +TreeTraversalStateDiffIterator:: + TreeTraversalStateDiffIterator( + const TreeTraversalState* pState, + int64_t previousIndex, + int64_t currentIndex) noexcept + : _pState(pState), + _previousIndex(previousIndex), + _currentIndex(currentIndex), + _nextSiblingIndex(-1), + _skippingPrevious(false) { + bool validPreviousIndex = + previousIndex < int64_t(pState->_previousTraversal.size()); + bool validCurrentIndex = + currentIndex < int64_t(pState->_currentTraversal.size()); + if (validPreviousIndex && validCurrentIndex) { + const TreeTraversalState::TraversalData& + previousData = pState->_previousTraversal[size_t(previousIndex)]; + const TreeTraversalState::TraversalData& currentData = + pState->_currentTraversal[size_t(currentIndex)]; + CESIUM_ASSERT(previousData.pNode == currentData.pNode); + if (previousData.state != currentData.state) { + // The root node has a different state in the two traversals, so this is + // our first difference. + return; + } else { + // The root node has the same state in both traversals. Advanced to the + // next difference. + this->advanceToNextDifference(); + } + } else if (validPreviousIndex) { + // There are no nodes at all in the current traversal, so all previous + // states are differences. + this->_skippingPrevious = true; + this->_nextSiblingIndex = int64_t(pState->_previousTraversal.size()); + } else if (validCurrentIndex) { + // There were no nodes at all in the previous traversal, so all current + // states are differences. + this->_skippingPrevious = false; + this->_nextSiblingIndex = int64_t(pState->_currentTraversal.size()); + } + + // If none of the conditions above are met, then either both traversals are + // empty and there are no differences, or this is the `end` iterator. +} + +template +void TreeTraversalStateDiffIterator:: + advanceToNextDifference() noexcept { + bool first = true; + + if (this->_nextSiblingIndex >= 0) { + // Currently enumerating descendants that were not visited at all in the + // other traversal. + if (this->_skippingPrevious) { + ++this->_previousIndex; + if (this->_previousIndex < this->_nextSiblingIndex) { + return; + } + } else { + ++this->_currentIndex; + if (this->_currentIndex < this->_nextSiblingIndex) { + return; + } + } + + // If we don't return above, then the unmatched descendant enumeration is + // now complete. + this->_nextSiblingIndex = -1; + first = false; + } + + while (this->_previousIndex < + int64_t(this->_pState->_previousTraversal.size()) && + this->_currentIndex < + int64_t(this->_pState->_currentTraversal.size())) { + const TreeTraversalState::TraversalData& + previousData = + this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + const TreeTraversalState::TraversalData& currentData = + this->_pState->_currentTraversal[size_t(this->_currentIndex)]; + + CESIUM_ASSERT(previousData.pNode == currentData.pNode); + if (previousData.pNode != currentData.pNode) { + // This shouldn't happen. Stop the iteration by setting this iterator + // equal to the end() iterator. + this->_previousIndex = int64_t(this->_pState->_previousTraversal.size()); + this->_currentIndex = int64_t(this->_pState->_currentTraversal.size()); + return; + } + + if (!first && previousData.state != currentData.state) { + // Current node has a different state in the two traversals. + return; + } + + first = false; + + bool previousTraversalVisitedChildren = + previousData.nextSiblingIndex > this->_previousIndex + 1; + bool currentTraversalVisitedChildren = + currentData.nextSiblingIndex > this->_currentIndex + 1; + + ++this->_previousIndex; + ++this->_currentIndex; + + if (previousTraversalVisitedChildren && !currentTraversalVisitedChildren) { + // No descendants in current traversal, so every previous traversal + // descendant is a difference. + this->_skippingPrevious = true; + this->_nextSiblingIndex = previousData.nextSiblingIndex; + return; + } else if ( + currentTraversalVisitedChildren && !previousTraversalVisitedChildren) { + // No descendants in previous traversal, so every current traversal + // descendant is a difference. + this->_skippingPrevious = false; + this->_nextSiblingIndex = currentData.nextSiblingIndex; + return; + } + } + + // If we get here without returning, we're done iterating. We should be at the + // end of both traversals. + CESIUM_ASSERT( + this->_previousIndex == + int64_t(this->_pState->_previousTraversal.size())); + CESIUM_ASSERT( + this->_currentIndex == int64_t(this->_pState->_currentTraversal.size())); +} + } // namespace CesiumUtility diff --git a/CesiumUtility/test/TestTreeTraversalState.cpp b/CesiumUtility/test/TestTreeTraversalState.cpp index 063a86bcd..05a2754d4 100644 --- a/CesiumUtility/test/TestTreeTraversalState.cpp +++ b/CesiumUtility/test/TestTreeTraversalState.cpp @@ -15,10 +15,14 @@ struct Node { std::vector> getDifferences(const TreeTraversalState& traversalState) { std::vector> differences; - traversalState.diff( - [&differences](Node* pNode, int previousState, int currentState) { - differences.emplace_back(pNode, previousState, currentState); - }); + for (auto it = traversalState.beginDiff(); it != traversalState.endDiff(); + ++it) { + differences.emplace_back(it->pNode, it->previousState, it->currentState); + } + // traversalState.diff( + // [&differences](Node* pNode, int previousState, int currentState) { + // differences.emplace_back(pNode, previousState, currentState); + // }); return differences; } From 6578074e8fcf41f5da5e19209539a2b7b2dd67b5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 14 May 2025 12:27:17 +1000 Subject: [PATCH 08/18] Cleanup and doc. --- .../CesiumUtility/TreeTraversalState.h | 359 +++++++++--------- CesiumUtility/test/TestTreeTraversalState.cpp | 14 +- 2 files changed, 187 insertions(+), 186 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 5bdbc626b..fd01936a9 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -9,93 +9,6 @@ namespace CesiumUtility { -template -struct TreeTraversalStateDifference { - const TNodePointer& pNode; - const TState& previousState; - const TState& currentState; - - TreeTraversalStateDifference* operator->() { return this; } - const TreeTraversalStateDifference* operator->() const { return this; } -}; - -template class TreeTraversalState; - -template -class TreeTraversalStateDiffIterator { -public: - /** - * @brief The iterator category tag denoting this is a forward iterator. - */ - using iterator_category = std::forward_iterator_tag; - /** - * @brief The type of value that is being iterated over. - */ - using value_type = TreeTraversalStateDifference; - /** - * @brief The type used to identify distance between iterators. - * - * This is `void` because there is no meaningful measure of distance between - * tiles. - */ - using difference_type = void; - /** - * @brief A pointer to the type being iterated over. - */ - using pointer = const value_type*; - /** - * @brief A reference to the type being iterated over. - */ - using reference = const value_type&; - - /** - * @brief Returns a reference to the current difference being iterated. - */ - value_type operator*() const noexcept; - /** - * @brief Returns a pointer to the current difference being iterated. - */ - value_type operator->() const noexcept; - - /** - * @brief Advances the iterator to the next difference (pre-incrementing). - */ - TreeTraversalStateDiffIterator& operator++() noexcept; - /** - * @brief Advances the iterator to the next difference (post-incrementing). - */ - TreeTraversalStateDiffIterator operator++(int) noexcept; - - /** @brief Checks if two iterators are at the same difference. */ - bool operator==(const TreeTraversalStateDiffIterator& rhs) const noexcept; - /** @brief Checks if two iterators are not at the same difference. */ - bool operator!=(const TreeTraversalStateDiffIterator& rhs) const noexcept; - -private: - explicit TreeTraversalStateDiffIterator( - const TreeTraversalState* pState, - int64_t previousIndex, - int64_t currentIndex) noexcept; - void advanceToNextDifference() noexcept; - - const TreeTraversalState* _pState; - // The index of the current difference in _previousTraversal. - int64_t _previousIndex; - // The index of the current difference in _currentTraversal. - int64_t _currentIndex; - // The next sibling index if we're currently skipping siblings. -1 if we're - // not currently skipping siblings. - int64_t _nextSiblingIndex; - // If we're skipping siblings, this value is true when the skipped siblings - // are from the previous traversal, or false if the skipped siblings are from - // the current traversal. - bool _skippingPrevious; - - static const inline TState DEFAULT_STATE{}; - - friend class TreeTraversalState; -}; - /** * @brief Associates state (arbitrary data) with each node during partial, * depth-first traversal of a tree. Then, during a later traversal of a @@ -401,99 +314,185 @@ template class TreeTraversalState { return this->slowlyGetStates(this->_previousTraversal); } - using difference_iterator = - TreeTraversalStateDiffIterator; +#pragma region Differences - difference_iterator beginDiff() const noexcept { - return difference_iterator(this, 0, 0); - } + /** + * @brief Represents a single difference reported by {@link differences}. + */ + struct Difference { + /** + * @brief The node with a different state. + */ + const TNodePointer& pNode; + + /** + * @brief The state of the node in the previous traversal, or a + * default-constructed instance if the node was not visited at all in the + * previous traversal. + */ + const TState& previousState; + + /** + * @brief The state of the node in the current traversal, or a + * default-constructed instance if the node was not visited at all in the + * current traversal. + */ + const TState& currentState; + + // These operators allow a `Difference` instance to be returned by value + // from the difference_iterator's `operator->` method. + + /** @private */ + Difference* operator->() { return this; } + + /** @private */ + const Difference* operator->() const { return this; } + }; - difference_iterator endDiff() const noexcept { - return difference_iterator( - this, - int64_t(this->_previousTraversal.size()), - int64_t(this->_currentTraversal.size())); - } + /** + * @brief The type of the iterator created by {@link Differences}. + */ + class difference_iterator { + public: + /** + * @brief The iterator category tag denoting this is a forward iterator. + */ + using iterator_category = std::forward_iterator_tag; + /** + * @brief The type of value that is being iterated over. + */ + using value_type = Difference; + /** + * @brief The type used to identify distance between iterators. + * + * This is `void` because there is no meaningful measure of distance between + * tiles. + */ + using difference_type = void; + /** + * @brief A pointer to the type being iterated over. + */ + using pointer = const value_type*; + /** + * @brief A reference to the type being iterated over. + */ + using reference = const value_type&; + + /** + * @brief Returns a reference to the current difference being iterated. + */ + value_type operator*() const noexcept; + /** + * @brief Returns a pointer to the current difference being iterated. + */ + value_type operator->() const noexcept; + + /** + * @brief Advances the iterator to the next difference (pre-incrementing). + */ + difference_iterator& operator++() noexcept; + /** + * @brief Advances the iterator to the next difference (post-incrementing). + */ + difference_iterator operator++(int) noexcept; + + /** @brief Checks if two iterators are at the same difference. */ + bool operator==(const difference_iterator& rhs) const noexcept; + /** @brief Checks if two iterators are not at the same difference. */ + bool operator!=(const difference_iterator& rhs) const noexcept; + + private: + explicit difference_iterator( + const TreeTraversalState* pState, + int64_t previousIndex, + int64_t currentIndex) noexcept; + void advanceToNextDifference() noexcept; + + const TreeTraversalState* _pState; + // The index of the current difference in _previousTraversal. + int64_t _previousIndex; + // The index of the current difference in _currentTraversal. + int64_t _currentIndex; + // The next sibling index if we're currently skipping siblings. -1 if we're + // not currently skipping siblings. + int64_t _nextSiblingIndex; + // If we're skipping siblings, this value is true when the skipped siblings + // are from the previous traversal, or false if the skipped siblings are + // from the current traversal. + bool _skippingPrevious; + + static const inline TState DEFAULT_STATE{}; + + friend class TreeTraversalState; + }; /** - * @brief Compares the current traversal against the previous one, invoking a - * callback for each case where a node had a different state in the two + * @brief Returned by the {@link differences} method to allow iteration over + * the differences between two traversals of the same tree. + */ + class Differences { + public: + /** + * @brief Gets an iterator pointing to the first difference. + */ + difference_iterator begin() const noexcept { + return difference_iterator(this->_pState, 0, 0); + } + + /** + * @brief Gets an iterator pointing to one past the last difference. + */ + difference_iterator end() const noexcept { + return difference_iterator( + this->_pState, + int64_t(this->_previousTraversalSize), + int64_t(this->_currentTraversalSize)); + } + + private: + Differences( + const TreeTraversalState& traversalState, + size_t previousTraversalSize, + size_t currentTraversalSize) noexcept + : _pState(&traversalState), + _previousTraversalSize(previousTraversalSize), + _currentTraversalSize(currentTraversalSize) {} + + const TreeTraversalState* _pState; + size_t _previousTraversalSize; + size_t _currentTraversalSize; + + friend class TreeTraversalState; + }; + + /** + * @brief Compares the current traversal against the previous one. Provides an + * iterator over all of the nodes that had a different state in the two * traversals. * - * The callback is also invoked: + * The iteration also includes: * - * * For each node that was visited previously but was not visited in the - * current traversal. In this case, the current state provided to the callback - * is a default-constructed `TState` instance. - * * For each node that was not visited previously but was visited in the - * current traversal. In this case, the previous state provided to the - * callback is a default-constructed `TState` instance. + * * Each node that was visited previously but was not visited in the + * current traversal. + * * Each node that was not visited previously but was visited in the + * current traversal. * * This method should only be called after the {@link finishNode} for the * root node, and before {@link beginTraversal}. In other words, it should * not be called while a traversal is in progress. - * - * @tparam TCallback The type of the callback. - * @param callback The callback to be invoked for each difference. The - * function is given the node pointer, the old state, and the new state, in - * that order. */ - template void diff(TCallback&& callback) const { + Differences differences() const noexcept { + // Assert that a traversal is not currently in progress. CESIUM_ASSERT(this->_parentIndices.empty()); - int64_t previousIndex = 0; - int64_t currentIndex = 0; - - while (previousIndex < int64_t(this->_previousTraversal.size()) && - currentIndex < int64_t(this->_currentTraversal.size())) { - const TraversalData& previousData = - this->_previousTraversal[size_t(previousIndex)]; - const TraversalData& currentData = - this->_currentTraversal[size_t(currentIndex)]; - - CESIUM_ASSERT(previousData.pNode == currentData.pNode); - if (previousData.pNode != currentData.pNode) { - // This shouldn't happen. - break; - } - - if (previousData.state != currentData.state) { - // Different state - report it - callback(previousData.pNode, previousData.state, currentData.state); - } - - bool previousTraversalVisitedChildren = - previousData.nextSiblingIndex > previousIndex + 1; - bool currentTraversalVisitedChildren = - currentData.nextSiblingIndex > currentIndex + 1; - - ++previousIndex; - ++currentIndex; - - if (previousTraversalVisitedChildren && - !currentTraversalVisitedChildren) { - while (previousIndex < previousData.nextSiblingIndex) { - const TraversalData& skipped = - this->_previousTraversal[size_t(previousIndex)]; - callback(skipped.pNode, skipped.state, TState()); - ++previousIndex; - } - } else if ( - currentTraversalVisitedChildren && - !previousTraversalVisitedChildren) { - while (currentIndex < currentData.nextSiblingIndex) { - const TraversalData& skipped = - this->_currentTraversal[size_t(currentIndex)]; - callback(skipped.pNode, TState(), skipped.state); - ++currentIndex; - } - } - } - - CESIUM_ASSERT(previousIndex == int64_t(this->_previousTraversal.size())); - CESIUM_ASSERT(currentIndex == int64_t(this->_currentTraversal.size())); + return Differences{ + *this, + this->_previousTraversal.size(), + this->_currentTraversal.size()}; } +#pragma endregion + private: struct TraversalData { TNodePointer pNode; @@ -588,8 +587,8 @@ template class TreeTraversalState { }; template -TreeTraversalStateDiffIterator::value_type -TreeTraversalStateDiffIterator::operator*() +TreeTraversalState::Difference +TreeTraversalState::difference_iterator::operator*() const noexcept { if (this->_nextSiblingIndex >= 0) { if (this->_skippingPrevious) { @@ -642,43 +641,45 @@ TreeTraversalStateDiffIterator::operator*() } template -TreeTraversalStateDiffIterator::value_type -TreeTraversalStateDiffIterator::operator->() +TreeTraversalState::Difference +TreeTraversalState::difference_iterator::operator->() const noexcept { return this->operator*(); } template -TreeTraversalStateDiffIterator& -TreeTraversalStateDiffIterator::operator++() noexcept { +TreeTraversalState::difference_iterator& +TreeTraversalState::difference_iterator:: +operator++() noexcept { this->advanceToNextDifference(); return *this; } template -TreeTraversalStateDiffIterator -TreeTraversalStateDiffIterator::operator++(int) noexcept { +TreeTraversalState::difference_iterator +TreeTraversalState::difference_iterator::operator++( + int) noexcept { TreeTraversalStateDiffIterator result = *this; return ++result; } template -bool TreeTraversalStateDiffIterator::operator==( - const TreeTraversalStateDiffIterator& rhs) const noexcept { +bool TreeTraversalState::difference_iterator::operator==( + const TreeTraversalState::difference_iterator& rhs) const noexcept { return this->_previousIndex == rhs._previousIndex && this->_currentIndex == rhs._currentIndex && this->_pState == rhs._pState; } template -bool TreeTraversalStateDiffIterator::operator!=( - const TreeTraversalStateDiffIterator& rhs) const noexcept { +bool TreeTraversalState::difference_iterator::operator!=( + const TreeTraversalState::difference_iterator& rhs) const noexcept { return !(*this == rhs); } template -TreeTraversalStateDiffIterator:: - TreeTraversalStateDiffIterator( +TreeTraversalState::difference_iterator:: + difference_iterator( const TreeTraversalState* pState, int64_t previousIndex, int64_t currentIndex) noexcept @@ -723,7 +724,7 @@ TreeTraversalStateDiffIterator:: } template -void TreeTraversalStateDiffIterator:: +void TreeTraversalState::difference_iterator:: advanceToNextDifference() noexcept { bool first = true; diff --git a/CesiumUtility/test/TestTreeTraversalState.cpp b/CesiumUtility/test/TestTreeTraversalState.cpp index 05a2754d4..7aacfb27f 100644 --- a/CesiumUtility/test/TestTreeTraversalState.cpp +++ b/CesiumUtility/test/TestTreeTraversalState.cpp @@ -15,14 +15,14 @@ struct Node { std::vector> getDifferences(const TreeTraversalState& traversalState) { std::vector> differences; - for (auto it = traversalState.beginDiff(); it != traversalState.endDiff(); - ++it) { - differences.emplace_back(it->pNode, it->previousState, it->currentState); + + for (auto difference : traversalState.differences()) { + differences.emplace_back( + difference.pNode, + difference.previousState, + difference.currentState); } - // traversalState.diff( - // [&differences](Node* pNode, int previousState, int currentState) { - // differences.emplace_back(pNode, previousState, currentState); - // }); + return differences; } From d2492404972884a1cdac5567632d90810752dc14 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 14 May 2025 12:36:10 +1000 Subject: [PATCH 09/18] Tweak organization. --- .../CesiumUtility/TreeTraversalState.h | 63 +++++++++++-------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index fd01936a9..2de19ed50 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -314,7 +314,38 @@ template class TreeTraversalState { return this->slowlyGetStates(this->_previousTraversal); } -#pragma region Differences + class Differences; + + /** + * @brief Compares the current traversal against the previous one. Provides an + * iterator over all of the nodes that had a different state in the two + * traversals. + * + * The iteration also includes: + * + * * Each node that was visited previously but was not visited in the + * current traversal. + * * Each node that was not visited previously but was visited in the + * current traversal. + * + * This method should only be called after the {@link finishNode} for the + * root node, and before {@link beginTraversal}. In other words, it should + * not be called while a traversal is in progress. + * + * Starting a traversal after calling this method invalidates the returned + * instance. + */ + Differences differences() const noexcept { + // Assert that a traversal is not currently in progress. + CESIUM_ASSERT(this->_parentIndices.empty()); + + return Differences{ + *this, + this->_previousTraversal.size(), + this->_currentTraversal.size()}; + } + +#pragma region Differences Implementation /** * @brief Represents a single difference reported by {@link differences}. @@ -465,32 +496,6 @@ template class TreeTraversalState { friend class TreeTraversalState; }; - /** - * @brief Compares the current traversal against the previous one. Provides an - * iterator over all of the nodes that had a different state in the two - * traversals. - * - * The iteration also includes: - * - * * Each node that was visited previously but was not visited in the - * current traversal. - * * Each node that was not visited previously but was visited in the - * current traversal. - * - * This method should only be called after the {@link finishNode} for the - * root node, and before {@link beginTraversal}. In other words, it should - * not be called while a traversal is in progress. - */ - Differences differences() const noexcept { - // Assert that a traversal is not currently in progress. - CESIUM_ASSERT(this->_parentIndices.empty()); - - return Differences{ - *this, - this->_previousTraversal.size(), - this->_currentTraversal.size()}; - } - #pragma endregion private: @@ -586,6 +591,8 @@ template class TreeTraversalState { friend class TreeTraversalStateDiffIterator; }; +#pragma region Differences Implementation + template TreeTraversalState::Difference TreeTraversalState::difference_iterator::operator*() @@ -808,4 +815,6 @@ void TreeTraversalState::difference_iterator:: this->_currentIndex == int64_t(this->_pState->_currentTraversal.size())); } +#pragma endregion + } // namespace CesiumUtility From be79e34d83169b00764360f3959d8724263545e4 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 14 May 2025 13:28:34 +1000 Subject: [PATCH 10/18] More efficient fields for reporting differences. --- .../CesiumUtility/TreeTraversalState.h | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 2de19ed50..e4a6a3b3d 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -351,24 +351,47 @@ template class TreeTraversalState { * @brief Represents a single difference reported by {@link differences}. */ struct Difference { + /** + * @brief The type used to report the node with a difference. + * + * This will be a simple pointer if `TNodePointer` is a simple pointer. + * Otherwise, it will be a const reference to `TNodePointer`. + */ + using PointerStorageType = std::conditional_t< + std::is_pointer_v, + TNodePointer, + const TNodePointer&>; + + /** + * @brief The type used to report the previous and current states of the + * node. + * + * This will be a `TState` instance of `TState` is trivially copy + * constructible. Otherwise, it will be a const reference to the `TState`. + */ + using StateStorageType = std::conditional_t< + std::is_trivially_copy_constructible_v, + TState, + const TState&>; + /** * @brief The node with a different state. */ - const TNodePointer& pNode; + PointerStorageType pNode; /** * @brief The state of the node in the previous traversal, or a * default-constructed instance if the node was not visited at all in the * previous traversal. */ - const TState& previousState; + StateStorageType previousState; /** * @brief The state of the node in the current traversal, or a * default-constructed instance if the node was not visited at all in the * current traversal. */ - const TState& currentState; + StateStorageType currentState; // These operators allow a `Difference` instance to be returned by value // from the difference_iterator's `operator->` method. From 31677421c517bee10bc3a48aa033ced5ec47ab30 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 14 May 2025 14:03:15 +1000 Subject: [PATCH 11/18] More cleanup. --- .../CesiumUtility/TreeTraversalState.h | 126 ++++++++++-------- 1 file changed, 70 insertions(+), 56 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index e4a6a3b3d..58a1eac85 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -462,18 +462,30 @@ template class TreeTraversalState { int64_t currentIndex) noexcept; void advanceToNextDifference() noexcept; + // The instance for which we're comparing traversals. const TreeTraversalState* _pState; + // The index of the current difference in _previousTraversal. int64_t _previousIndex; + // The index of the current difference in _currentTraversal. int64_t _currentIndex; - // The next sibling index if we're currently skipping siblings. -1 if we're - // not currently skipping siblings. + + // The type of difference we're currently enumerating. + enum class DifferenceType { + // A node that was visited in both traversals, but with a different state + // in each. + StateChange, + // A node that was only visited in the previous traversal. + NodeOnlyInPrevious, + // A node that was only visited in the current traversal. + NodeOnlyInCurrent + } _differenceType; + + // If the _differenceType is NodeOnlyInPrevious or NodeOnlyInCurrent, this + // field is set to the index of the next node that is expected to be in both + // traversals again. int64_t _nextSiblingIndex; - // If we're skipping siblings, this value is true when the skipped siblings - // are from the previous traversal, or false if the skipped siblings are - // from the current traversal. - bool _skippingPrevious; static const inline TState DEFAULT_STATE{}; @@ -620,33 +632,7 @@ template TreeTraversalState::Difference TreeTraversalState::difference_iterator::operator*() const noexcept { - if (this->_nextSiblingIndex >= 0) { - if (this->_skippingPrevious) { - CESIUM_ASSERT( - this->_previousIndex >= 0 && - size_t(this->_previousIndex) < - this->_pState->_previousTraversal.size()); - - const TreeTraversalState::TraversalData& data = - this->_pState->_previousTraversal[size_t(this->_previousIndex)]; - return value_type{ - .pNode = data.pNode, - .previousState = data.state, - .currentState = DEFAULT_STATE}; - } else { - CESIUM_ASSERT( - this->_currentIndex >= 0 && - size_t(this->_currentIndex) < - this->_pState->_currentTraversal.size()); - - const TreeTraversalState::TraversalData& data = - this->_pState->_currentTraversal[size_t(this->_currentIndex)]; - return value_type{ - .pNode = data.pNode, - .previousState = DEFAULT_STATE, - .currentState = data.state}; - } - } else { + if (this->_differenceType == DifferenceType::StateChange) { CESIUM_ASSERT( this->_previousIndex >= 0 && size_t(this->_previousIndex) < @@ -667,6 +653,29 @@ TreeTraversalState::difference_iterator::operator*() .pNode = previousData.pNode, .previousState = previousData.state, .currentState = currentData.state}; + } else if (this->_differenceType == DifferenceType::NodeOnlyInPrevious) { + CESIUM_ASSERT( + this->_previousIndex >= 0 && + size_t(this->_previousIndex) < + this->_pState->_previousTraversal.size()); + + const TreeTraversalState::TraversalData& data = + this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + return value_type{ + .pNode = data.pNode, + .previousState = data.state, + .currentState = DEFAULT_STATE}; + } else { + CESIUM_ASSERT( + this->_currentIndex >= 0 && + size_t(this->_currentIndex) < this->_pState->_currentTraversal.size()); + + const TreeTraversalState::TraversalData& data = + this->_pState->_currentTraversal[size_t(this->_currentIndex)]; + return value_type{ + .pNode = data.pNode, + .previousState = DEFAULT_STATE, + .currentState = data.state}; } } @@ -716,8 +725,8 @@ TreeTraversalState::difference_iterator:: : _pState(pState), _previousIndex(previousIndex), _currentIndex(currentIndex), - _nextSiblingIndex(-1), - _skippingPrevious(false) { + _differenceType(DifferenceType::StateChange), + _nextSiblingIndex(-1) { bool validPreviousIndex = previousIndex < int64_t(pState->_previousTraversal.size()); bool validCurrentIndex = @@ -740,12 +749,12 @@ TreeTraversalState::difference_iterator:: } else if (validPreviousIndex) { // There are no nodes at all in the current traversal, so all previous // states are differences. - this->_skippingPrevious = true; + this->_differenceType = DifferenceType::NodeOnlyInPrevious; this->_nextSiblingIndex = int64_t(pState->_previousTraversal.size()); } else if (validCurrentIndex) { // There were no nodes at all in the previous traversal, so all current // states are differences. - this->_skippingPrevious = false; + this->_differenceType = DifferenceType::NodeOnlyInCurrent; this->_nextSiblingIndex = int64_t(pState->_currentTraversal.size()); } @@ -758,25 +767,30 @@ void TreeTraversalState::difference_iterator:: advanceToNextDifference() noexcept { bool first = true; - if (this->_nextSiblingIndex >= 0) { - // Currently enumerating descendants that were not visited at all in the - // other traversal. - if (this->_skippingPrevious) { - ++this->_previousIndex; - if (this->_previousIndex < this->_nextSiblingIndex) { - return; - } + if (this->_differenceType == DifferenceType::NodeOnlyInPrevious) { + ++this->_previousIndex; + if (this->_previousIndex < this->_nextSiblingIndex) { + // Enumerating another previous node that doesn't exist in the current + // traversal. + return; } else { - ++this->_currentIndex; - if (this->_currentIndex < this->_nextSiblingIndex) { - return; - } + // We reached the end of the previous nodes that don't exist in the + // current traversal. + this->_differenceType = DifferenceType::StateChange; + first = false; + } + } else if (this->_differenceType == DifferenceType::NodeOnlyInCurrent) { + ++this->_currentIndex; + if (this->_currentIndex < this->_nextSiblingIndex) { + // Enumerating another current node that doesn't exist in the previous + // traversal. + return; + } else { + // We reached the end of the current nodes that don't exist in the + // previous traversal. + this->_differenceType = DifferenceType::StateChange; + first = false; } - - // If we don't return above, then the unmatched descendant enumeration is - // now complete. - this->_nextSiblingIndex = -1; - first = false; } while (this->_previousIndex < @@ -816,14 +830,14 @@ void TreeTraversalState::difference_iterator:: if (previousTraversalVisitedChildren && !currentTraversalVisitedChildren) { // No descendants in current traversal, so every previous traversal // descendant is a difference. - this->_skippingPrevious = true; + this->_differenceType = DifferenceType::NodeOnlyInPrevious; this->_nextSiblingIndex = previousData.nextSiblingIndex; return; } else if ( currentTraversalVisitedChildren && !previousTraversalVisitedChildren) { // No descendants in previous traversal, so every current traversal // descendant is a difference. - this->_skippingPrevious = false; + this->_differenceType = DifferenceType::NodeOnlyInCurrent; this->_nextSiblingIndex = currentData.nextSiblingIndex; return; } From 093cc5ad0644a5a167f19353d79571d4e91354f1 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 14 May 2025 16:49:37 +1000 Subject: [PATCH 12/18] Fix compiler error on older MSVC. --- CesiumUtility/include/CesiumUtility/TreeTraversalState.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 58a1eac85..e72a1abc2 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -687,7 +687,7 @@ TreeTraversalState::difference_iterator::operator->() } template -TreeTraversalState::difference_iterator& +typename TreeTraversalState::difference_iterator& TreeTraversalState::difference_iterator:: operator++() noexcept { this->advanceToNextDifference(); From af42e04da950e32cc99ace9be41da5453b14e481 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 14 May 2025 17:33:28 +1000 Subject: [PATCH 13/18] Add descendantsEnd property. --- .../CesiumUtility/TreeTraversalState.h | 91 ++++++++++++++----- 1 file changed, 66 insertions(+), 25 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index e72a1abc2..6c094979b 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -328,6 +328,9 @@ template class TreeTraversalState { * * Each node that was not visited previously but was visited in the * current traversal. * + * Nodes are iterated depth-first, pre-order, so differences in a parent node + * are provided before any differences in children. + * * This method should only be called after the {@link finishNode} for the * root node, and before {@link beginTraversal}. In other words, it should * not be called while a traversal is in progress. @@ -347,6 +350,8 @@ template class TreeTraversalState { #pragma region Differences Implementation + class difference_iterator; + /** * @brief Represents a single difference reported by {@link differences}. */ @@ -393,6 +398,16 @@ template class TreeTraversalState { */ StateStorageType currentState; + /** + * @brief The "end" iterator for the descendants of the current node. + * + * This can be used to skip differences in this node's descendant nodes, or + * to otherwise treat them specially. It refers to one difference past the + * last difference for any descendants of this tile, in the same way that a + * normal "end" iterator is after the last element in a collection. + */ + difference_iterator descendantsEnd; + // These operators allow a `Difference` instance to be returned by value // from the difference_iterator's `operator->` method. @@ -456,7 +471,16 @@ template class TreeTraversalState { bool operator!=(const difference_iterator& rhs) const noexcept; private: + /** + * @brief Creates the "begin" iterator for this state. + */ explicit difference_iterator( + const TreeTraversalState* pState) noexcept; + + /** + * @brief Creates an iterator at a specific position, which must be valid. + */ + difference_iterator( const TreeTraversalState* pState, int64_t previousIndex, int64_t currentIndex) noexcept; @@ -502,7 +526,7 @@ template class TreeTraversalState { * @brief Gets an iterator pointing to the first difference. */ difference_iterator begin() const noexcept { - return difference_iterator(this->_pState, 0, 0); + return difference_iterator(this->_pState); } /** @@ -652,7 +676,11 @@ TreeTraversalState::difference_iterator::operator*() return value_type{ .pNode = previousData.pNode, .previousState = previousData.state, - .currentState = currentData.state}; + .currentState = currentData.state, + .descendantsEnd = difference_iterator( + this->_pState, + previousData.nextSiblingIndex, + currentData.nextSiblingIndex)}; } else if (this->_differenceType == DifferenceType::NodeOnlyInPrevious) { CESIUM_ASSERT( this->_previousIndex >= 0 && @@ -664,7 +692,11 @@ TreeTraversalState::difference_iterator::operator*() return value_type{ .pNode = data.pNode, .previousState = data.state, - .currentState = DEFAULT_STATE}; + .currentState = DEFAULT_STATE, + .descendantsEnd = difference_iterator( + this->_pState, + data.nextSiblingIndex, + this->_currentIndex)}; } else { CESIUM_ASSERT( this->_currentIndex >= 0 && @@ -675,7 +707,11 @@ TreeTraversalState::difference_iterator::operator*() return value_type{ .pNode = data.pNode, .previousState = DEFAULT_STATE, - .currentState = data.state}; + .currentState = data.state, + .descendantsEnd = difference_iterator( + this->_pState, + this->_previousIndex, + data.nextSiblingIndex)}; } } @@ -719,49 +755,54 @@ bool TreeTraversalState::difference_iterator::operator!=( template TreeTraversalState::difference_iterator:: difference_iterator( - const TreeTraversalState* pState, - int64_t previousIndex, - int64_t currentIndex) noexcept - : _pState(pState), - _previousIndex(previousIndex), - _currentIndex(currentIndex), - _differenceType(DifferenceType::StateChange), - _nextSiblingIndex(-1) { - bool validPreviousIndex = - previousIndex < int64_t(pState->_previousTraversal.size()); - bool validCurrentIndex = - currentIndex < int64_t(pState->_currentTraversal.size()); - if (validPreviousIndex && validCurrentIndex) { + const TreeTraversalState* pState) noexcept + : difference_iterator(pState, 0, 0) { + // The iterator starts at the root tile in both traversals. But either + // traversal could be empty, or the root tile may not be a difference. + bool hasPreviousTraversal = !pState->_previousTraversal.empty(); + bool hasCurrentTraversal = !pState->_currentTraversal.empty(); + + if (hasPreviousTraversal && hasCurrentTraversal) { const TreeTraversalState::TraversalData& - previousData = pState->_previousTraversal[size_t(previousIndex)]; + previousData = pState->_previousTraversal.front(); const TreeTraversalState::TraversalData& currentData = - pState->_currentTraversal[size_t(currentIndex)]; + pState->_currentTraversal.front(); CESIUM_ASSERT(previousData.pNode == currentData.pNode); if (previousData.state != currentData.state) { // The root node has a different state in the two traversals, so this is // our first difference. - return; } else { // The root node has the same state in both traversals. Advanced to the // next difference. this->advanceToNextDifference(); } - } else if (validPreviousIndex) { + } else if (hasPreviousTraversal) { // There are no nodes at all in the current traversal, so all previous // states are differences. this->_differenceType = DifferenceType::NodeOnlyInPrevious; this->_nextSiblingIndex = int64_t(pState->_previousTraversal.size()); - } else if (validCurrentIndex) { + } else if (hasCurrentTraversal) { // There were no nodes at all in the previous traversal, so all current // states are differences. this->_differenceType = DifferenceType::NodeOnlyInCurrent; this->_nextSiblingIndex = int64_t(pState->_currentTraversal.size()); + } else { + // Both traversals are empty, so there are no differences. } - - // If none of the conditions above are met, then either both traversals are - // empty and there are no differences, or this is the `end` iterator. } +template +TreeTraversalState::difference_iterator:: + difference_iterator( + const TreeTraversalState* pState, + int64_t previousIndex, + int64_t currentIndex) noexcept + : _pState(pState), + _previousIndex(previousIndex), + _currentIndex(currentIndex), + _differenceType(DifferenceType::StateChange), + _nextSiblingIndex(-1) {} + template void TreeTraversalState::difference_iterator:: advanceToNextDifference() noexcept { From 4446f6b88a7d9ef49b0a64b2dcced24f9bd08d56 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 14 May 2025 20:02:36 +1000 Subject: [PATCH 14/18] Better, but still slightly dodgy, descendantsBegin implementation. --- .../CesiumUtility/TreeTraversalState.h | 209 ++++++++++++++---- CesiumUtility/test/TestTreeTraversalState.cpp | 11 +- 2 files changed, 167 insertions(+), 53 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 6c094979b..20f839640 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -398,16 +398,6 @@ template class TreeTraversalState { */ StateStorageType currentState; - /** - * @brief The "end" iterator for the descendants of the current node. - * - * This can be used to skip differences in this node's descendant nodes, or - * to otherwise treat them specially. It refers to one difference past the - * last difference for any descendants of this tile, in the same way that a - * normal "end" iterator is after the last element in a collection. - */ - difference_iterator descendantsEnd; - // These operators allow a `Difference` instance to be returned by value // from the difference_iterator's `operator->` method. @@ -465,6 +455,16 @@ template class TreeTraversalState { */ difference_iterator operator++(int) noexcept; + /** + * @brief Gets the "end" iterator for the descendants of the current node. + * + * This can be used to skip differences in this node's descendant nodes, or + * to otherwise treat them specially. It refers to one difference past the + * last difference for any descendants of this tile, in the same way that a + * normal "end" iterator is after the last element in a collection. + */ + difference_iterator descendantsEnd(); + /** @brief Checks if two iterators are at the same difference. */ bool operator==(const difference_iterator& rhs) const noexcept; /** @brief Checks if two iterators are not at the same difference. */ @@ -484,6 +484,22 @@ template class TreeTraversalState { const TreeTraversalState* pState, int64_t previousIndex, int64_t currentIndex) noexcept; + + /** + * @brief While the iterator doesn't point to an actual difference, and + * isn't at the end, move it forward. + * + * If the iterator already points to a valid difference, this method does + * nothing. + */ + void advanceToCurrentDifference() noexcept; + + /** + * @brief Moves the current iterator to the next difference after the + * current one. Unlike {@link advanceToCurrentDifference}, this method + * always moves the iterator. Calling this on an iterator that is already at + * the end is undefined behavior. + */ void advanceToNextDifference() noexcept; // The instance for which we're comparing traversals. @@ -676,11 +692,7 @@ TreeTraversalState::difference_iterator::operator*() return value_type{ .pNode = previousData.pNode, .previousState = previousData.state, - .currentState = currentData.state, - .descendantsEnd = difference_iterator( - this->_pState, - previousData.nextSiblingIndex, - currentData.nextSiblingIndex)}; + .currentState = currentData.state}; } else if (this->_differenceType == DifferenceType::NodeOnlyInPrevious) { CESIUM_ASSERT( this->_previousIndex >= 0 && @@ -689,14 +701,11 @@ TreeTraversalState::difference_iterator::operator*() const TreeTraversalState::TraversalData& data = this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + return value_type{ .pNode = data.pNode, .previousState = data.state, - .currentState = DEFAULT_STATE, - .descendantsEnd = difference_iterator( - this->_pState, - data.nextSiblingIndex, - this->_currentIndex)}; + .currentState = DEFAULT_STATE}; } else { CESIUM_ASSERT( this->_currentIndex >= 0 && @@ -704,14 +713,11 @@ TreeTraversalState::difference_iterator::operator*() const TreeTraversalState::TraversalData& data = this->_pState->_currentTraversal[size_t(this->_currentIndex)]; + return value_type{ .pNode = data.pNode, .previousState = DEFAULT_STATE, - .currentState = data.state, - .descendantsEnd = difference_iterator( - this->_pState, - this->_previousIndex, - data.nextSiblingIndex)}; + .currentState = data.state}; } } @@ -738,6 +744,63 @@ TreeTraversalState::difference_iterator::operator++( return ++result; } +template +TreeTraversalState::difference_iterator +TreeTraversalState::difference_iterator:: + descendantsEnd() { + if (this->_differenceType == DifferenceType::StateChange) { + const TreeTraversalState::TraversalData& + previousData = + this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + const TreeTraversalState::TraversalData& currentData = + this->_pState->_currentTraversal[size_t(this->_currentIndex)]; + + difference_iterator result( + this->_pState, + previousData.nextSiblingIndex, + currentData.nextSiblingIndex); + + result.advanceToCurrentDifference(); + + return result; + } else if (this->_differenceType == DifferenceType::NodeOnlyInPrevious) { + const TreeTraversalState::TraversalData& + previousData = + this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + + difference_iterator result = difference_iterator( + this->_pState, + previousData.nextSiblingIndex, + this->_currentIndex); + result._differenceType = + previousData.nextSiblingIndex == this->_nextSiblingIndex + ? DifferenceType::StateChange + : DifferenceType::NodeOnlyInPrevious; + result._nextSiblingIndex = this->_nextSiblingIndex; + + result.advanceToCurrentDifference(); + + return result; + } else { + const TreeTraversalState::TraversalData& currentData = + this->_pState->_currentTraversal[size_t(this->_currentIndex)]; + + difference_iterator result = difference_iterator( + this->_pState, + this->_previousIndex, + currentData.nextSiblingIndex); + result._differenceType = + currentData.nextSiblingIndex == this->_nextSiblingIndex + ? DifferenceType::StateChange + : DifferenceType::NodeOnlyInCurrent; + result._nextSiblingIndex = this->_nextSiblingIndex; + + result.advanceToCurrentDifference(); + + return result; + } +} + template bool TreeTraversalState::difference_iterator::operator==( const TreeTraversalState::difference_iterator& rhs) const noexcept { @@ -803,10 +866,76 @@ TreeTraversalState::difference_iterator:: _differenceType(DifferenceType::StateChange), _nextSiblingIndex(-1) {} +template +void TreeTraversalState::difference_iterator:: + advanceToCurrentDifference() noexcept { + // Nodes only visited in the previous or current traversal always represent a + // difference. + if (this->_differenceType != DifferenceType::StateChange) { + return; + } + + while (this->_previousIndex < + int64_t(this->_pState->_previousTraversal.size()) && + this->_currentIndex < + int64_t(this->_pState->_currentTraversal.size())) { + const TreeTraversalState::TraversalData& + previousData = + this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + const TreeTraversalState::TraversalData& currentData = + this->_pState->_currentTraversal[size_t(this->_currentIndex)]; + + CESIUM_ASSERT(previousData.pNode == currentData.pNode); + if (previousData.pNode != currentData.pNode) { + // This shouldn't happen. Stop the iteration by setting this iterator + // equal to the end() iterator. + this->_previousIndex = int64_t(this->_pState->_previousTraversal.size()); + this->_currentIndex = int64_t(this->_pState->_currentTraversal.size()); + return; + } + + if (previousData.state != currentData.state) { + // Current node has a different state in the two traversals. + return; + } + + bool previousTraversalVisitedChildren = + previousData.nextSiblingIndex > this->_previousIndex + 1; + bool currentTraversalVisitedChildren = + currentData.nextSiblingIndex > this->_currentIndex + 1; + + ++this->_previousIndex; + ++this->_currentIndex; + + if (previousTraversalVisitedChildren && !currentTraversalVisitedChildren) { + // No descendants in current traversal, so every previous traversal + // descendant is a difference. + this->_differenceType = DifferenceType::NodeOnlyInPrevious; + this->_nextSiblingIndex = previousData.nextSiblingIndex; + return; + } else if ( + currentTraversalVisitedChildren && !previousTraversalVisitedChildren) { + // No descendants in previous traversal, so every current traversal + // descendant is a difference. + this->_differenceType = DifferenceType::NodeOnlyInCurrent; + this->_nextSiblingIndex = currentData.nextSiblingIndex; + return; + } + } + + // If we get here without returning, we're done iterating. We should be at the + // end of both traversals. + CESIUM_ASSERT( + this->_previousIndex == + int64_t(this->_pState->_previousTraversal.size())); + CESIUM_ASSERT( + this->_currentIndex == int64_t(this->_pState->_currentTraversal.size())); +} + template void TreeTraversalState::difference_iterator:: advanceToNextDifference() noexcept { - bool first = true; + // bool first = true; if (this->_differenceType == DifferenceType::NodeOnlyInPrevious) { ++this->_previousIndex; @@ -818,7 +947,7 @@ void TreeTraversalState::difference_iterator:: // We reached the end of the previous nodes that don't exist in the // current traversal. this->_differenceType = DifferenceType::StateChange; - first = false; + this->advanceToCurrentDifference(); } } else if (this->_differenceType == DifferenceType::NodeOnlyInCurrent) { ++this->_currentIndex; @@ -830,14 +959,9 @@ void TreeTraversalState::difference_iterator:: // We reached the end of the current nodes that don't exist in the // previous traversal. this->_differenceType = DifferenceType::StateChange; - first = false; + this->advanceToCurrentDifference(); } - } - - while (this->_previousIndex < - int64_t(this->_pState->_previousTraversal.size()) && - this->_currentIndex < - int64_t(this->_pState->_currentTraversal.size())) { + } else { const TreeTraversalState::TraversalData& previousData = this->_pState->_previousTraversal[size_t(this->_previousIndex)]; @@ -853,13 +977,6 @@ void TreeTraversalState::difference_iterator:: return; } - if (!first && previousData.state != currentData.state) { - // Current node has a different state in the two traversals. - return; - } - - first = false; - bool previousTraversalVisitedChildren = previousData.nextSiblingIndex > this->_previousIndex + 1; bool currentTraversalVisitedChildren = @@ -882,15 +999,9 @@ void TreeTraversalState::difference_iterator:: this->_nextSiblingIndex = currentData.nextSiblingIndex; return; } - } - // If we get here without returning, we're done iterating. We should be at the - // end of both traversals. - CESIUM_ASSERT( - this->_previousIndex == - int64_t(this->_pState->_previousTraversal.size())); - CESIUM_ASSERT( - this->_currentIndex == int64_t(this->_pState->_currentTraversal.size())); + this->advanceToCurrentDifference(); + } } #pragma endregion diff --git a/CesiumUtility/test/TestTreeTraversalState.cpp b/CesiumUtility/test/TestTreeTraversalState.cpp index 7aacfb27f..eb6e5ef21 100644 --- a/CesiumUtility/test/TestTreeTraversalState.cpp +++ b/CesiumUtility/test/TestTreeTraversalState.cpp @@ -14,16 +14,19 @@ struct Node { std::vector> getDifferences(const TreeTraversalState& traversalState) { - std::vector> differences; + std::vector> result; - for (auto difference : traversalState.differences()) { - differences.emplace_back( + auto differences = traversalState.differences(); + for (auto it = differences.begin(); it != differences.end(); ++it) { + const auto& difference = *it; + auto descendantsEnd = it.descendantsEnd(); + result.emplace_back( difference.pNode, difference.previousState, difference.currentState); } - return differences; + return result; } } // namespace From bf125bd2a2ebafa53b1d7ff76c90ac8d9adc2de1 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 15 May 2025 20:12:20 +1000 Subject: [PATCH 15/18] Cleaner iterator advancement. --- .../CesiumUtility/TreeTraversalState.h | 343 +++++++++++------- 1 file changed, 213 insertions(+), 130 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 20f839640..74de34338 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -408,6 +408,10 @@ template class TreeTraversalState { const Difference* operator->() const { return this; } }; +private: + struct TraversalData; + +public: /** * @brief The type of the iterator created by {@link Differences}. */ @@ -486,13 +490,77 @@ template class TreeTraversalState { int64_t currentIndex) noexcept; /** - * @brief While the iterator doesn't point to an actual difference, and - * isn't at the end, move it forward. + * @brief Creates an iterator that points to the first difference that + * occurs on or after a given pair of traversal indices. + * + * The given indices must either both be after the end of the traversal + * vectors, or they must point to entries for the same node. * - * If the iterator already points to a valid difference, this method does - * nothing. + * If they point to the same node, and the state of that node is the same in + * the previous and current traversals, then the iterator will be advanced + * to the next actual difference. + * + * @param pState The tree traversal state being differenced. + * @param previousIndex The index of the current node in the previous + * traversal. + * @param currentIndex The index of the current node in the current + * traversal. + * @returns The created iterator. */ - void advanceToCurrentDifference() noexcept; + static difference_iterator createOnOrAfter( + const TreeTraversalState* pState, + int64_t previousIndex, + int64_t currentIndex); + + /** + * @brief Advances the iterator from its current position on a node that was + * visited in both the previous and current traversals. + * + * Use this overload if the previous and current traversal data for the + * current node are already known. Otherwise, use the one taking no + * parameters. + */ + void advanceFromMatchedNode( + const TraversalData& previousData, + const TraversalData& currentData); + + /** + * @brief Advances the iterator from its current position on a node that was + * visited in both the previous and current traversals. + * + * Use this overload if the previous and current traversal data for the + * current node are not already known. + */ + void advanceFromMatchedNode(); + + /** + * @brief If the iterator already points to a node where the state was + * different in the two traversals, this method does nothing. Otherwise, it + * advances the iterator until it points to a valid difference. On + * invocation of this method, the iterator _must_ point to a node that was + * visited in both the previous and current traversals. + */ + void advanceFromMatchedNodeUnlessDifferent(); + + /** + * @brief Advance this iterator to the next node, which might not actually + * represent a difference. + * + * If this method returns true, the iterator definitely points to the next + * difference. + * + * If this method returns false, the iterator will either represent the end + * of the traversal, or it will point to a node that existed in both + * traversals. In the latter case, the state between the two traversals may + * or may not be different. + * + * @returns true if the current position represents a structural difference + * ({@link DifferenceType::NodeOnlyInPrevious} or + * {@link DifferenceType::NodeOnlyInCurrent}); otherwise, false. + */ + bool advanceOnce( + const TraversalData& previousData, + const TraversalData& currentData); /** * @brief Moves the current iterator to the next difference after the @@ -760,7 +828,7 @@ TreeTraversalState::difference_iterator:: previousData.nextSiblingIndex, currentData.nextSiblingIndex); - result.advanceToCurrentDifference(); + result.advanceFromMatchedNodeUnlessDifferent(); return result; } else if (this->_differenceType == DifferenceType::NodeOnlyInPrevious) { @@ -772,13 +840,13 @@ TreeTraversalState::difference_iterator:: this->_pState, previousData.nextSiblingIndex, this->_currentIndex); - result._differenceType = - previousData.nextSiblingIndex == this->_nextSiblingIndex - ? DifferenceType::StateChange - : DifferenceType::NodeOnlyInPrevious; + result._differenceType = this->_differenceType; result._nextSiblingIndex = this->_nextSiblingIndex; - result.advanceToCurrentDifference(); + if (previousData.nextSiblingIndex == this->_nextSiblingIndex) { + result._differenceType = DifferenceType::StateChange; + result.advanceFromMatchedNodeUnlessDifferent(); + } return result; } else { @@ -789,13 +857,13 @@ TreeTraversalState::difference_iterator:: this->_pState, this->_previousIndex, currentData.nextSiblingIndex); - result._differenceType = - currentData.nextSiblingIndex == this->_nextSiblingIndex - ? DifferenceType::StateChange - : DifferenceType::NodeOnlyInCurrent; + result._differenceType = this->_differenceType; result._nextSiblingIndex = this->_nextSiblingIndex; - result.advanceToCurrentDifference(); + if (currentData.nextSiblingIndex == this->_nextSiblingIndex) { + result._differenceType = DifferenceType::StateChange; + result.advanceFromMatchedNodeUnlessDifferent(); + } return result; } @@ -826,19 +894,7 @@ TreeTraversalState::difference_iterator:: bool hasCurrentTraversal = !pState->_currentTraversal.empty(); if (hasPreviousTraversal && hasCurrentTraversal) { - const TreeTraversalState::TraversalData& - previousData = pState->_previousTraversal.front(); - const TreeTraversalState::TraversalData& currentData = - pState->_currentTraversal.front(); - CESIUM_ASSERT(previousData.pNode == currentData.pNode); - if (previousData.state != currentData.state) { - // The root node has a different state in the two traversals, so this is - // our first difference. - } else { - // The root node has the same state in both traversals. Advanced to the - // next difference. - this->advanceToNextDifference(); - } + this->advanceFromMatchedNodeUnlessDifferent(); } else if (hasPreviousTraversal) { // There are no nodes at all in the current traversal, so all previous // states are differences. @@ -866,59 +922,137 @@ TreeTraversalState::difference_iterator:: _differenceType(DifferenceType::StateChange), _nextSiblingIndex(-1) {} +template +/*static*/ TreeTraversalState::difference_iterator +TreeTraversalState::difference_iterator::createOnOrAfter( + const TreeTraversalState* pState, + int64_t previousIndex, + int64_t currentIndex) { + CESIUM_ASSERT(previousIndex >= 0); + CESIUM_ASSERT(currentIndex >= 0); + + if (previousIndex >= int64_t(pState->_previousTraversal.size()) || + currentIndex >= int64_t(pState->_currentTraversal.size())) { + // If either index is past the end, they both should be. + CESIUM_ASSERT(previousIndex >= int64_t(pState->_previousTraversal.size())); + CESIUM_ASSERT(currentIndex >= int64_t(pState->_currentTraversal.size())); + + // Return the end iterator. + return difference_iterator( + pState, + int64_t(pState->_previousTraversal.size()), + int64_t(pState->_currentTraversal.size())); + } + + // Valid indices - they must point to the same node. + const TraversalData& previousData = + pState->_previousTraversal[size_t(previousIndex)]; + const TraversalData& currentData = + pState->_currentTraversal[size_t(currentIndex)]; + + CESIUM_ASSERT(previousData.pNode == currentData.pNode); + if (previousData.pNode != currentData.pNode) { + // This shouldn't happen. Stop the iteration by setting this iterator + // equal to the end() iterator. + return difference_iterator( + pState, + int64_t(pState->_previousTraversal.size()), + int64_t(pState->_currentTraversal.size())); + } + + // Create an iterator that may initially _not_ point to a difference. + difference_iterator result(pState, previousIndex, currentIndex); + + if (previousData.state == currentData.state) { + // This is not a valid difference, so move to the next one. + result.advanceFromMatchedNode(); + } + + return result; +} + +template +bool TreeTraversalState::difference_iterator::advanceOnce( + const TraversalData& previousData, + const TraversalData& currentData) { + CESIUM_ASSERT(previousData.pNode == currentData.pNode); + + bool previousTraversalVisitedChildren = + previousData.nextSiblingIndex > this->_previousIndex + 1; + bool currentTraversalVisitedChildren = + currentData.nextSiblingIndex > this->_currentIndex + 1; + + ++this->_previousIndex; + ++this->_currentIndex; + + if (previousTraversalVisitedChildren && !currentTraversalVisitedChildren) { + // No descendants in current traversal, so every previous traversal + // descendant is a difference. + this->_differenceType = DifferenceType::NodeOnlyInPrevious; + this->_nextSiblingIndex = previousData.nextSiblingIndex; + return true; + } else if ( + currentTraversalVisitedChildren && !previousTraversalVisitedChildren) { + // No descendants in previous traversal, so every current traversal + // descendant is a difference. + this->_differenceType = DifferenceType::NodeOnlyInCurrent; + this->_nextSiblingIndex = currentData.nextSiblingIndex; + return true; + } + + return false; +} + template void TreeTraversalState::difference_iterator:: - advanceToCurrentDifference() noexcept { - // Nodes only visited in the previous or current traversal always represent a - // difference. - if (this->_differenceType != DifferenceType::StateChange) { + advanceFromMatchedNode( + const TraversalData& previousData, + const TraversalData& currentData) { + bool isStructuralDifference = this->advanceOnce(previousData, currentData); + if (isStructuralDifference) { + // Found a node that exists in one traversal and not the other. return; } + this->advanceFromMatchedNodeUnlessDifferent(); +} + +template +void TreeTraversalState::difference_iterator:: + advanceFromMatchedNode() { + if (this->_previousIndex < + int64_t(this->_pState->_previousTraversal.size()) && + this->_currentIndex < int64_t(this->_pState->_currentTraversal.size())) { + const TraversalData& previousData = + this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + const TraversalData& currentData = + this->_pState->_currentTraversal[size_t(this->_currentIndex)]; + this->advanceFromMatchedNode(previousData, currentData); + } +} + +template +void TreeTraversalState::difference_iterator:: + advanceFromMatchedNodeUnlessDifferent() { while (this->_previousIndex < int64_t(this->_pState->_previousTraversal.size()) && this->_currentIndex < int64_t(this->_pState->_currentTraversal.size())) { - const TreeTraversalState::TraversalData& - previousData = - this->_pState->_previousTraversal[size_t(this->_previousIndex)]; - const TreeTraversalState::TraversalData& currentData = + const TraversalData& previousData = + this->_pState->_previousTraversal[size_t(this->_previousIndex)]; + const TraversalData& currentData = this->_pState->_currentTraversal[size_t(this->_currentIndex)]; CESIUM_ASSERT(previousData.pNode == currentData.pNode); - if (previousData.pNode != currentData.pNode) { - // This shouldn't happen. Stop the iteration by setting this iterator - // equal to the end() iterator. - this->_previousIndex = int64_t(this->_pState->_previousTraversal.size()); - this->_currentIndex = int64_t(this->_pState->_currentTraversal.size()); - return; - } if (previousData.state != currentData.state) { - // Current node has a different state in the two traversals. + // Found the next difference. return; } - bool previousTraversalVisitedChildren = - previousData.nextSiblingIndex > this->_previousIndex + 1; - bool currentTraversalVisitedChildren = - currentData.nextSiblingIndex > this->_currentIndex + 1; - - ++this->_previousIndex; - ++this->_currentIndex; - - if (previousTraversalVisitedChildren && !currentTraversalVisitedChildren) { - // No descendants in current traversal, so every previous traversal - // descendant is a difference. - this->_differenceType = DifferenceType::NodeOnlyInPrevious; - this->_nextSiblingIndex = previousData.nextSiblingIndex; - return; - } else if ( - currentTraversalVisitedChildren && !previousTraversalVisitedChildren) { - // No descendants in previous traversal, so every current traversal - // descendant is a difference. - this->_differenceType = DifferenceType::NodeOnlyInCurrent; - this->_nextSiblingIndex = currentData.nextSiblingIndex; + bool isStructuralDifference = this->advanceOnce(previousData, currentData); + if (isStructuralDifference) { + // Found a node that exists in one traversal and not the other. return; } } @@ -935,72 +1069,21 @@ void TreeTraversalState::difference_iterator:: template void TreeTraversalState::difference_iterator:: advanceToNextDifference() noexcept { - // bool first = true; - - if (this->_differenceType == DifferenceType::NodeOnlyInPrevious) { - ++this->_previousIndex; - if (this->_previousIndex < this->_nextSiblingIndex) { - // Enumerating another previous node that doesn't exist in the current - // traversal. - return; - } else { - // We reached the end of the previous nodes that don't exist in the - // current traversal. - this->_differenceType = DifferenceType::StateChange; - this->advanceToCurrentDifference(); - } - } else if (this->_differenceType == DifferenceType::NodeOnlyInCurrent) { - ++this->_currentIndex; - if (this->_currentIndex < this->_nextSiblingIndex) { - // Enumerating another current node that doesn't exist in the previous - // traversal. - return; - } else { - // We reached the end of the current nodes that don't exist in the - // previous traversal. - this->_differenceType = DifferenceType::StateChange; - this->advanceToCurrentDifference(); - } - } else { - const TreeTraversalState::TraversalData& - previousData = - this->_pState->_previousTraversal[size_t(this->_previousIndex)]; - const TreeTraversalState::TraversalData& currentData = - this->_pState->_currentTraversal[size_t(this->_currentIndex)]; - - CESIUM_ASSERT(previousData.pNode == currentData.pNode); - if (previousData.pNode != currentData.pNode) { - // This shouldn't happen. Stop the iteration by setting this iterator - // equal to the end() iterator. - this->_previousIndex = int64_t(this->_pState->_previousTraversal.size()); - this->_currentIndex = int64_t(this->_pState->_currentTraversal.size()); - return; - } - - bool previousTraversalVisitedChildren = - previousData.nextSiblingIndex > this->_previousIndex + 1; - bool currentTraversalVisitedChildren = - currentData.nextSiblingIndex > this->_currentIndex + 1; - - ++this->_previousIndex; - ++this->_currentIndex; - - if (previousTraversalVisitedChildren && !currentTraversalVisitedChildren) { - // No descendants in current traversal, so every previous traversal - // descendant is a difference. - this->_differenceType = DifferenceType::NodeOnlyInPrevious; - this->_nextSiblingIndex = previousData.nextSiblingIndex; - return; - } else if ( - currentTraversalVisitedChildren && !previousTraversalVisitedChildren) { - // No descendants in previous traversal, so every current traversal - // descendant is a difference. - this->_differenceType = DifferenceType::NodeOnlyInCurrent; - this->_nextSiblingIndex = currentData.nextSiblingIndex; - return; - } + if (this->_differenceType == DifferenceType::StateChange) { + // We're on a matched node, advanced to the next difference. + this->advanceFromMatchedNode(); + } - this->advanceToCurrentDifference(); + // We're on a node that only exists in one of the traversals. + int64_t index = this->_differenceType == DifferenceType::NodeOnlyInPrevious + ? ++this->_previousIndex + : ++this->_currentIndex; + if (index >= this->_nextSiblingIndex) { + // We reached the end of the nodes that don't exist in the other traversal. + // The next node is guaranteed to exist in both traversals, but may or may + // not have a different state. + this->_differenceType = DifferenceType::StateChange; + this->advanceFromMatchedNodeUnlessDifferent(); } } From 572e9bc68135a5058e6769cd994d259ad00deec8 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 15 May 2025 21:48:08 +1000 Subject: [PATCH 16/18] Fix improper fall-through. --- .../CesiumUtility/TreeTraversalState.h | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 74de34338..846301e03 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -1072,18 +1072,18 @@ void TreeTraversalState::difference_iterator:: if (this->_differenceType == DifferenceType::StateChange) { // We're on a matched node, advanced to the next difference. this->advanceFromMatchedNode(); - } - - // We're on a node that only exists in one of the traversals. - int64_t index = this->_differenceType == DifferenceType::NodeOnlyInPrevious - ? ++this->_previousIndex - : ++this->_currentIndex; - if (index >= this->_nextSiblingIndex) { - // We reached the end of the nodes that don't exist in the other traversal. - // The next node is guaranteed to exist in both traversals, but may or may - // not have a different state. - this->_differenceType = DifferenceType::StateChange; - this->advanceFromMatchedNodeUnlessDifferent(); + } else { + // We're on a node that only exists in one of the traversals. + int64_t index = this->_differenceType == DifferenceType::NodeOnlyInPrevious + ? ++this->_previousIndex + : ++this->_currentIndex; + if (index >= this->_nextSiblingIndex) { + // We reached the end of the nodes that don't exist in the other + // traversal. The next node is guaranteed to exist in both traversals, but + // may or may not have a different state. + this->_differenceType = DifferenceType::StateChange; + this->advanceFromMatchedNodeUnlessDifferent(); + } } } From c4bffeec8d2687013e82c3d5fdd6d535f81cf123 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 16 May 2025 21:06:28 +1000 Subject: [PATCH 17/18] Fix clang errors. --- CesiumUtility/include/CesiumUtility/TreeTraversalState.h | 4 ++-- CesiumUtility/test/TestTreeTraversalState.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 846301e03..34cccb751 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -730,7 +730,7 @@ template class TreeTraversalState { // `_previousTraversal`, if it exists at all, it will be found at this index. int64_t _previousTraversalNextNodeIndex = 0; - template + template friend class TreeTraversalStateDiffIterator; }; @@ -808,7 +808,7 @@ template TreeTraversalState::difference_iterator TreeTraversalState::difference_iterator::operator++( int) noexcept { - TreeTraversalStateDiffIterator result = *this; + difference_iterator result = *this; return ++result; } diff --git a/CesiumUtility/test/TestTreeTraversalState.cpp b/CesiumUtility/test/TestTreeTraversalState.cpp index eb6e5ef21..8e08518ff 100644 --- a/CesiumUtility/test/TestTreeTraversalState.cpp +++ b/CesiumUtility/test/TestTreeTraversalState.cpp @@ -19,7 +19,7 @@ getDifferences(const TreeTraversalState& traversalState) { auto differences = traversalState.differences(); for (auto it = differences.begin(); it != differences.end(); ++it) { const auto& difference = *it; - auto descendantsEnd = it.descendantsEnd(); + [[maybe_unused]] auto descendantsEnd = it.descendantsEnd(); result.emplace_back( difference.pNode, difference.previousState, From 74ccf7284abc934bafa23a8f838dc6b63d24b777 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 16 May 2025 21:24:55 +1000 Subject: [PATCH 18/18] More clang errors. --- .../include/CesiumUtility/TreeTraversalState.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h index 34cccb751..a0a2ff076 100644 --- a/CesiumUtility/include/CesiumUtility/TreeTraversalState.h +++ b/CesiumUtility/include/CesiumUtility/TreeTraversalState.h @@ -737,7 +737,7 @@ template class TreeTraversalState { #pragma region Differences Implementation template -TreeTraversalState::Difference +typename TreeTraversalState::Difference TreeTraversalState::difference_iterator::operator*() const noexcept { if (this->_differenceType == DifferenceType::StateChange) { @@ -790,7 +790,7 @@ TreeTraversalState::difference_iterator::operator*() } template -TreeTraversalState::Difference +typename TreeTraversalState::Difference TreeTraversalState::difference_iterator::operator->() const noexcept { return this->operator*(); @@ -805,7 +805,7 @@ operator++() noexcept { } template -TreeTraversalState::difference_iterator +typename TreeTraversalState::difference_iterator TreeTraversalState::difference_iterator::operator++( int) noexcept { difference_iterator result = *this; @@ -813,7 +813,7 @@ TreeTraversalState::difference_iterator::operator++( } template -TreeTraversalState::difference_iterator +typename TreeTraversalState::difference_iterator TreeTraversalState::difference_iterator:: descendantsEnd() { if (this->_differenceType == DifferenceType::StateChange) { @@ -923,7 +923,9 @@ TreeTraversalState::difference_iterator:: _nextSiblingIndex(-1) {} template -/*static*/ TreeTraversalState::difference_iterator +/*static*/ typename TreeTraversalState< + TNodePointer, + TState>::difference_iterator TreeTraversalState::difference_iterator::createOnOrAfter( const TreeTraversalState* pState, int64_t previousIndex,