backends/flint/flint_cursor.cc

Go to the documentation of this file.
00001 /* flint_cursor.cc: Btree cursor implementation
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2002,2003,2004,2005,2006,2007,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 <xapian/error.h>
00027 
00028 #include "flint_cursor.h"
00029 #include "flint_table.h"
00030 #include "flint_btreeutil.h"
00031 #include "omassert.h"
00032 #include "omdebug.h"
00033 
00034 #ifdef XAPIAN_DEBUG_VERBOSE
00035 static string
00036 hex_encode(const string & input)
00037 {
00038     const char * table = "0123456789abcdef";
00039     string result;
00040     for (string::const_iterator i = input.begin(); i != input.end(); ++i) {
00041         unsigned char val = *i;
00042         result += "\\x";
00043         result += table[val/16];
00044         result += table[val%16];
00045     }
00046 
00047     return result;
00048 }
00049 #endif
00050 
00051 #define DIR_START        11
00052 
00053 FlintCursor::FlintCursor(FlintTable *B_)
00054         : is_positioned(false),
00055           is_after_end(false),
00056           tag_status(UNREAD),
00057           B(B_),
00058           version(B_->cursor_version),
00059           level(B_->level)
00060 {
00061     B->cursor_created_since_last_modification = true;
00062     C = new Cursor_[level + 1];
00063 
00064     for (int j = 0; j < level; j++) {
00065         C[j].n = BLK_UNUSED;
00066         C[j].p = new byte[B->block_size];
00067     }
00068     C[level].n = B->C[level].n;
00069     C[level].p = B->C[level].p;
00070 }
00071 
00072 void
00073 FlintCursor::rebuild()
00074 {
00075     int new_level = B->level;
00076     if (new_level <= level) {
00077         for (int i = 0; i < new_level; i++) {
00078             C[i].n = BLK_UNUSED;
00079         }
00080         for (int j = new_level; j < level; ++j) {
00081             delete C[j].p;
00082         }
00083     } else {
00084         Cursor_ * old_C = C;
00085         C = new Cursor_[new_level + 1];
00086         for (int i = 0; i < level; i++) {
00087             C[i].p = old_C[i].p;
00088             C[i].n = BLK_UNUSED;
00089         }
00090         delete old_C;
00091         for (int j = level; j < new_level; j++) {
00092             C[j].p = new byte[B->block_size];
00093             C[j].n = BLK_UNUSED;
00094         }
00095     }
00096     level = new_level;
00097     C[level].n = B->C[level].n;
00098     C[level].p = B->C[level].p;
00099     version = B->cursor_version;
00100 }
00101 
00102 FlintCursor::~FlintCursor()
00103 {
00104     // Use the value of level stored in the cursor rather than the
00105     // Btree, since the Btree might have been deleted already.
00106     for (int j = 0; j < level; j++) {
00107         delete [] C[j].p;
00108     }
00109     delete [] C;
00110 }
00111 
00112 bool
00113 FlintCursor::prev()
00114 {
00115     DEBUGCALL(DB, bool, "FlintCursor::prev", "");
00116     Assert(!is_after_end);
00117     if (B->cursor_version != version || !is_positioned) {
00118         // Either the cursor needs rebuilding (in which case find_entry() will
00119         // call rebuild() and then reposition the cursor), or we've read the
00120         // last key and tag, and we're now not positioned (in which case we
00121         // seek to the current key, and then it's as if we read the key but not
00122         // the tag).
00123         if (!find_entry(current_key)) {
00124             // Exact entry was no longer there after rebuild(), and we've
00125             // automatically ended up on the entry before it.
00126             RETURN(true);
00127         }
00128     } else if (tag_status != UNREAD) {
00129         while (true) {
00130             if (! B->prev(C, 0)) {
00131                 is_positioned = false;
00132                 return false;
00133             }
00134             if (Item_(C[0].p, C[0].c).component_of() == 1) {
00135                 break;
00136             }
00137         }
00138     }
00139 
00140     while (true) {
00141         if (! B->prev(C, 0)) {
00142             is_positioned = false;
00143             return false;
00144         }
00145         if (Item_(C[0].p, C[0].c).component_of() == 1) {
00146             break;
00147         }
00148     }
00149     get_key(&current_key);
00150     tag_status = UNREAD;
00151 
00152     DEBUGLINE(DB, "Moved to entry: key=`" << hex_encode(current_key) << "'");
00153     return true;
00154 }
00155 
00156 bool
00157 FlintCursor::next()
00158 {
00159     DEBUGCALL(DB, bool, "FlintCursor::next", "");
00160     Assert(!is_after_end);
00161     if (B->cursor_version != version) {
00162         // find_entry() will call rebuild().
00163         (void)find_entry(current_key);
00164     } else if (tag_status == UNREAD) {
00165         while (true) {
00166             if (! B->next(C, 0)) {
00167                 is_positioned = false;
00168                 break;
00169             }
00170             if (Item_(C[0].p, C[0].c).component_of() == 1) {
00171                 is_positioned = true;
00172                 break;
00173             }
00174         }
00175     }
00176 
00177     if (!is_positioned) {
00178         is_after_end = true;
00179         return false;
00180     }
00181 
00182     get_key(&current_key);
00183     tag_status = UNREAD;
00184 
00185     DEBUGLINE(DB, "Moved to entry: key=`" << hex_encode(current_key) << "'");
00186     return true;
00187 }
00188 
00189 bool
00190 FlintCursor::find_entry(const string &key)
00191 {
00192     DEBUGCALL(DB, bool, "FlintCursor::find_entry", key);
00193     if (B->cursor_version != version) {
00194         rebuild();
00195     }
00196 
00197     is_after_end = false;
00198 
00199     bool found;
00200 
00201     is_positioned = true;
00202     if (key.size() > FLINT_BTREE_MAX_KEY_LEN) {
00203         // Can't find key - too long to possibly be present, so find the
00204         // truncated form but ignore "found".
00205         B->form_key(key.substr(0, FLINT_BTREE_MAX_KEY_LEN));
00206         (void)(B->find(C));
00207         found = false;
00208     } else {
00209         B->form_key(key);
00210         found = B->find(C);
00211     }
00212 
00213     if (!found) {
00214         if (C[0].c < DIR_START) {
00215             C[0].c = DIR_START;
00216             if (! B->prev(C, 0)) goto done;
00217         }
00218         while (Item_(C[0].p, C[0].c).component_of() != 1) {
00219             if (! B->prev(C, 0)) {
00220                 is_positioned = false;
00221                 throw Xapian::DatabaseCorruptError("find_entry failed to find any entry at all!");
00222             }
00223         }
00224     }
00225 done:
00226 
00227     if (found)
00228         current_key = key;
00229     else
00230         get_key(&current_key);
00231     tag_status = UNREAD;
00232 
00233     DEBUGLINE(DB, "Found entry: key=`" << hex_encode(current_key) << "'");
00234     RETURN(found);
00235 }
00236 
00237 bool
00238 FlintCursor::find_entry_ge(const string &key)
00239 {
00240     DEBUGCALL(DB, bool, "FlintCursor::find_entry_ge", key);
00241     if (B->cursor_version != version) {
00242         rebuild();
00243     }
00244 
00245     is_after_end = false;
00246 
00247     bool found;
00248 
00249     is_positioned = true;
00250     if (key.size() > FLINT_BTREE_MAX_KEY_LEN) {
00251         // Can't find key - too long to possibly be present, so find the
00252         // truncated form but ignore "found".
00253         B->form_key(key.substr(0, FLINT_BTREE_MAX_KEY_LEN));
00254         (void)(B->find(C));
00255         found = false;
00256     } else {
00257         B->form_key(key);
00258         found = B->find(C);
00259     }
00260 
00261     if (found) {
00262         current_key = key;
00263     } else {
00264         if (! B->next(C, 0)) {
00265             is_after_end = true;
00266             is_positioned = false;
00267             RETURN(false);
00268         }
00269         Assert(Item_(C[0].p, C[0].c).component_of() == 1);
00270         get_key(&current_key);
00271     }
00272     tag_status = UNREAD;
00273 
00274     DEBUGLINE(DB, "Found entry: key=`" << hex_encode(current_key) << "'");
00275     RETURN(found);
00276 }
00277 
00278 void
00279 FlintCursor::get_key(string * key) const
00280 {
00281     Assert(B->level <= level);
00282     Assert(is_positioned);
00283 
00284     (void)Item_(C[0].p, C[0].c).key().read(key);
00285 }
00286 
00287 bool
00288 FlintCursor::read_tag(bool keep_compressed)
00289 {
00290     DEBUGCALL(DB, bool, "FlintCursor::read_tag", keep_compressed);
00291     if (tag_status == UNREAD) {
00292         Assert(B->level <= level);
00293         Assert(is_positioned);
00294 
00295         if (B->read_tag(C, &current_tag, keep_compressed)) {
00296             tag_status = COMPRESSED;
00297         } else {
00298             tag_status = UNCOMPRESSED;
00299         }
00300 
00301         // We need to call B->next(...) after B->read_tag(...) so that the
00302         // cursor ends up on the next key.
00303         is_positioned = B->next(C, 0);
00304 
00305         DEBUGLINE(DB, "tag=`" << hex_encode(current_tag) << "'");
00306     }
00307     return (tag_status == COMPRESSED);
00308 }
00309 
00310 bool
00311 FlintCursor::del()
00312 {
00313     Assert(!is_after_end);
00314 
00315     B->del(current_key);
00316 
00317     // If we're iterating an older revision of the tree, then the deletion
00318     // happens in a new (uncommitted) revision and the cursor still sees
00319     // the deleted key.  But if we're iterating the new uncommitted revision
00320     // then the deleted key is no longer visible.  We need to handle both
00321     // cases - either find_entry_ge() finds the deleted key or not.
00322     if (!find_entry_ge(current_key)) return is_positioned;
00323     return next();
00324 }

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