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
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
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
37 template <typename Collection, typename NodeType>
38 class CollectionIndexCache {
39 DISALLOW_ALLOCATION();
41 CollectionIndexCache();
43 bool isEmpty(const Collection& collection)
45 if (isCachedNodeCountValid())
46 return !cachedNodeCount();
49 return !nodeAt(collection, 0);
51 bool hasExactlyOneNode(const Collection& collection)
53 if (isCachedNodeCountValid())
54 return cachedNodeCount() == 1;
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);
65 void trace(Visitor* visitor)
67 visitor->trace(m_currentNode);
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)
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;
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())
133 if (index > cachedNodeIndex())
134 return nodeAfterCachedNode(collection, index);
135 if (index < cachedNodeIndex())
136 return nodeBeforeCachedNode(collection, index);
140 // No valid cache yet, let's find the first matching element.
141 ASSERT(!isCachedNodeCountValid());
142 NodeType* firstNode = collection.traverseToFirst();
144 // The collection is empty.
145 setCachedNodeCount(0);
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();
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);
172 setCachedNode(currentNode, currentIndex);
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();
188 setCachedNode(lastItem, cachedNodeCount() - 1);
189 if (index < cachedNodeCount() - 1)
190 return nodeBeforeCachedNode(collection, index);
194 // Forward traversal from the cached node to the requested index.
195 NodeType* currentNode = collection.traverseForwardToOffset(index, *cachedNode(), currentIndex);
197 // Did not find the node. On plus side, we now know the length.
198 setCachedNodeCount(currentIndex + 1);
201 setCachedNode(currentNode, currentIndex);
207 #endif // CollectionIndexCache_h