backends/quartz/bcursor.cc

Go to the documentation of this file.
00001 /* bcursor.cc: Btree cursor implementation
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2002,2003,2004,2005,2006,2010 Olly Betts
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License as
00008  * published by the Free Software Foundation; either version 2 of the
00009  * License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00019  * USA
00020  */
00021 
00022 #include <config.h>
00023 
00024 #include "safeerrno.h"
00025 
00026 #include "bcursor.h"
00027 #include "btree.h"
00028 #include "btree_util.h"
00029 #include "omassert.h"
00030 #include "omdebug.h"
00031 
00032 #ifdef XAPIAN_DEBUG_VERBOSE
00033 static string
00034 hex_encode(const string & input)
00035 {
00036     const char * table = "0123456789abcdef";
00037     string result;
00038     for (string::const_iterator i = input.begin(); i != input.end(); ++i) {
00039         unsigned char val = *i;
00040         result += "\\x";
00041         result += table[val/16];
00042         result += table[val%16];
00043     }
00044 
00045     return result;
00046 }
00047 #endif
00048 
00049 Bcursor::Bcursor(Btree *B_)
00050         : is_positioned(false),
00051           is_after_end(false),
00052           have_read_tag(false),
00053           B(B_),
00054           version(B_->cursor_version),
00055           level(B_->level)
00056 {
00057     B->cursor_created_since_last_modification = true;
00058     C = new Cursor[level + 1];
00059 
00060     for (int j = 0; j < level; j++) {
00061         C[j].n = BLK_UNUSED;
00062         C[j].p = new byte[B->block_size];
00063     }
00064     C[level].n = B->C[level].n;
00065     C[level].p = B->C[level].p;
00066 }
00067 
00068 void
00069 Bcursor::rebuild()
00070 {
00071     int new_level = B->level;
00072     if (new_level <= level) {
00073         for (int i = 0; i < new_level; i++) {
00074             C[i].n = BLK_UNUSED;
00075         }
00076         for (int j = new_level; j < level; ++j) {
00077             delete C[j].p;
00078         }
00079     } else {
00080         Cursor * old_C = C;
00081         C = new Cursor[new_level + 1];
00082         for (int i = 0; i < level; i++) {
00083             C[i].p = old_C[i].p;
00084             C[i].n = BLK_UNUSED;
00085         }
00086         delete old_C;
00087         for (int j = level; j < new_level; j++) {
00088             C[j].p = new byte[B->block_size];
00089             C[j].n = BLK_UNUSED;
00090         }
00091     }
00092     level = new_level;
00093     C[level].n = B->C[level].n;
00094     C[level].p = B->C[level].p;
00095     version = B->cursor_version;
00096 }
00097 
00098 Bcursor::~Bcursor()
00099 {
00100     // Use the value of level stored in the cursor rather than the
00101     // Btree, since the Btree might have been deleted already.
00102     for (int j = 0; j < level; j++) {
00103         delete [] C[j].p;
00104     }
00105     delete [] C;
00106 }
00107 
00108 bool
00109 Bcursor::prev()
00110 {
00111     DEBUGCALL(DB, bool, "Bcursor::prev", "");
00112     Assert(!is_after_end);
00113     if (B->cursor_version != version || !is_positioned) {
00114         // Either the cursor needs rebuilding (in which case find_entry() will
00115         // call rebuild() and then reposition the cursor), or we've read the
00116         // last key and tag, and we're now not positioned (in which case we
00117         // seek to the current key, and then it's as if we read the key but not
00118         // the tag).
00119         if (!find_entry(current_key)) {
00120             // Exact entry was no longer there after rebuild(), and we've
00121             // automatically ended up on the entry before it.
00122             RETURN(true);
00123         }
00124     } else if (have_read_tag) {
00125         while (true) {
00126             if (! B->prev(C, 0)) {
00127                 is_positioned = false;
00128                 return false;
00129             }
00130             if (Item(C[0].p, C[0].c).component_of() == 1) {
00131                 break;
00132             }
00133         }
00134     }
00135 
00136     while (true) {
00137         if (! B->prev(C, 0)) {
00138             is_positioned = false;
00139             return false;
00140         }
00141         if (Item(C[0].p, C[0].c).component_of() == 1) {
00142             break;
00143         }
00144     }
00145     get_key(&current_key);
00146     // FIXME: check for errors
00147     have_read_tag = false;
00148 
00149     DEBUGLINE(DB, "Moved to entry: key=`" << hex_encode(current_key) << "'");
00150     return true;
00151 }
00152 
00153 bool
00154 Bcursor::next()
00155 {
00156     DEBUGCALL(DB, bool, "Bcursor::next", "");
00157     Assert(!is_after_end);
00158     if (B->cursor_version != version) {
00159         // find_entry() will call rebuild().
00160         (void)find_entry(current_key);
00161     } else if (!have_read_tag) {
00162         while (true) {
00163             if (! B->next(C, 0)) {
00164                 is_positioned = false;
00165                 break;
00166             }
00167             if (Item(C[0].p, C[0].c).component_of() == 1) {
00168                 is_positioned = true;
00169                 break;
00170             }
00171         }
00172     }
00173 
00174     if (!is_positioned) {
00175         is_after_end = true;
00176         return false;
00177     }
00178 
00179     get_key(&current_key);
00180     // FIXME: check for errors
00181     have_read_tag = false;
00182 
00183     DEBUGLINE(DB, "Moved to entry: key=`" << hex_encode(current_key) << "'");
00184     return true;
00185 }
00186 
00187 bool
00188 Bcursor::find_entry(const string &key)
00189 {
00190     DEBUGCALL(DB, bool, "Bcursor::find_entry", key);
00191     if (B->cursor_version != version) {
00192         rebuild();
00193     }
00194 
00195     is_after_end = false;
00196 
00197     bool found;
00198 
00199     if (key.size() > BTREE_MAX_KEY_LEN) {
00200         is_positioned = true;
00201         // Can't find key - too long to possibly be present, so find the
00202         // truncated form but ignore "found".
00203         B->form_key(key.substr(0, BTREE_MAX_KEY_LEN));
00204         (void)(B->find(C));
00205         found = false;
00206     } else {
00207         is_positioned = true;
00208         B->form_key(key);
00209         found = B->find(C);
00210     }
00211 
00212     if (!found) {
00213         if (C[0].c < DIR_START) {
00214             C[0].c = DIR_START;
00215             if (! B->prev(C, 0)) goto done;
00216         }
00217         while (Item(C[0].p, C[0].c).component_of() != 1) {
00218             if (! B->prev(C, 0)) {
00219                 is_positioned = false;
00220                 break;
00221             }
00222         }
00223     }
00224 done:
00225 
00226     bool err = get_key(&current_key);
00227     (void)err; // FIXME: check for errors
00228     have_read_tag = false;
00229 
00230     DEBUGLINE(DB, "Found entry: key=`" << hex_encode(current_key) << "'");
00231 
00232     RETURN(found);
00233 }
00234 
00235 bool
00236 Bcursor::get_key(string * key) const
00237 {
00238     Assert(B->level <= level);
00239 
00240     if (!is_positioned) return false;
00241 
00242     (void)Item(C[0].p, C[0].c).key().read(key);
00243     return true;
00244 }
00245 
00246 void
00247 Bcursor::read_tag()
00248 {
00249     DEBUGCALL(DB, void, "Bcursor::read_tag", "");
00250     if (have_read_tag) return;
00251 
00252     Assert(B->level <= level);
00253     Assert(is_positioned);
00254 
00255     B->read_tag(C, &current_tag);
00256 
00257     // We need to call B->next(...) after B->read_tag(...) so that the
00258     // cursor ends up on the next key.
00259     is_positioned = B->next(C, 0);
00260 
00261     have_read_tag = true;
00262 
00263     DEBUGLINE(DB, "tag=`" << hex_encode(current_tag) << "'");
00264 }
00265 
00266 void
00267 Bcursor::del()
00268 {
00269     Assert(!is_after_end);
00270 
00271     // FIXME: this isn't the most efficient approach, but I struggled to
00272     // make the obvious approaches work.
00273     B->del(current_key);
00274     find_entry(current_key);
00275     next();
00276 
00277     DEBUGLINE(DB, "Moved to entry: key=`" << hex_encode(current_key) << "'");
00278 }

Documentation for Xapian (version 1.0.20).
Generated on 28 Apr 2010 by Doxygen 1.5.2.