00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00105
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
00119
00120
00121
00122
00123 if (!find_entry(current_key)) {
00124
00125
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(¤t_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
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(¤t_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
00204
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(¤t_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
00252
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(¤t_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, ¤t_tag, keep_compressed)) {
00296 tag_status = COMPRESSED;
00297 } else {
00298 tag_status = UNCOMPRESSED;
00299 }
00300
00301
00302
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
00318
00319
00320
00321
00322 if (!find_entry_ge(current_key)) return is_positioned;
00323 return next();
00324 }