root / tags / 1.0.8 / xapian-core / backends / flint / flint_cursor.cc

Revision 11051, 6.5 kB (checked in by olly, 5 months ago)

backends/flint/flint_cursor.cc: Fix comment typo in previous commit.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* flint_cursor.cc: Btree cursor implementation
2 *
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002,2003,2004,2005,2006,2007 Olly Betts
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19 * USA
20 */
21
22#include <config.h>
23
24#include "safeerrno.h"
25
26#include <xapian/error.h>
27
28#include "flint_cursor.h"
29#include "flint_table.h"
30#include "flint_btreeutil.h"
31#include "omassert.h"
32#include "omdebug.h"
33
34#ifdef XAPIAN_DEBUG_VERBOSE
35static string
36hex_encode(const string & input)
37{
38    const char * table = "0123456789abcdef";
39    string result;
40    for (string::const_iterator i = input.begin(); i != input.end(); ++i) {
41        unsigned char val = *i;
42        result += "\\x";
43        result += table[val/16];
44        result += table[val%16];
45    }
46
47    return result;
48}
49#endif
50
51#define DIR_START        11
52
53FlintCursor::FlintCursor(FlintTable *B_)
54        : is_positioned(false),
55          is_after_end(false),
56          tag_status(UNREAD),
57          B(B_),
58          level(B_->level)
59{
60    C = new Cursor_[level + 1];
61
62    for (int j = 0; j < level; j++) {
63        C[j].n = BLK_UNUSED;
64        C[j].p = new byte[B->block_size];
65    }
66    C[level].n = B->C[level].n;
67    C[level].p = B->C[level].p;
68}
69
70FlintCursor::~FlintCursor()
71{
72    // Use the value of level stored in the cursor rather than the
73    // Btree, since the Btree might have been deleted already.
74    for (int j = 0; j < level; j++) {
75        delete [] C[j].p;
76    }
77    delete [] C;
78}
79
80bool
81FlintCursor::prev()
82{
83    DEBUGCALL(DB, bool, "FlintCursor::prev", "");
84    Assert(B->level <= level);
85    Assert(!is_after_end);
86
87    if (!is_positioned) {
88        // We've read the last key and tag, and we're now not positioned.
89        // Simple fix - seek to the current key, and then it's as if we
90        // read the key but not the tag.
91        (void)find_entry(current_key);
92        tag_status = UNREAD;
93    }
94
95    if (tag_status != UNREAD) {
96        while (true) {
97            if (! B->prev(C, 0)) {
98                is_positioned = false;
99                return false;
100            }
101            if (Item_(C[0].p, C[0].c).component_of() == 1) {
102                break;
103            }
104        }
105    }
106
107    while (true) {
108        if (! B->prev(C, 0)) {
109            is_positioned = false;
110            return false;
111        }
112        if (Item_(C[0].p, C[0].c).component_of() == 1) {
113            break;
114        }
115    }
116    get_key(&current_key);
117    tag_status = UNREAD;
118
119    DEBUGLINE(DB, "Moved to entry: key=`" << hex_encode(current_key) << "'");
120    return true;
121}
122
123bool
124FlintCursor::next()
125{
126    DEBUGCALL(DB, bool, "FlintCursor::next", "");
127    Assert(B->level <= level);
128    Assert(!is_after_end);
129    if (tag_status == UNREAD) {
130        while (true) {
131            if (! B->next(C, 0)) {
132                is_positioned = false;
133                break;
134            }
135            if (Item_(C[0].p, C[0].c).component_of() == 1) {
136                is_positioned = true;
137                break;
138            }
139        }
140    }
141
142    if (!is_positioned) {
143        is_after_end = true;
144        return false;
145    }
146
147    get_key(&current_key);
148    tag_status = UNREAD;
149
150    DEBUGLINE(DB, "Moved to entry: key=`" << hex_encode(current_key) << "'");
151    return true;
152}
153
154bool
155FlintCursor::find_entry(const string &key)
156{
157    DEBUGCALL(DB, bool, "FlintCursor::find_entry", key);
158    Assert(B->level <= level);
159
160    is_after_end = false;
161
162    bool found;
163
164    is_positioned = true;
165    if (key.size() > FLINT_BTREE_MAX_KEY_LEN) {
166        // Can't find key - too long to possibly be present, so find the
167        // truncated form but ignore "found".
168        B->form_key(key.substr(0, FLINT_BTREE_MAX_KEY_LEN));
169        (void)(B->find(C));
170        found = false;
171    } else {
172        B->form_key(key);
173        found = B->find(C);
174    }
175
176    if (!found) {
177        if (C[0].c < DIR_START) {
178            C[0].c = DIR_START;
179            if (! B->prev(C, 0)) goto done;
180        }
181        while (Item_(C[0].p, C[0].c).component_of() != 1) {
182            if (! B->prev(C, 0)) {
183                is_positioned = false;
184                throw Xapian::DatabaseCorruptError("find_entry failed to find any entry at all!");
185            }
186        }
187    }
188done:
189
190    if (found)
191        current_key = key;
192    else
193        get_key(&current_key);
194    tag_status = UNREAD;
195
196    DEBUGLINE(DB, "Found entry: key=`" << hex_encode(current_key) << "'");
197    RETURN(found);
198}
199
200bool
201FlintCursor::find_entry_ge(const string &key)
202{
203    DEBUGCALL(DB, bool, "FlintCursor::find_entry_ge", key);
204    Assert(B->level <= level);
205
206    is_after_end = false;
207
208    bool found;
209
210    is_positioned = true;
211    if (key.size() > FLINT_BTREE_MAX_KEY_LEN) {
212        // Can't find key - too long to possibly be present, so find the
213        // truncated form but ignore "found".
214        B->form_key(key.substr(0, FLINT_BTREE_MAX_KEY_LEN));
215        (void)(B->find(C));
216        found = false;
217    } else {
218        B->form_key(key);
219        found = B->find(C);
220    }
221
222    if (found) {
223        current_key = key;
224    } else {
225        if (! B->next(C, 0)) {
226            is_after_end = true;
227            is_positioned = false;
228            RETURN(false);
229        }
230        Assert(Item_(C[0].p, C[0].c).component_of() == 1);
231        get_key(&current_key);
232    }
233    tag_status = UNREAD;
234
235    DEBUGLINE(DB, "Found entry: key=`" << hex_encode(current_key) << "'");
236    RETURN(found);
237}
238
239void
240FlintCursor::get_key(string * key) const
241{
242    Assert(B->level <= level);
243    Assert(is_positioned);
244
245    (void)Item_(C[0].p, C[0].c).key().read(key);
246}
247
248bool
249FlintCursor::read_tag(bool keep_compressed)
250{
251    DEBUGCALL(DB, bool, "FlintCursor::read_tag", keep_compressed);
252    if (tag_status == UNREAD) {
253        Assert(B->level <= level);
254        Assert(is_positioned);
255
256        if (B->read_tag(C, &current_tag, keep_compressed)) {
257            tag_status = COMPRESSED;
258        } else {
259            tag_status = UNCOMPRESSED;
260        }
261
262        // We need to call B->next(...) after B->read_tag(...) so that the
263        // cursor ends up on the next key.
264        is_positioned = B->next(C, 0);
265
266        DEBUGLINE(DB, "tag=`" << hex_encode(current_tag) << "'");
267    }
268    return (tag_status == COMPRESSED);
269}
270
271bool
272FlintCursor::del()
273{
274    Assert(!is_after_end);
275
276    B->del(current_key);
277
278    // If we're iterating an older revision of the tree, then the deletion
279    // happens in a new (uncommitted) revision and the cursor still sees
280    // the deleted key.  But if we're iterating the new uncommitted revision
281    // then the deleted key is no longer visible.  We need to handle both
282    // cases - either find_entry_ge() finds the deleted key or not.
283    if (!find_entry_ge(current_key)) return is_positioned;
284    return next();
285}
Note: See TracBrowser for help on using the browser.