1 /* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * Copyright (C) 2014 Samsung Electronics. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #ifndef CollectionIndexCache_h 33 #define CollectionIndexCache_h 34 35 #include "core/dom/Element.h" 36 37 namespace WebCore { 38 39 template <typename Collection, typename NodeType> 40 class CollectionIndexCache { 41 DISALLOW_ALLOCATION(); 42 public: 43 CollectionIndexCache(); 44 45 bool isEmpty(const Collection& collection) 46 { 47 if (isCachedNodeCountValid()) 48 return !cachedNodeCount(); 49 if (cachedNode()) 50 return false; 51 return !nodeAt(collection, 0); 52 } 53 bool hasExactlyOneNode(const Collection& collection) 54 { 55 if (isCachedNodeCountValid()) 56 return cachedNodeCount() == 1; 57 if (cachedNode()) 58 return !cachedNodeIndex() && !nodeAt(collection, 1); 59 return nodeAt(collection, 0) && !nodeAt(collection, 1); 60 } 61 62 unsigned nodeCount(const Collection&); 63 NodeType* nodeAt(const Collection&, unsigned index); 64 65 void invalidate(); 66 67 void trace(Visitor* visitor) 68 { 69 visitor->trace(m_currentNode); 70 } 71 72 private: 73 NodeType* nodeBeforeCachedNode(const Collection&, unsigned index); 74 NodeType* nodeAfterCachedNode(const Collection&, unsigned index); 75 76 ALWAYS_INLINE NodeType* cachedNode() const { return m_currentNode; } 77 ALWAYS_INLINE unsigned cachedNodeIndex() const { ASSERT(cachedNode()); return m_cachedNodeIndex; } 78 ALWAYS_INLINE void setCachedNode(NodeType* node, unsigned index) 79 { 80 ASSERT(node); 81 m_currentNode = node; 82 m_cachedNodeIndex = index; 83 } 84 85 ALWAYS_INLINE bool isCachedNodeCountValid() const { return m_isLengthCacheValid; } 86 ALWAYS_INLINE unsigned cachedNodeCount() const { return m_cachedNodeCount; } 87 ALWAYS_INLINE void setCachedNodeCount(unsigned length) 88 { 89 m_cachedNodeCount = length; 90 m_isLengthCacheValid = true; 91 } 92 93 RawPtrWillBeMember<NodeType> m_currentNode; 94 unsigned m_cachedNodeCount; 95 unsigned m_cachedNodeIndex; 96 unsigned m_isLengthCacheValid : 1; 97 }; 98 99 template <typename Collection, typename NodeType> 100 CollectionIndexCache<Collection, NodeType>::CollectionIndexCache() 101 : m_currentNode(nullptr) 102 , m_cachedNodeCount(0) 103 , m_cachedNodeIndex(0) 104 , m_isLengthCacheValid(false) 105 { 106 } 107 108 template <typename Collection, typename NodeType> 109 void CollectionIndexCache<Collection, NodeType>::invalidate() 110 { 111 m_currentNode = nullptr; 112 m_isLengthCacheValid = false; 113 } 114 115 template <typename Collection, typename NodeType> 116 inline unsigned CollectionIndexCache<Collection, NodeType>::nodeCount(const Collection& collection) 117 { 118 if (isCachedNodeCountValid()) 119 return cachedNodeCount(); 120 121 nodeAt(collection, UINT_MAX); 122 ASSERT(isCachedNodeCountValid()); 123 124 return cachedNodeCount(); 125 } 126 127 template <typename Collection, typename NodeType> 128 inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAt(const Collection& collection, unsigned index) 129 { 130 if (isCachedNodeCountValid() && index >= cachedNodeCount()) 131 return 0; 132 133 if (cachedNode()) { 134 if (index > cachedNodeIndex()) 135 return nodeAfterCachedNode(collection, index); 136 if (index < cachedNodeIndex()) 137 return nodeBeforeCachedNode(collection, index); 138 return cachedNode(); 139 } 140 141 // No valid cache yet, let's find the first matching element. 142 ASSERT(!isCachedNodeCountValid()); 143 NodeType* firstNode = collection.traverseToFirstElement(); 144 if (!firstNode) { 145 // The collection is empty. 146 setCachedNodeCount(0); 147 return 0; 148 } 149 setCachedNode(firstNode, 0); 150 return index ? nodeAfterCachedNode(collection, index) : firstNode; 151 } 152 153 template <typename Collection, typename NodeType> 154 inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeBeforeCachedNode(const Collection& collection, unsigned index) 155 { 156 ASSERT(cachedNode()); // Cache should be valid. 157 unsigned currentIndex = cachedNodeIndex(); 158 ASSERT(currentIndex > index); 159 160 // Determine if we should traverse from the beginning of the collection instead of the cached node. 161 bool firstIsCloser = index < currentIndex - index; 162 if (firstIsCloser || !collection.canTraverseBackward()) { 163 NodeType* firstNode = collection.traverseToFirstElement(); 164 ASSERT(firstNode); 165 setCachedNode(firstNode, 0); 166 return index ? nodeAfterCachedNode(collection, index) : firstNode; 167 } 168 169 // Backward traversal from the cached node to the requested index. 170 ASSERT(collection.canTraverseBackward()); 171 NodeType* currentNode = collection.traverseBackwardToOffset(index, *cachedNode(), currentIndex); 172 ASSERT(currentNode); 173 setCachedNode(currentNode, currentIndex); 174 return currentNode; 175 } 176 177 template <typename Collection, typename NodeType> 178 inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAfterCachedNode(const Collection& collection, unsigned index) 179 { 180 ASSERT(cachedNode()); // Cache should be valid. 181 unsigned currentIndex = cachedNodeIndex(); 182 ASSERT(currentIndex < index); 183 184 // Determine if we should traverse from the end of the collection instead of the cached node. 185 bool lastIsCloser = isCachedNodeCountValid() && cachedNodeCount() - index < index - currentIndex; 186 if (lastIsCloser && collection.canTraverseBackward()) { 187 NodeType* lastItem = collection.traverseToLastElement(); 188 ASSERT(lastItem); 189 setCachedNode(lastItem, cachedNodeCount() - 1); 190 if (index < cachedNodeCount() - 1) 191 return nodeBeforeCachedNode(collection, index); 192 return lastItem; 193 } 194 195 // Forward traversal from the cached node to the requested index. 196 NodeType* currentNode = collection.traverseForwardToOffset(index, *cachedNode(), currentIndex); 197 if (!currentNode) { 198 // Did not find the node. On plus side, we now know the length. 199 setCachedNodeCount(currentIndex + 1); 200 return 0; 201 } 202 setCachedNode(currentNode, currentIndex); 203 return currentNode; 204 } 205 206 } // namespace WebCore 207 208 #endif // CollectionIndexCache_h 209