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 "chert_cursor.h"
00025
00026 #include "safeerrno.h"
00027
00028 #include <xapian/error.h>
00029
00030 #include "chert_table.h"
00031 #include "debuglog.h"
00032 #include "omassert.h"
00033
00034 #ifdef XAPIAN_DEBUG_LOG
00035 static string
00036 hex_display_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 ChertCursor::ChertCursor(const ChertTable *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 ChertCursor::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 ChertCursor::~ChertCursor()
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 ChertCursor::prev()
00114 {
00115 LOGCALL(DB, bool, "ChertCursor::prev", NO_ARGS);
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 LOGLINE(DB, "Moved to entry: key=" << hex_display_encode(current_key));
00153 RETURN(true);
00154 }
00155
00156 bool
00157 ChertCursor::next()
00158 {
00159 LOGCALL(DB, bool, "ChertCursor::next", NO_ARGS);
00160 Assert(!is_after_end);
00161 if (B->cursor_version != version) {
00162
00163 (void)find_entry(current_key);
00164
00165
00166
00167 }
00168 if (tag_status == UNREAD) {
00169 while (true) {
00170 if (! B->next(C, 0)) {
00171 is_positioned = false;
00172 break;
00173 }
00174 if (Item(C[0].p, C[0].c).component_of() == 1) {
00175 is_positioned = true;
00176 break;
00177 }
00178 }
00179 }
00180
00181 if (!is_positioned) {
00182 is_after_end = true;
00183 RETURN(false);
00184 }
00185
00186 get_key(¤t_key);
00187 tag_status = UNREAD;
00188
00189 LOGLINE(DB, "Moved to entry: key=" << hex_display_encode(current_key));
00190 RETURN(true);
00191 }
00192
00193 bool
00194 ChertCursor::find_entry(const string &key)
00195 {
00196 LOGCALL(DB, bool, "ChertCursor::find_entry", key);
00197 if (B->cursor_version != version) {
00198 rebuild();
00199 }
00200
00201 is_after_end = false;
00202
00203 bool found;
00204
00205 is_positioned = true;
00206 if (key.size() > CHERT_BTREE_MAX_KEY_LEN) {
00207
00208
00209 B->form_key(key.substr(0, CHERT_BTREE_MAX_KEY_LEN));
00210 (void)(B->find(C));
00211 found = false;
00212 } else {
00213 B->form_key(key);
00214 found = B->find(C);
00215 }
00216
00217 if (!found) {
00218 if (C[0].c < DIR_START) {
00219 C[0].c = DIR_START;
00220 if (! B->prev(C, 0)) goto done;
00221 }
00222 while (Item(C[0].p, C[0].c).component_of() != 1) {
00223 if (! B->prev(C, 0)) {
00224 is_positioned = false;
00225 throw Xapian::DatabaseCorruptError("find_entry failed to find any entry at all!");
00226 }
00227 }
00228 }
00229 done:
00230
00231 if (found)
00232 current_key = key;
00233 else
00234 get_key(¤t_key);
00235 tag_status = UNREAD;
00236
00237 LOGLINE(DB, "Found entry: key=" << hex_display_encode(current_key));
00238 RETURN(found);
00239 }
00240
00241 bool
00242 ChertCursor::find_entry_ge(const string &key)
00243 {
00244 LOGCALL(DB, bool, "ChertCursor::find_entry_ge", key);
00245 if (B->cursor_version != version) {
00246 rebuild();
00247 }
00248
00249 is_after_end = false;
00250
00251 bool found;
00252
00253 is_positioned = true;
00254 if (key.size() > CHERT_BTREE_MAX_KEY_LEN) {
00255
00256
00257 B->form_key(key.substr(0, CHERT_BTREE_MAX_KEY_LEN));
00258 (void)(B->find(C));
00259 found = false;
00260 } else {
00261 B->form_key(key);
00262 found = B->find(C);
00263 }
00264
00265 if (found) {
00266 current_key = key;
00267 } else {
00268 if (! B->next(C, 0)) {
00269 is_after_end = true;
00270 is_positioned = false;
00271 RETURN(false);
00272 }
00273 Assert(Item(C[0].p, C[0].c).component_of() == 1);
00274 get_key(¤t_key);
00275 }
00276 tag_status = UNREAD;
00277
00278 LOGLINE(DB, "Found entry: key=" << hex_display_encode(current_key));
00279 RETURN(found);
00280 }
00281
00282 void
00283 ChertCursor::get_key(string * key) const
00284 {
00285 Assert(B->level <= level);
00286 Assert(is_positioned);
00287
00288 (void)Item(C[0].p, C[0].c).key().read(key);
00289 }
00290
00291 bool
00292 ChertCursor::read_tag(bool keep_compressed)
00293 {
00294 LOGCALL(DB, bool, "ChertCursor::read_tag", keep_compressed);
00295 if (tag_status == UNREAD) {
00296 Assert(B->level <= level);
00297 Assert(is_positioned);
00298
00299 if (B->read_tag(C, ¤t_tag, keep_compressed)) {
00300 tag_status = COMPRESSED;
00301 } else {
00302 tag_status = UNCOMPRESSED;
00303 }
00304
00305
00306
00307 is_positioned = B->next(C, 0);
00308
00309 LOGLINE(DB, "tag=" << hex_display_encode(current_tag));
00310 }
00311 RETURN(tag_status == COMPRESSED);
00312 }
00313
00314 bool
00315 MutableChertCursor::del()
00316 {
00317 Assert(!is_after_end);
00318
00319
00320
00321
00322
00323 (const_cast<ChertTable*>(B))->del(current_key);
00324
00325
00326
00327
00328
00329
00330 if (!find_entry_ge(current_key)) return is_positioned;
00331 return next();
00332 }