Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / CollectionIndexCache.h
blobfd70e981455d5d0f155f645fdb870cdde7eb8be7
1 /*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2014 Samsung Electronics. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
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.
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.
32 #ifndef CollectionIndexCache_h
33 #define CollectionIndexCache_h
35 namespace blink {
37 template <typename Collection, typename NodeType>
38 class CollectionIndexCache {
39 DISALLOW_ALLOCATION();
40 public:
41 CollectionIndexCache();
43 bool isEmpty(const Collection& collection)
45 if (isCachedNodeCountValid())
46 return !cachedNodeCount();
47 if (cachedNode())
48 return false;
49 return !nodeAt(collection, 0);
51 bool hasExactlyOneNode(const Collection& collection)
53 if (isCachedNodeCountValid())
54 return cachedNodeCount() == 1;
55 if (cachedNode())
56 return !cachedNodeIndex() && !nodeAt(collection, 1);
57 return nodeAt(collection, 0) && !nodeAt(collection, 1);
60 unsigned nodeCount(const Collection&);
61 NodeType* nodeAt(const Collection&, unsigned index);
63 void invalidate();
65 DEFINE_INLINE_VIRTUAL_TRACE()
67 visitor->trace(m_currentNode);
70 protected:
71 ALWAYS_INLINE NodeType* cachedNode() const { return m_currentNode; }
72 ALWAYS_INLINE unsigned cachedNodeIndex() const { ASSERT(cachedNode()); return m_cachedNodeIndex; }
73 ALWAYS_INLINE void setCachedNode(NodeType* node, unsigned index)
75 ASSERT(node);
76 m_currentNode = node;
77 m_cachedNodeIndex = index;
80 ALWAYS_INLINE bool isCachedNodeCountValid() const { return m_isLengthCacheValid; }
81 ALWAYS_INLINE unsigned cachedNodeCount() const { return m_cachedNodeCount; }
82 ALWAYS_INLINE void setCachedNodeCount(unsigned length)
84 m_cachedNodeCount = length;
85 m_isLengthCacheValid = true;
88 private:
89 NodeType* nodeBeforeCachedNode(const Collection&, unsigned index);
90 NodeType* nodeAfterCachedNode(const Collection&, unsigned index);
92 RawPtrWillBeMember<NodeType> m_currentNode;
93 unsigned m_cachedNodeCount;
94 unsigned m_cachedNodeIndex : 31;
95 unsigned m_isLengthCacheValid : 1;
98 template <typename Collection, typename NodeType>
99 CollectionIndexCache<Collection, NodeType>::CollectionIndexCache()
100 : m_currentNode(nullptr)
101 , m_cachedNodeCount(0)
102 , m_cachedNodeIndex(0)
103 , m_isLengthCacheValid(false)
107 template <typename Collection, typename NodeType>
108 void CollectionIndexCache<Collection, NodeType>::invalidate()
110 m_currentNode = nullptr;
111 m_isLengthCacheValid = false;
114 template <typename Collection, typename NodeType>
115 inline unsigned CollectionIndexCache<Collection, NodeType>::nodeCount(const Collection& collection)
117 if (isCachedNodeCountValid())
118 return cachedNodeCount();
120 nodeAt(collection, UINT_MAX);
121 ASSERT(isCachedNodeCountValid());
123 return cachedNodeCount();
126 template <typename Collection, typename NodeType>
127 inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAt(const Collection& collection, unsigned index)
129 if (isCachedNodeCountValid() && index >= cachedNodeCount())
130 return nullptr;
132 if (cachedNode()) {
133 if (index > cachedNodeIndex())
134 return nodeAfterCachedNode(collection, index);
135 if (index < cachedNodeIndex())
136 return nodeBeforeCachedNode(collection, index);
137 return cachedNode();
140 // No valid cache yet, let's find the first matching element.
141 ASSERT(!isCachedNodeCountValid());
142 NodeType* firstNode = collection.traverseToFirst();
143 if (!firstNode) {
144 // The collection is empty.
145 setCachedNodeCount(0);
146 return nullptr;
148 setCachedNode(firstNode, 0);
149 return index ? nodeAfterCachedNode(collection, index) : firstNode;
152 template <typename Collection, typename NodeType>
153 inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeBeforeCachedNode(const Collection& collection, unsigned index)
155 ASSERT(cachedNode()); // Cache should be valid.
156 unsigned currentIndex = cachedNodeIndex();
157 ASSERT(currentIndex > index);
159 // Determine if we should traverse from the beginning of the collection instead of the cached node.
160 bool firstIsCloser = index < currentIndex - index;
161 if (firstIsCloser || !collection.canTraverseBackward()) {
162 NodeType* firstNode = collection.traverseToFirst();
163 ASSERT(firstNode);
164 setCachedNode(firstNode, 0);
165 return index ? nodeAfterCachedNode(collection, index) : firstNode;
168 // Backward traversal from the cached node to the requested index.
169 ASSERT(collection.canTraverseBackward());
170 NodeType* currentNode = collection.traverseBackwardToOffset(index, *cachedNode(), currentIndex);
171 ASSERT(currentNode);
172 setCachedNode(currentNode, currentIndex);
173 return currentNode;
176 template <typename Collection, typename NodeType>
177 inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAfterCachedNode(const Collection& collection, unsigned index)
179 ASSERT(cachedNode()); // Cache should be valid.
180 unsigned currentIndex = cachedNodeIndex();
181 ASSERT(currentIndex < index);
183 // Determine if we should traverse from the end of the collection instead of the cached node.
184 bool lastIsCloser = isCachedNodeCountValid() && cachedNodeCount() - index < index - currentIndex;
185 if (lastIsCloser && collection.canTraverseBackward()) {
186 NodeType* lastItem = collection.traverseToLast();
187 ASSERT(lastItem);
188 setCachedNode(lastItem, cachedNodeCount() - 1);
189 if (index < cachedNodeCount() - 1)
190 return nodeBeforeCachedNode(collection, index);
191 return lastItem;
194 // Forward traversal from the cached node to the requested index.
195 NodeType* currentNode = collection.traverseForwardToOffset(index, *cachedNode(), currentIndex);
196 if (!currentNode) {
197 // Did not find the node. On plus side, we now know the length.
198 setCachedNodeCount(currentIndex + 1);
199 return nullptr;
201 setCachedNode(currentNode, currentIndex);
202 return currentNode;
205 } // namespace blink
207 #endif // CollectionIndexCache_h