Index: Source/core/html/CollectionIndexCache.h |
diff --git a/Source/core/html/CollectionIndexCache.h b/Source/core/html/CollectionIndexCache.h |
index 39b60017c195f03f430a2b9eccefcb8087636f4a..4efcebb37d843f1c505432f7fb0a91a89bb56226 100644 |
--- a/Source/core/html/CollectionIndexCache.h |
+++ b/Source/core/html/CollectionIndexCache.h |
@@ -64,9 +64,8 @@ public: |
void invalidate(); |
private: |
- NodeType* nodeBeforeOrAfterCachedNode(const Collection&, unsigned index, const ContainerNode& root); |
- bool isLastNodeCloserThanLastOrCachedNode(unsigned index) const; |
- bool isFirstNodeCloserThanCachedNode(unsigned index) const; |
+ NodeType* nodeBeforeCachedNode(const Collection&, unsigned index, const ContainerNode& root); |
+ NodeType* nodeAfterCachedNode(const Collection&, unsigned index, const ContainerNode& root); |
ALWAYS_INLINE NodeType* cachedNode() const { return m_currentNode; } |
ALWAYS_INLINE unsigned cachedNodeIndex() const { ASSERT(cachedNode()); return m_cachedNodeIndex; } |
@@ -122,56 +121,81 @@ inline unsigned CollectionIndexCache<Collection, NodeType>::nodeCount(const Coll |
template <typename Collection, typename NodeType> |
inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAt(const Collection& collection, unsigned index) |
{ |
- if (cachedNode() && cachedNodeIndex() == index) |
+ if (isCachedNodeCountValid() && index >= cachedNodeCount()) |
+ return 0; |
+ |
+ ContainerNode& root = collection.rootNode(); |
+ if (cachedNode()) { |
+ if (index > cachedNodeIndex()) |
+ return nodeAfterCachedNode(collection, index, root); |
+ if (index < cachedNodeIndex()) |
+ return nodeBeforeCachedNode(collection, index, root); |
return cachedNode(); |
+ } |
- if (isCachedNodeCountValid() && cachedNodeCount() <= index) |
+ // No valid cache yet, let's find the first matching element. |
+ ASSERT(!isCachedNodeCountValid()); |
+ NodeType* firstNode = collection.traverseToFirstElement(root); |
+ if (!firstNode) { |
+ // The collection is empty. |
+ setCachedNodeCount(0); |
return 0; |
+ } |
+ setCachedNode(firstNode, 0); |
+ return index ? nodeAfterCachedNode(collection, index, root) : firstNode; |
+} |
- ContainerNode& root = collection.rootNode(); |
- if (isCachedNodeCountValid() && collection.canTraverseBackward() && isLastNodeCloserThanLastOrCachedNode(index)) { |
- NodeType* lastNode = collection.itemBefore(0); |
- ASSERT(lastNode); |
- setCachedNode(lastNode, cachedNodeCount() - 1); |
- } else if (!cachedNode() || isFirstNodeCloserThanCachedNode(index) || (!collection.canTraverseBackward() && index < cachedNodeIndex())) { |
+template <typename Collection, typename NodeType> |
+inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeBeforeCachedNode(const Collection& collection, unsigned index, const ContainerNode& root) |
+{ |
+ ASSERT(cachedNode()); // Cache should be valid. |
+ unsigned currentIndex = cachedNodeIndex(); |
+ ASSERT(currentIndex > index); |
+ |
+ // Determine if we should traverse from the beginning of the collection instead of the cached node. |
+ bool firstIsCloser = index < currentIndex - index; |
+ if (firstIsCloser || !collection.canTraverseBackward()) { |
NodeType* firstNode = collection.traverseToFirstElement(root); |
- if (!firstNode) { |
- setCachedNodeCount(0); |
- return 0; |
- } |
+ ASSERT(firstNode); |
setCachedNode(firstNode, 0); |
- ASSERT(!cachedNodeIndex()); |
+ return index ? nodeAfterCachedNode(collection, index, root) : firstNode; |
} |
- if (cachedNodeIndex() == index) |
- return cachedNode(); |
- |
- return nodeBeforeOrAfterCachedNode(collection, index, root); |
+ // Backward traversal from the cached node to the requested index. |
+ NodeType* currentNode = cachedNode(); |
+ ASSERT(collection.canTraverseBackward()); |
+ while ((currentNode = collection.itemBefore(currentNode))) { |
+ ASSERT(currentIndex); |
+ --currentIndex; |
+ if (currentIndex == index) { |
+ setCachedNode(currentNode, currentIndex); |
+ return currentNode; |
+ } |
+ } |
+ ASSERT_NOT_REACHED(); |
+ return 0; |
} |
template <typename Collection, typename NodeType> |
-inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeBeforeOrAfterCachedNode(const Collection& collection, unsigned index, const ContainerNode &root) |
+inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAfterCachedNode(const Collection& collection, unsigned index, const ContainerNode& root) |
{ |
+ ASSERT(cachedNode()); // Cache should be valid. |
unsigned currentIndex = cachedNodeIndex(); |
- NodeType* currentNode = cachedNode(); |
- ASSERT(currentNode); |
- ASSERT(currentIndex != index); |
- |
- if (index < cachedNodeIndex()) { |
- ASSERT(collection.canTraverseBackward()); |
- while ((currentNode = collection.itemBefore(currentNode))) { |
- ASSERT(currentIndex); |
- currentIndex--; |
- if (currentIndex == index) { |
- setCachedNode(currentNode, currentIndex); |
- return currentNode; |
- } |
- } |
- ASSERT_NOT_REACHED(); |
- return 0; |
+ ASSERT(currentIndex < index); |
+ |
+ // Determine if we should traverse from the end of the collection instead of the cached node. |
+ bool lastIsCloser = isCachedNodeCountValid() && cachedNodeCount() - index < index - currentIndex; |
+ if (lastIsCloser && collection.canTraverseBackward()) { |
+ NodeType* lastItem = collection.itemBefore(0); |
+ ASSERT(lastItem); |
+ setCachedNode(lastItem, cachedNodeCount() - 1); |
+ if (index < cachedNodeCount() - 1) |
+ return nodeBeforeCachedNode(collection, index, root); |
+ return lastItem; |
} |
- currentNode = collection.traverseForwardToOffset(index, *currentNode, currentIndex, root); |
+ // Forward traversal from the cached node to the requested index. |
+ NodeType* currentNode = collection.traverseForwardToOffset(index, *cachedNode(), currentIndex, root); |
if (!currentNode) { |
// Did not find the node. On plus side, we now know the length. |
setCachedNodeCount(currentIndex + 1); |
@@ -181,27 +205,6 @@ inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeBeforeOrAfterCa |
return currentNode; |
} |
-template <typename Collection, typename NodeType> |
-ALWAYS_INLINE bool CollectionIndexCache<Collection, NodeType>::isLastNodeCloserThanLastOrCachedNode(unsigned index) const |
-{ |
- ASSERT(isCachedNodeCountValid()); |
- unsigned distanceFromLastNode = cachedNodeCount() - index; |
- if (!cachedNode()) |
- return distanceFromLastNode < index; |
- |
- return cachedNodeIndex() < index && distanceFromLastNode < index - cachedNodeIndex(); |
-} |
- |
-template <typename Collection, typename NodeType> |
-ALWAYS_INLINE bool CollectionIndexCache<Collection, NodeType>::isFirstNodeCloserThanCachedNode(unsigned index) const |
-{ |
- if (cachedNodeIndex() < index) |
- return false; |
- |
- unsigned distanceFromCachedNode = cachedNodeIndex() - index; |
- return index < distanceFromCachedNode; |
-} |
- |
} // namespace WebCore |
#endif // CollectionIndexCache_h |