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 DEFINE_INLINE_VIRTUAL_TRACE()
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