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 "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
00101
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
00115
00116
00117
00118
00119 if (!find_entry(current_key)) {
00120
00121
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(¤t_key);
00146
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
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(¤t_key);
00180
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
00202
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(¤t_key);
00227 (void)err;
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, ¤t_tag);
00256
00257
00258
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
00272
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 }