| OLD | NEW |
| (Empty) |
| 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 namespace blink { | |
| 36 | |
| 37 template <typename Collection, typename NodeType> | |
| 38 class CollectionIndexCache { | |
| 39 DISALLOW_NEW(); | |
| 40 | |
| 41 public: | |
| 42 CollectionIndexCache(); | |
| 43 | |
| 44 bool isEmpty(const Collection& collection) { | |
| 45 if (isCachedNodeCountValid()) | |
| 46 return !cachedNodeCount(); | |
| 47 if (cachedNode()) | |
| 48 return false; | |
| 49 return !nodeAt(collection, 0); | |
| 50 } | |
| 51 bool hasExactlyOneNode(const Collection& collection) { | |
| 52 if (isCachedNodeCountValid()) | |
| 53 return cachedNodeCount() == 1; | |
| 54 if (cachedNode()) | |
| 55 return !cachedNodeIndex() && !nodeAt(collection, 1); | |
| 56 return nodeAt(collection, 0) && !nodeAt(collection, 1); | |
| 57 } | |
| 58 | |
| 59 unsigned nodeCount(const Collection&); | |
| 60 NodeType* nodeAt(const Collection&, unsigned index); | |
| 61 | |
| 62 void invalidate(); | |
| 63 | |
| 64 DEFINE_INLINE_VIRTUAL_TRACE() { visitor->trace(m_currentNode); } | |
| 65 | |
| 66 protected: | |
| 67 ALWAYS_INLINE NodeType* cachedNode() const { return m_currentNode; } | |
| 68 ALWAYS_INLINE unsigned cachedNodeIndex() const { | |
| 69 DCHECK(cachedNode()); | |
| 70 return m_cachedNodeIndex; | |
| 71 } | |
| 72 ALWAYS_INLINE void setCachedNode(NodeType* node, unsigned index) { | |
| 73 DCHECK(node); | |
| 74 m_currentNode = node; | |
| 75 m_cachedNodeIndex = index; | |
| 76 } | |
| 77 | |
| 78 ALWAYS_INLINE bool isCachedNodeCountValid() const { | |
| 79 return m_isLengthCacheValid; | |
| 80 } | |
| 81 ALWAYS_INLINE unsigned cachedNodeCount() const { return m_cachedNodeCount; } | |
| 82 ALWAYS_INLINE void setCachedNodeCount(unsigned length) { | |
| 83 m_cachedNodeCount = length; | |
| 84 m_isLengthCacheValid = true; | |
| 85 } | |
| 86 | |
| 87 private: | |
| 88 NodeType* nodeBeforeCachedNode(const Collection&, unsigned index); | |
| 89 NodeType* nodeAfterCachedNode(const Collection&, unsigned index); | |
| 90 | |
| 91 Member<NodeType> m_currentNode; | |
| 92 unsigned m_cachedNodeCount; | |
| 93 unsigned m_cachedNodeIndex : 31; | |
| 94 unsigned m_isLengthCacheValid : 1; | |
| 95 }; | |
| 96 | |
| 97 template <typename Collection, typename NodeType> | |
| 98 CollectionIndexCache<Collection, NodeType>::CollectionIndexCache() | |
| 99 : m_currentNode(nullptr), | |
| 100 m_cachedNodeCount(0), | |
| 101 m_cachedNodeIndex(0), | |
| 102 m_isLengthCacheValid(false) {} | |
| 103 | |
| 104 template <typename Collection, typename NodeType> | |
| 105 void CollectionIndexCache<Collection, NodeType>::invalidate() { | |
| 106 m_currentNode = nullptr; | |
| 107 m_isLengthCacheValid = false; | |
| 108 } | |
| 109 | |
| 110 template <typename Collection, typename NodeType> | |
| 111 inline unsigned CollectionIndexCache<Collection, NodeType>::nodeCount( | |
| 112 const Collection& collection) { | |
| 113 if (isCachedNodeCountValid()) | |
| 114 return cachedNodeCount(); | |
| 115 | |
| 116 nodeAt(collection, UINT_MAX); | |
| 117 DCHECK(isCachedNodeCountValid()); | |
| 118 | |
| 119 return cachedNodeCount(); | |
| 120 } | |
| 121 | |
| 122 template <typename Collection, typename NodeType> | |
| 123 inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAt( | |
| 124 const Collection& collection, | |
| 125 unsigned index) { | |
| 126 if (isCachedNodeCountValid() && index >= cachedNodeCount()) | |
| 127 return nullptr; | |
| 128 | |
| 129 if (cachedNode()) { | |
| 130 if (index > cachedNodeIndex()) | |
| 131 return nodeAfterCachedNode(collection, index); | |
| 132 if (index < cachedNodeIndex()) | |
| 133 return nodeBeforeCachedNode(collection, index); | |
| 134 return cachedNode(); | |
| 135 } | |
| 136 | |
| 137 // No valid cache yet, let's find the first matching element. | |
| 138 DCHECK(!isCachedNodeCountValid()); | |
| 139 NodeType* firstNode = collection.traverseToFirst(); | |
| 140 if (!firstNode) { | |
| 141 // The collection is empty. | |
| 142 setCachedNodeCount(0); | |
| 143 return nullptr; | |
| 144 } | |
| 145 setCachedNode(firstNode, 0); | |
| 146 return index ? nodeAfterCachedNode(collection, index) : firstNode; | |
| 147 } | |
| 148 | |
| 149 template <typename Collection, typename NodeType> | |
| 150 inline NodeType* | |
| 151 CollectionIndexCache<Collection, NodeType>::nodeBeforeCachedNode( | |
| 152 const Collection& collection, | |
| 153 unsigned index) { | |
| 154 DCHECK(cachedNode()); // Cache should be valid. | |
| 155 unsigned currentIndex = cachedNodeIndex(); | |
| 156 DCHECK_GT(currentIndex, index); | |
| 157 | |
| 158 // Determine if we should traverse from the beginning of the collection | |
| 159 // instead of the cached node. | |
| 160 bool firstIsCloser = index < currentIndex - index; | |
| 161 if (firstIsCloser || !collection.canTraverseBackward()) { | |
| 162 NodeType* firstNode = collection.traverseToFirst(); | |
| 163 DCHECK(firstNode); | |
| 164 setCachedNode(firstNode, 0); | |
| 165 return index ? nodeAfterCachedNode(collection, index) : firstNode; | |
| 166 } | |
| 167 | |
| 168 // Backward traversal from the cached node to the requested index. | |
| 169 DCHECK(collection.canTraverseBackward()); | |
| 170 NodeType* currentNode = | |
| 171 collection.traverseBackwardToOffset(index, *cachedNode(), currentIndex); | |
| 172 DCHECK(currentNode); | |
| 173 setCachedNode(currentNode, currentIndex); | |
| 174 return currentNode; | |
| 175 } | |
| 176 | |
| 177 template <typename Collection, typename NodeType> | |
| 178 inline NodeType* | |
| 179 CollectionIndexCache<Collection, NodeType>::nodeAfterCachedNode( | |
| 180 const Collection& collection, | |
| 181 unsigned index) { | |
| 182 DCHECK(cachedNode()); // Cache should be valid. | |
| 183 unsigned currentIndex = cachedNodeIndex(); | |
| 184 DCHECK_LT(currentIndex, index); | |
| 185 | |
| 186 // Determine if we should traverse from the end of the collection instead of | |
| 187 // the cached node. | |
| 188 bool lastIsCloser = isCachedNodeCountValid() && | |
| 189 cachedNodeCount() - index < index - currentIndex; | |
| 190 if (lastIsCloser && collection.canTraverseBackward()) { | |
| 191 NodeType* lastItem = collection.traverseToLast(); | |
| 192 DCHECK(lastItem); | |
| 193 setCachedNode(lastItem, cachedNodeCount() - 1); | |
| 194 if (index < cachedNodeCount() - 1) | |
| 195 return nodeBeforeCachedNode(collection, index); | |
| 196 return lastItem; | |
| 197 } | |
| 198 | |
| 199 // Forward traversal from the cached node to the requested index. | |
| 200 NodeType* currentNode = | |
| 201 collection.traverseForwardToOffset(index, *cachedNode(), currentIndex); | |
| 202 if (!currentNode) { | |
| 203 // Did not find the node. On plus side, we now know the length. | |
| 204 if (isCachedNodeCountValid()) | |
| 205 DCHECK_EQ(currentIndex + 1, cachedNodeCount()); | |
| 206 setCachedNodeCount(currentIndex + 1); | |
| 207 return nullptr; | |
| 208 } | |
| 209 setCachedNode(currentNode, currentIndex); | |
| 210 return currentNode; | |
| 211 } | |
| 212 | |
| 213 } // namespace blink | |
| 214 | |
| 215 #endif // CollectionIndexCache_h | |
| OLD | NEW |