1/*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "IDBBackingStoreCursorLevelDB.h"
28
29#if ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
30
31#include "IDBBackingStoreLevelDB.h"
32#include "LevelDBTransaction.h"
33
34namespace WebCore {
35
36IDBBackingStoreCursorLevelDB::IDBBackingStoreCursorLevelDB(const IDBBackingStoreCursorLevelDB* other)
37    : m_transaction(other->m_transaction)
38    , m_cursorOptions(other->m_cursorOptions)
39    , m_currentKey(other->m_currentKey)
40    , m_recordIdentifier(IDBRecordIdentifier::create())
41{
42    if (other->m_iterator) {
43        m_iterator = m_transaction->createIterator();
44
45        if (other->m_iterator->isValid()) {
46            m_iterator->seek(other->m_iterator->key());
47            ASSERT(m_iterator->isValid());
48        }
49    }
50
51    m_recordIdentifier->reset(other->m_recordIdentifier->encodedPrimaryKey(), other->m_recordIdentifier->version());
52}
53
54bool IDBBackingStoreCursorLevelDB::firstSeek()
55{
56    m_iterator = m_transaction->createIterator();
57    if (m_cursorOptions.forward)
58        m_iterator->seek(m_cursorOptions.lowKey);
59    else
60        m_iterator->seek(m_cursorOptions.highKey);
61
62    return continueFunction(0, Ready);
63}
64
65bool IDBBackingStoreCursorLevelDB::advance(unsigned long count)
66{
67    while (count--) {
68        if (!continueFunction())
69            return false;
70    }
71    return true;
72}
73
74bool IDBBackingStoreCursorLevelDB::continueFunction(const IDBKey* key, IteratorState nextState)
75{
76    RefPtr<IDBKey> previousKey = m_currentKey;
77
78    bool firstIteration = true;
79
80    // When iterating with PrevNoDuplicate, spec requires that the
81    // value we yield for each key is the first duplicate in forwards
82    // order.
83    RefPtr<IDBKey> lastDuplicateKey;
84
85    bool forward = m_cursorOptions.forward;
86
87    for (;;) {
88        if (nextState == Seek) {
89            // FIXME: Optimize seeking for reverse cursors as well.
90            if (firstIteration && key && forward) {
91                m_iterator->seek(encodeKey(*key));
92                firstIteration = false;
93            } else if (forward)
94                m_iterator->next();
95            else
96                m_iterator->prev();
97        } else
98            nextState = Seek; // for subsequent iterations
99
100        if (!m_iterator->isValid()) {
101            if (!forward && lastDuplicateKey.get()) {
102                // We need to walk forward because we hit the end of
103                // the data.
104                forward = true;
105                continue;
106            }
107
108            return false;
109        }
110
111        if (isPastBounds()) {
112            if (!forward && lastDuplicateKey.get()) {
113                // We need to walk forward because now we're beyond the
114                // bounds defined by the cursor.
115                forward = true;
116                continue;
117            }
118
119            return false;
120        }
121
122        if (!haveEnteredRange())
123            continue;
124
125        // The row may not load because there's a stale entry in the
126        // index. This is not fatal.
127        if (!loadCurrentRow())
128            continue;
129
130        if (key) {
131            if (forward) {
132                if (m_currentKey->isLessThan(key))
133                    continue;
134            } else {
135                if (key->isLessThan(m_currentKey.get()))
136                    continue;
137            }
138        }
139
140        if (m_cursorOptions.unique) {
141
142            if (m_currentKey->isEqual(previousKey.get())) {
143                // We should never be able to walk forward all the way
144                // to the previous key.
145                ASSERT(!lastDuplicateKey.get());
146                continue;
147            }
148
149            if (!forward) {
150                if (!lastDuplicateKey.get()) {
151                    lastDuplicateKey = m_currentKey;
152                    continue;
153                }
154
155                // We need to walk forward because we hit the boundary
156                // between key ranges.
157                if (!lastDuplicateKey->isEqual(m_currentKey.get())) {
158                    forward = true;
159                    continue;
160                }
161
162                continue;
163            }
164        }
165        break;
166    }
167
168    ASSERT(!lastDuplicateKey.get() || (forward && lastDuplicateKey->isEqual(m_currentKey.get())));
169    return true;
170}
171
172bool IDBBackingStoreCursorLevelDB::haveEnteredRange() const
173{
174    if (m_cursorOptions.forward) {
175        if (m_cursorOptions.lowOpen)
176            return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) > 0;
177
178        return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) >= 0;
179    }
180    if (m_cursorOptions.highOpen)
181        return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) < 0;
182
183    return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) <= 0;
184}
185
186bool IDBBackingStoreCursorLevelDB::isPastBounds() const
187{
188    if (m_cursorOptions.forward) {
189        if (m_cursorOptions.highOpen)
190            return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) >= 0;
191        return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.highKey) > 0;
192    }
193
194    if (m_cursorOptions.lowOpen)
195        return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) <= 0;
196    return IDBBackingStoreLevelDB::compareIndexKeys(m_iterator->key(), m_cursorOptions.lowKey) < 0;
197}
198
199} // namespace WebCore
200
201#endif // ENABLE(INDEXED_DATABASE) && USE(LEVELDB)
202
203