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