00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <config.h>
00024
00025 #include "safeerrno.h"
00026 #ifdef __WIN32__
00027 # include "msvc_posix_wrapper.h"
00028 #endif
00029
00030 #include "flint_btreebase.h"
00031 #include "flint_utils.h"
00032 #include "io_utils.h"
00033 #include "str.h"
00034 #include "utils.h"
00035 #include <xapian/error.h>
00036 #include "omassert.h"
00037
00038 #include <climits>
00039
00040 #include <algorithm>
00041 #include <cstring>
00042
00043 using namespace std;
00044
00045
00046
00074 #define CURR_FORMAT 5U
00075
00076 FlintTable_base::FlintTable_base()
00077 : revision(0),
00078 block_size(0),
00079 root(0),
00080 level(0),
00081 bit_map_size(0),
00082 item_count(0),
00083 last_block(0),
00084 have_fakeroot(false),
00085 sequential(false),
00086 bit_map_low(0),
00087 bit_map0(0),
00088 bit_map(0)
00089 {
00090 }
00091
00092 void
00093 FlintTable_base::swap(FlintTable_base &other)
00094 {
00095 std::swap(revision, other.revision);
00096 std::swap(block_size, other.block_size);
00097 std::swap(root, other.root);
00098 std::swap(level, other.level);
00099 std::swap(bit_map_size, other.bit_map_size);
00100 std::swap(item_count, other.item_count);
00101 std::swap(last_block, other.last_block);
00102 std::swap(have_fakeroot, other.have_fakeroot);
00103 std::swap(sequential, other.sequential);
00104 std::swap(bit_map_low, other.bit_map_low);
00105 std::swap(bit_map0, other.bit_map0);
00106 std::swap(bit_map, other.bit_map);
00107 }
00108
00109 FlintTable_base::~FlintTable_base()
00110 {
00111 delete [] bit_map;
00112 bit_map = 0;
00113 delete [] bit_map0;
00114 bit_map0 = 0;
00115 }
00116
00117 bool
00118 FlintTable_base::do_unpack_uint(const char **start, const char *end,
00119 uint4 *dest, string &err_msg,
00120 const string &basename,
00121 const char *varname)
00122 {
00123 bool result = F_unpack_uint(start, end, dest);
00124 if (!result) {
00125 err_msg += "Unable to read " + string(varname) + " from " +
00126 basename + "\n";
00127 }
00128 return result;
00129 }
00130
00131 #define DO_UNPACK_UINT_ERRCHECK(start, end, var) \
00132 do { \
00133 if (!do_unpack_uint(start, end, &var, err_msg, basename, #var)) { \
00134 return false; \
00135 } \
00136 } while(0)
00137
00138
00139
00140
00141
00142
00143
00144 #define REASONABLE_BASE_SIZE 1024
00145
00146 bool
00147 FlintTable_base::read(const string & name, char ch, bool read_bitmap,
00148 string &err_msg)
00149 {
00150 string basename = name + "base" + ch;
00151 #ifdef __WIN32__
00152 int h = msvc_posix_open(basename.c_str(), O_RDONLY | O_BINARY);
00153 #else
00154 int h = open(basename.c_str(), O_RDONLY | O_BINARY);
00155 #endif
00156
00157 if (h == -1) {
00158 err_msg += "Couldn't open " + basename + ": " + strerror(errno) + "\n";
00159 return false;
00160 }
00161 fdcloser closefd(h);
00162
00163 char buf[REASONABLE_BASE_SIZE];
00164
00165 const char *start = buf;
00166 const char *end = buf + io_read(h, buf, REASONABLE_BASE_SIZE, 0);
00167
00168 DO_UNPACK_UINT_ERRCHECK(&start, end, revision);
00169 uint4 format;
00170 DO_UNPACK_UINT_ERRCHECK(&start, end, format);
00171 if (format != CURR_FORMAT) {
00172 err_msg += "Bad base file format " + str(format) + " in " +
00173 basename + "\n";
00174 return false;
00175 }
00176 DO_UNPACK_UINT_ERRCHECK(&start, end, block_size);
00177 DO_UNPACK_UINT_ERRCHECK(&start, end, root);
00178 DO_UNPACK_UINT_ERRCHECK(&start, end, level);
00179 DO_UNPACK_UINT_ERRCHECK(&start, end, bit_map_size);
00180 DO_UNPACK_UINT_ERRCHECK(&start, end, item_count);
00181 DO_UNPACK_UINT_ERRCHECK(&start, end, last_block);
00182 uint4 have_fakeroot_;
00183 DO_UNPACK_UINT_ERRCHECK(&start, end, have_fakeroot_);
00184 have_fakeroot = have_fakeroot_;
00185
00186 uint4 sequential_;
00187 DO_UNPACK_UINT_ERRCHECK(&start, end, sequential_);
00188 sequential = sequential_;
00189
00190 if (have_fakeroot && !sequential) {
00191 sequential = true;
00192
00193
00194
00195
00196
00197
00198
00199 }
00200
00201 uint4 revision2;
00202 DO_UNPACK_UINT_ERRCHECK(&start, end, revision2);
00203 if (revision != revision2) {
00204 err_msg += "Revision number mismatch in " +
00205 basename + ": " +
00206 str(revision) + " vs " + str(revision2) + "\n";
00207 return false;
00208 }
00209
00210
00211 delete [] bit_map0;
00212 bit_map0 = 0;
00213 delete [] bit_map;
00214 bit_map = 0;
00215
00216 if (!read_bitmap)
00217 return true;
00218
00219 bit_map0 = new byte[bit_map_size];
00220 bit_map = new byte[bit_map_size];
00221
00222 size_t n = end - start;
00223 if (n < bit_map_size) {
00224 memcpy(bit_map0, start, n);
00225 (void)io_read(h, reinterpret_cast<char *>(bit_map0) + n,
00226 bit_map_size - n, bit_map_size - n);
00227 n = 0;
00228 } else {
00229 memcpy(bit_map0, start, bit_map_size);
00230 n -= bit_map_size;
00231 if (n) memmove(buf, start + bit_map_size, n);
00232 }
00233 memcpy(bit_map, bit_map0, bit_map_size);
00234
00235 start = buf;
00236 end = buf + n;
00237 end += io_read(h, buf + n, REASONABLE_BASE_SIZE - n, 0);
00238
00239 uint4 revision3;
00240 if (!F_unpack_uint(&start, end, &revision3)) {
00241 err_msg += "Couldn't read revision3 from base file " +
00242 basename + "\n";
00243 return false;
00244 }
00245
00246 if (revision != revision3) {
00247 err_msg += "Revision number mismatch in " +
00248 basename + ": " +
00249 str(revision) + " vs " + str(revision3) + "\n";
00250 return false;
00251 }
00252
00253 if (start != end) {
00254 err_msg += "Junk at end of base file " + basename + "\n";
00255 return false;
00256 }
00257
00258 return true;
00259 }
00260
00261 void
00262 FlintTable_base::write_to_file(const string &filename,
00263 char base_letter,
00264 const string &tablename,
00265 int changes_fd,
00266 const string * changes_tail)
00267 {
00268 calculate_last_block();
00269
00270 string buf;
00271 buf += F_pack_uint(revision);
00272 buf += F_pack_uint(CURR_FORMAT);
00273 buf += F_pack_uint(block_size);
00274 buf += F_pack_uint(static_cast<uint4>(root));
00275 buf += F_pack_uint(static_cast<uint4>(level));
00276 buf += F_pack_uint(static_cast<uint4>(bit_map_size));
00277 buf += F_pack_uint(static_cast<uint4>(item_count));
00278 buf += F_pack_uint(static_cast<uint4>(last_block));
00279 buf += F_pack_uint(have_fakeroot);
00280 buf += F_pack_uint(sequential);
00281 buf += F_pack_uint(revision);
00282 if (bit_map_size > 0) {
00283 buf.append(reinterpret_cast<const char *>(bit_map), bit_map_size);
00284 }
00285 buf += F_pack_uint(revision);
00286
00287 #ifdef __WIN32__
00288 int h = msvc_posix_open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
00289 #else
00290 int h = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
00291 #endif
00292 if (h < 0) {
00293 string message = string("Couldn't open base ")
00294 + filename + " to write: " + strerror(errno);
00295 throw Xapian::DatabaseOpeningError(message);
00296 }
00297 fdcloser closefd(h);
00298
00299 if (changes_fd >= 0) {
00300 string changes_buf;
00301 changes_buf += F_pack_uint(1u);
00302 changes_buf += F_pack_string(tablename);
00303 changes_buf += base_letter;
00304 changes_buf += F_pack_uint(buf.size());
00305 io_write(changes_fd, changes_buf.data(), changes_buf.size());
00306 io_write(changes_fd, buf.data(), buf.size());
00307 if (changes_tail != NULL) {
00308 io_write(changes_fd, changes_tail->data(), changes_tail->size());
00309
00310 io_sync(changes_fd);
00311 }
00312 }
00313
00314 io_write(h, buf.data(), buf.size());
00315 io_sync(h);
00316 }
00317
00318
00319
00320
00321
00322
00323 bool
00324 FlintTable_base::block_free_at_start(uint4 n) const
00325 {
00326 size_t i = n / CHAR_BIT;
00327 int bit = 0x1 << n % CHAR_BIT;
00328 return (bit_map0[i] & bit) == 0;
00329 }
00330
00331
00332
00333
00334
00335
00336 void
00337 FlintTable_base::free_block(uint4 n)
00338 {
00339 uint4 i = n / CHAR_BIT;
00340 int bit = 0x1 << n % CHAR_BIT;
00341 bit_map[i] &= ~ bit;
00342
00343 if (bit_map_low > i)
00344 if ((bit_map0[i] & bit) == 0)
00345 bit_map_low = i;
00346 }
00347
00348
00349
00350
00351
00352
00353
00354 #define BIT_MAP_INC 1000
00355
00356
00357 void
00358 FlintTable_base::extend_bit_map()
00359 {
00360 int n = bit_map_size + BIT_MAP_INC;
00361 byte *new_bit_map0 = 0;
00362 byte *new_bit_map = 0;
00363
00364 try {
00365 new_bit_map0 = new byte[n];
00366 new_bit_map = new byte[n];
00367
00368 memcpy(new_bit_map0, bit_map0, bit_map_size);
00369 memset(new_bit_map0 + bit_map_size, 0, n - bit_map_size);
00370
00371 memcpy(new_bit_map, bit_map, bit_map_size);
00372 memset(new_bit_map + bit_map_size, 0, n - bit_map_size);
00373 } catch (...) {
00374 delete [] new_bit_map0;
00375 delete [] new_bit_map;
00376 throw;
00377 }
00378 delete [] bit_map0;
00379 bit_map0 = new_bit_map0;
00380 delete [] bit_map;
00381 bit_map = new_bit_map;
00382 bit_map_size = n;
00383 }
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395 uint4
00396 FlintTable_base::next_free_block()
00397 {
00398 uint4 i;
00399 int x;
00400 for (i = bit_map_low;; i++) {
00401 if (i >= bit_map_size) {
00402 extend_bit_map();
00403 }
00404 x = bit_map0[i] | bit_map[i];
00405 if (x != UCHAR_MAX) break;
00406 }
00407 uint4 n = i * CHAR_BIT;
00408 int d = 0x1;
00409 while ((x & d) != 0) { d <<= 1; n++; }
00410 bit_map[i] |= d;
00411 bit_map_low = i;
00412 if (n > last_block) {
00413 last_block = n;
00414 }
00415 return n;
00416 }
00417
00418 bool
00419 FlintTable_base::find_changed_block(uint4 * n)
00420 {
00421
00422
00423 while ((*n) <= last_block) {
00424 size_t offset = (*n) / CHAR_BIT;
00425 int bit = 0x1 << (*n) % CHAR_BIT;
00426
00427 if (((bit_map0[offset] & bit) == 0) && ((bit_map[offset] & bit) != 0)) {
00428 return true;
00429 }
00430 ++(*n);
00431 }
00432
00433 return false;
00434 }
00435
00436 bool
00437 FlintTable_base::block_free_now(uint4 n)
00438 {
00439 uint4 i = n / CHAR_BIT;
00440 int bit = 0x1 << n % CHAR_BIT;
00441 return (bit_map[i] & bit) == 0;
00442 }
00443
00444 void
00445 FlintTable_base::calculate_last_block()
00446 {
00447 if (bit_map_size == 0) {
00448 last_block = 0;
00449 return;
00450 }
00451 int i = bit_map_size - 1;
00452 while (bit_map[i] == 0 && i > 0) {
00453 i--;
00454 }
00455 bit_map_size = i + 1;
00456
00457 int x = bit_map[i];
00458
00459
00460 if (x == 0) {
00461 last_block = 0;
00462 return;
00463 }
00464 uint4 n = (i + 1) * CHAR_BIT - 1;
00465 int d = 0x1 << (CHAR_BIT - 1);
00466 while ((x & d) == 0) { d >>= 1; n--; }
00467
00468 last_block = n;
00469 }
00470
00471 bool
00472 FlintTable_base::is_empty() const
00473 {
00474 for (uint4 i = 0; i < bit_map_size; i++) {
00475 if (bit_map[i] != 0) {
00476 return false;
00477 }
00478 }
00479 return true;
00480 }
00481
00482 void
00483 FlintTable_base::clear_bit_map()
00484 {
00485 memset(bit_map, 0, bit_map_size);
00486 }
00487
00488
00489 void
00490 FlintTable_base::commit()
00491 {
00492 memcpy(bit_map0, bit_map, bit_map_size);
00493 bit_map_low = 0;
00494 }