35 #include <sys/types.h>
55 #include <string_view>
59 using namespace Glass;
70 #undef BTREE_DEBUG_FULL
72 #ifdef BTREE_DEBUG_FULL
75 static void print_key(
const uint8_t *
p,
int c,
int j);
76 static void print_tag(
const uint8_t *
p,
int c,
int j);
94 uint8_t *temp =
new uint8_t[size];
95 memset(temp, 0, size);
153 #define SEQ_START_POINT (-10)
163 #define BYTE_PAIR_RANGE (1 << 2 * CHAR_BIT)
171 if (
rare(handle == -2))
173 AssertRel(n,<,free_list.get_first_unused_block());
175 io_read_block(handle,
reinterpret_cast<char *
>(
p), block_size, n, offset);
179 if (
rare(dir_end <
DIR_START ||
unsigned(dir_end) > block_size)) {
180 string msg(
"dir_end invalid in block ");
200 LOGCALL_VOID(DB,
"GlassTable::write_block", n |
p | appending);
203 AssertRel(n,<,free_list.get_first_unused_block());
227 const char * p_char =
reinterpret_cast<const char *
>(
p);
230 if (!changes_obj)
return;
234 if (strcmp(tablename,
"position") == 0) {
236 }
else if (strcmp(tablename,
"postlist") == 0) {
238 }
else if (strcmp(tablename,
"docdata") == 0) {
240 }
else if (strcmp(tablename,
"spelling") == 0) {
242 }
else if (strcmp(tablename,
"synonym") == 0) {
244 }
else if (strcmp(tablename,
"termlist") == 0) {
250 if (block_size == 2048) {
252 }
else if (block_size == 4096) {
254 }
else if (block_size == 8192) {
256 }
else if (block_size == 16384) {
258 }
else if (block_size == 32768) {
260 }
else if (block_size == 65536) {
271 changes_obj->write_block(buf);
272 changes_obj->write_block(
reinterpret_cast<const char *
>(
p), block_size);
298 LOGCALL_VOID(DB,
"GlassTable::throw_overwritten", NO_ARGS);
303 throw Xapian::DatabaseModifiedError(
"The revision being read has been discarded - you should call Xapian::Database::reopen() and retry the operation");
319 LOGCALL_VOID(DB,
"GlassTable::block_to_cursor", (
void*)C_ | j | n);
320 if (n == C_[j].get_n())
return;
322 if (writable && C_[j].rewrite) {
324 write_block(C_[j].get_n(), C_[j].get_p());
331 if (n ==
C[j].get_n()) {
334 uint8_t * q = C_[j].
init(block_size);
348 string msg =
"Expected block ";
350 msg +=
" to be level ";
390 if (
C[j].rewrite)
return;
394 if (
rev == revision_number + 1) {
399 free_list.mark_block_unused(
this, block_size, n);
400 SET_REVISION(
C[j].get_modifiable_p(block_size), revision_number + 1);
401 n = free_list.get_block(
this, block_size);
404 if (j == level)
return;
437 if (c < j && i < c) {
446 if (c < j && i < c) {
457 int k = i + ((j - i) / (
D2 * 2)) *
D2;
474 template<
typename ITEM>
int
486 if (c < j && i < c) {
488 if (r == 0)
return c;
492 if (c < j && i < c) {
494 if (r == 0)
return c;
500 int k = i + ((j - i) / (
D2 * 2)) *
D2;
538 LOGCALL(DB,
bool,
"GlassTable::find", (
void*)C_);
542 for (
int j = level; j > 0; --j) {
544 c = find_in_branch(
p, kt, C_[j].c);
545 #ifdef BTREE_DEBUG_FULL
546 printf(
"Block in GlassTable:find - code position 1");
547 report_block_full(j, C_[j].get_n(),
p);
550 block_to_cursor(C_, j - 1,
BItem(
p, c).block_given_by());
554 c = find_in_leaf(
p, kt, C_[0].c, exact);
555 #ifdef BTREE_DEBUG_FULL
556 printf(
"Block in GlassTable:find - code position 2");
557 report_block_full(0, C_[0].get_n(),
p);
575 uint8_t * b = buffer;
583 if (e < 0)
throw_corrupt(
"Leaf item size extends outside block");
593 if (e < 0)
throw_corrupt(
"Branch item size extends outside block");
598 memcpy(
p + e, b + e, block_size - e);
600 if (e < 0)
throw_corrupt(
"Items overlap with item pointers");
623 uint8_t * q =
C[level].init(block_size);
624 memset(q, 0, block_size);
626 C[level].set_n(free_list.get_block(
this, block_size));
627 C[level].rewrite =
true;
637 add_branch_item(item, level);
662 Key prevkey = previtem.
key();
663 Key newkey = newitem.
key();
666 uint4 blocknumber =
C[0].get_n();
670 const int newkey_len = newkey.
length();
676 const int min_len = min(newkey_len, prevkey.
length());
677 while (i < min_len && prevkey[i] == newkey[i]) {
682 if (i < newkey_len) i++;
692 AssertEq(
C[1].c, find_in_branch(
C[1].get_p(), item,
C[1].c));
695 add_branch_item(item, 1);
723 uint4 blocknumber =
C[j - 1].get_n();
732 AssertEq(
C[j].c, find_in_branch(
C[j].get_p(), item,
C[j].c));
735 add_branch_item(item, j);
745 LOGCALL(DB,
int,
"GlassTable::mid_point", (
void*)
p);
758 if (l < n - size)
RETURN(c);
785 int kt_len = kt_.
size();
786 int needed = kt_len +
D2;
797 memmove(
p + c +
D2,
p + c, dir_end - c);
801 int o = dir_end + new_max;
825 int kt_len = kt_.
size();
826 int needed = kt_len +
D2;
837 memmove(
p + c +
D2,
p + c, dir_end - c);
841 int o = dir_end + new_max;
860 uint8_t *
p =
C[0].get_modifiable_p(block_size);
864 int needed = kt_.
size() +
D2;
882 uint4 split_n =
C[0].get_n();
883 C[0].set_n(free_list.get_block(
this, block_size));
885 memcpy(split_p,
p, block_size);
898 bool add_to_upper_half;
900 add_to_upper_half = (c >= m);
904 add_to_upper_half = (
TOTAL_FREE(split_p) < needed);
907 if (add_to_upper_half) {
912 add_item_to_leaf(
p, kt_, c);
917 add_item_to_leaf(split_p, kt_, c);
920 write_block(split_n, split_p);
923 if (0 == level) split_root(split_n);
937 add_item_to_leaf(
p, kt_, c);
955 uint8_t *
p =
C[j].get_modifiable_p(block_size);
958 int needed = kt_.
size() +
D2;
976 uint4 split_n =
C[j].get_n();
977 C[j].set_n(free_list.get_block(
this, block_size));
979 memcpy(split_p,
p, block_size);
992 bool add_to_upper_half;
994 add_to_upper_half = (c >= m);
998 add_to_upper_half = (
TOTAL_FREE(split_p) < needed);
1001 if (add_to_upper_half) {
1006 add_item_to_branch(
p, kt_, c);
1010 add_item_to_branch(split_p, kt_, c);
1012 write_block(split_n, split_p);
1015 if (j == level) split_root(split_n);
1036 add_item_to_branch(
p, kt_, c);
1050 LOGCALL_VOID(DB,
"GlassTable::delete_leaf_item", repeatedly);
1052 uint8_t *
p =
C[0].get_modifiable_p(block_size);
1059 memmove(
p + c,
p + c +
D2, dir_end - c);
1064 if (!repeatedly)
return;
1067 free_list.mark_block_unused(
this, block_size,
C[0].get_n());
1068 C[0].rewrite =
false;
1070 C[1].rewrite =
true;
1071 delete_branch_item(1);
1087 uint8_t *
p =
C[j].get_modifiable_p(block_size);
1094 memmove(
p + c,
p + c +
D2, dir_end - c);
1101 free_list.mark_block_unused(
this, block_size,
C[j].get_n());
1102 C[j].rewrite =
false;
1104 C[j + 1].rewrite =
true;
1105 delete_branch_item(j + 1);
1112 free_list.mark_block_unused(
this, block_size,
C[level].get_n());
1116 block_to_cursor(
C, level, new_root);
1118 dir_end =
DIR_END(
C[level].get_p());
1160 LOGCALL(DB,
int,
"GlassTable::add_kt", found);
1176 uint8_t *
p =
C[0].get_modifiable_p(block_size);
1181 int kt_size = kt.size();
1182 int needed = kt_size - item.
size();
1188 memmove(
const_cast<uint8_t *
>(item.
get_address()),
1189 kt.get_address(), kt_size);
1196 memmove(
p + o, kt.get_address(), kt_size);
1202 delete_leaf_item(
false);
1208 if (changed_n ==
C[0].get_n() && changed_c ==
C[0].c) {
1209 if (seq_count < 0) seq_count++;
1233 LOGCALL(DB,
int,
"GlassTable::delete_kt", NO_ARGS);
1244 delete_leaf_item(
true);
1290 LOGCALL_VOID(DB,
"GlassTable::add", key | tag | already_compressed);
1298 root_info.
init(block_size, compress_min);
1299 do_open_to_write(&root_info);
1304 const char* tag_data = tag.data();
1305 size_t tag_size = tag.size();
1307 bool compressed =
false;
1308 if (already_compressed) {
1310 }
else if (compress_min > 0 && tag_size > compress_min) {
1311 const char * res = comp_stream.compress(tag_data, &tag_size);
1319 const size_t cd = kt.key().length() +
K1 +
I2 +
X2;
1320 const size_t L = max_item_size - cd;
1321 size_t first_L = L +
X2;
1322 bool found = find(
C);
1323 if (tag_size <= first_L) {
1327 }
else if (!found) {
1328 const uint8_t *
p =
C[0].get_p();
1344 size_t last = (tag_size -
X2) % L;
1345 if (n >= last || (full_compaction && n >= key.size() + 34)) {
1354 int m = (tag_size - first_L + L - 1) / L + 1;
1359 string message =
"Btree tag entry of size ";
1360 message +=
str(tag_size);
1361 message +=
" is too large to store - "
1362 "increase the block size to raise this limit";
1367 size_t residue = tag_size;
1368 bool replacement =
false;
1369 bool components_to_del =
false;
1371 for (i = 1; i <= m; ++i) {
1372 size_t l = (i == m ? residue : (i == 1 ? first_L : L));
1373 size_t this_cd = (i == 1 ? cd -
X2 : cd);
1374 Assert(this_cd + l <= block_size);
1375 Assert(o + l <= tag_size);
1376 kt.set_tag(this_cd, tag_data + o, l, compressed, i, m);
1381 if (i > 1) found = find(
C);
1382 int result = add_kt(found);
1383 if (result) replacement =
true;
1384 components_to_del = (result == 1);
1387 if (components_to_del) {
1390 kt.set_component_of(++i);
1391 }
while (delete_kt() == 1);
1393 if (!replacement) ++item_count;
1394 Btree_modified =
true;
1395 if (cursor_created_since_last_modification) {
1396 cursor_created_since_last_modification =
false;
1410 LOGCALL(DB,
bool,
"GlassTable::del", key);
1423 if (key.empty())
RETURN(
false);
1426 int r = delete_kt();
1427 if (r == 0)
RETURN(
false);
1430 kt.set_component_of(++i);
1435 Btree_modified =
true;
1436 if (cursor_created_since_last_modification) {
1437 cursor_created_since_last_modification =
false;
1446 LOGCALL(DB,
bool,
"GlassTable::readahead_key", key);
1473 const uint8_t *
p =
C[level].get_p();
1474 int c = find_in_branch(
p, kt,
C[level].c);
1478 if (n != last_readahead && n !=
C[level - 1].get_n()) {
1489 LOGCALL(DB,
bool,
"GlassTable::get_exact_entry", key | tag);
1505 (void)read_tag(
C, &tag,
false);
1512 LOGCALL(DB,
bool,
"GlassTable::key_exists", key);
1525 LOGCALL(DB,
bool,
"GlassTable::read_tag",
Literal(
"C_") | tag | keep_compressed);
1529 LeafItem item(C_[0].get_p(), C_[0].c);
1531 bool decompress =
false;
1532 if (compressed && !keep_compressed) {
1533 comp_stream.decompress_start();
1545 "Too many chunks of compressed data" :
1546 "Too few chunks of compressed data");
1555 item.
init(C_[0].get_p(), C_[0].c);
1560 RETURN(compressed && keep_compressed);
1566 LOGCALL_VOID(DB,
"GlassTable::set_full_compaction", parity);
1569 if (parity) seq_count = 0;
1570 full_compaction = parity;
1591 revision_number =
rev;
1598 if (!fl_serialised.empty()) {
1599 if (!free_list.unpack(fl_serialised))
1612 for (
int j = 0; j <= level; ++j) {
1613 C[j].init(block_size);
1618 if (cursor_created_since_last_modification) {
1619 cursor_created_since_last_modification =
false;
1628 if (faked_root_block) {
1630 uint8_t *
p =
C[0].init(block_size);
1636 memset(
p, 0, block_size);
1638 int o = block_size -
I2 -
K1;
1657 C[0].set_n(free_list.get_block(
this, block_size));
1658 C[0].rewrite =
true;
1662 block_to_cursor(
C, level, root);
1664 if (
REVISION(
C[level].get_p()) > revision_number) throw_overwritten();
1679 handle = -3 - handle;
1686 if (lazy &&
rev && errno == ENOENT) {
1687 revision_number =
rev;
1690 string message((
rev == 0) ?
"Couldn't create " :
"Couldn't open ");
1698 basic_open(root_info,
rev);
1700 split_p =
new uint8_t[block_size];
1710 bool readonly_,
bool lazy_)
1711 : tablename(tablename_),
1715 faked_root_block(
true),
1728 Btree_modified(
false),
1729 full_compaction(
false),
1730 writable(!readonly_),
1731 cursor_created_since_last_modification(
false),
1736 comp_stream(Z_DEFAULT_STRATEGY),
1741 LOGCALL_CTOR(DB,
"GlassTable", tablename_ | path_ | readonly_ | lazy_);
1745 bool readonly_,
bool lazy_)
1746 : tablename(tablename_),
1750 faked_root_block(
true),
1763 Btree_modified(
false),
1764 full_compaction(
false),
1765 writable(!readonly_),
1766 cursor_created_since_last_modification(
false),
1771 comp_stream(Z_DEFAULT_STRATEGY),
1776 LOGCALL_CTOR(DB,
"GlassTable", tablename_ | fd | offset_ | readonly_ | lazy_);
1781 LOGCALL(DB,
bool,
"GlassTable::exists", NO_ARGS);
1789 LOGCALL_VOID(DB,
"GlassTable::create_and_open", flags_|root_info);
1800 Assert((block_size_ & (block_size_ - 1)) == 0);
1843 for (
int j =
level; j >= 0; --j) {
1867 for (
int j =
level; j >= 0; --j) {
1912 for (
int i = 0; i <=
level; ++i) {
1963 if (!fl_serialised.empty()) {
1972 for (
int j = 0; j <=
level; ++j) {
1994 LOGCALL(DB,
bool,
"GlassTable::do_open_to_read", root_info|
rev);
2008 string message(
"Couldn't open ");
2050 if (n == 0)
RETURN(
false);
2052 if (n ==
C[0].get_n()) {
2064 for (j = 1; j <=
level; ++j) {
2065 if (n ==
C[j].get_n())
break;
2067 if (j <=
level)
continue;
2095 const uint8_t *
p = C_[0].
get_p();
2107 if (n ==
C[0].get_n()) {
2118 for (j = 1; j <=
level; ++j) {
2119 if (n ==
C[j].get_n())
break;
2121 if (j <=
level)
continue;
2150 LOGCALL(DB,
bool,
"GlassTable::prev_default",
Literal(
"C_") | j);
2151 const uint8_t *
p = C_[j].
get_p();
2174 LOGCALL(DB,
bool,
"GlassTable::next_default",
Literal(
"C_") | j);
2175 const uint8_t *
p = C_[j].
get_p();
2195 #ifdef BTREE_DEBUG_FULL
2196 printf(
"Block in GlassTable:next_default");
2197 report_block_full(j - 1, C_[j - 1].get_n(), C_[j - 1].get_p());
2209 #ifdef DISABLE_GPL_LIBXAPIAN
2210 # error GPL source we cannot relicense included in libxapian
A cursor pointing to a position in a Btree table, for reading several entries in order,...
void pack(std::string &buf)
uint4 get_first_unused_block() const
void set_revision(uint4 revision_)
void commit(const GlassTable *B, uint4 block_size)
bool unpack(const char **pstart, const char *end)
Class managing a Btree table in a Glass database.
void block_to_cursor(Glass::Cursor *C_, int j, uint4 n) const
bool Btree_modified
Set to true the first time the B-tree is modified.
uint4 changed_n
the last block to be changed by an addition
void alter()
Btree::alter(); is called when the B-tree is to be altered.
bool next_for_sequential(Glass::Cursor *C_, int dummy) const
~GlassTable()
Close the Btree.
void compact(uint8_t *p)
compact(p) compact the block at p by shuffling all the items up to the end.
GlassFreeList free_list
List of free blocks.
int seq_count
count of the number of successive instances of purely sequential addition, starting at SEQ_START_POIN...
void create_and_open(int flags_, const RootInfo &root_info)
Create a new empty btree structure on disk and open it at the initial revision.
uint8_t * buffer
buffer of size block_size for reforming blocks
void throw_overwritten() const
bool key_exists(std::string_view key) const
Check if a key exists in the Btree.
void basic_open(const RootInfo *root_info, glass_revision_number_t rev)
glass_tablesize_t item_count
keeps a count of the number of items in the B-tree.
void delete_branch_item(int j)
GlassTable::delete_branch_item(j, repeatedly) is (almost) the converse of add_branch_item.
bool faked_root_block
true if the root block is faked (not written to disk).
void commit(glass_revision_number_t revision, RootInfo *root_info)
Commit any outstanding changes to the table.
int changed_c
directory offset corresponding to last block to be changed by an addition
bool next_default(Glass::Cursor *C_, int j) const
uint4 root
the root block of the B-tree
void form_key(std::string_view key) const
void split_root(uint4 split_n)
Btree needs to gain a new level to insert more items: so split root block and construct a new one.
uint8_t * split_p
Buffer used when splitting a block.
int add_kt(bool found)
add_kt(found) adds the item (key-tag pair) at B->kt into the B-tree, using cursor C.
void do_open_to_read(const RootInfo *root_info, glass_revision_number_t rev)
Perform the opening operation to read.
int level
number of levels, counting from 0
bool exists() const
Determine whether the btree exists on disk.
void enter_key_above_branch(int j, Glass::BItem newitem)
enter_key_above_branch(j, newkey) is called after a branch block split.
void flush_db()
Flush any outstanding changes to the DB file of the table.
bool prev_default(Glass::Cursor *C_, int j) const
bool writable
Set to true when the database is opened to write.
void open(int flags_, const RootInfo &root_info, glass_revision_number_t rev)
Open the btree.
GlassTable(const GlassTable &)
Copying not allowed.
void enter_key_above_leaf(Glass::LeafItem previtem, Glass::LeafItem newitem)
enter_key_above_leaf(previtem, newitem) is called after a leaf block split.
bool find(Glass::Cursor *) const
find(C_) searches for the key of B->kt in the B-tree.
bool read_tag(Glass::Cursor *C_, std::string *tag, bool keep_compressed) const
Read the tag value for the key pointed to by cursor C_.
void write_block(uint4 n, const uint8_t *p, bool appending=false) const
write_block(n, p, appending) writes block n in the DB file from address p.
int mid_point(uint8_t *p) const
mid_point(p) finds the directory entry in c that determines the approximate mid point of the data in ...
void add_leaf_item(Glass::LeafItem kt)
GlassTable::add_leaf_item(kt_) adds item kt_ to the leaf block.
void read_block(uint4 n, uint8_t *p) const
read_block(n, p) reads block n of the DB file to address p.
unsigned int block_size
block size of the B tree in bytes
GlassCursor * cursor_get() const
Get a cursor for reading from the table.
bool get_exact_entry(std::string_view key, std::string &tag) const
Read an entry from the table, if and only if it is exactly that being asked for.
bool cursor_created_since_last_modification
Flag for tracking when cursors need to rebuild.
void set_full_compaction(bool parity)
unsigned long cursor_version
Version count for tracking when cursors need to rebuild.
void add_item_to_branch(uint8_t *p, Glass::BItem kt, int c)
add_item_to_branch(p, kt_, c) adds item kt_ to the branch block at p.
std::string name
The path name of the B tree.
bool lazy
If true, don't create the table until it's needed.
void add_branch_item(Glass::BItem kt, int j)
GlassTable::add_item(kt_, j) adds item kt_ to the block at cursor level C[j].
static uint4 block_given_by(const uint8_t *p, int c)
block_given_by(p, c) finds the item at block address p, directory offset c, and returns its tag value...
static int find_in_branch(const uint8_t *p, Glass::LeafItem item, int c)
void close(bool permanent=false)
Close the Btree.
void delete_leaf_item(bool repeatedly)
GlassTable::delete_leaf_item(repeatedly) is (almost) the converse of add_leaf_item.
Glass::Cursor C[GLASS_BTREE_CURSOR_LEVELS]
bool del(std::string_view key)
Delete an entry from the table.
int handle
File descriptor of the table.
void cancel(const RootInfo &root_info, glass_revision_number_t rev)
Cancel any outstanding changes.
Glass::LeafItem_wr kt
buffer of size block_size for making up key-tag items
bool prev_for_sequential(Glass::Cursor *C_, int dummy) const
void add_item_to_leaf(uint8_t *p, Glass::LeafItem kt, int c)
add_item_to_leaf(p, kt_, c) adds item kt_ to the leaf block at p.
void do_open_to_write(const RootInfo *root_info, glass_revision_number_t rev=0)
Perform the opening operation to write.
static void throw_database_closed()
Throw an exception indicating that the database is closed.
uint4 compress_min
Minimum size tag to try compressing (0 for no compression).
bool sequential
true iff the data has been written in a single write in sequential order.
int flags
Flags like DB_NO_SYNC and DB_DANGEROUS.
void add(std::string_view key, std::string_view tag, bool already_compressed=false)
Add a key/tag pair to the table, replacing any existing pair with the same key.
bool readahead_key(std::string_view key) const
static int find_in_leaf(const uint8_t *p, Glass::LeafItem item, int c, bool &exact)
find_in_leaf(p, key, c, exact) searches for the key in the leaf block at p.
glass_revision_number_t revision_number
revision number of the opened B-tree.
uint4 block_given_by() const
Get this item's tag as a block number (this block should not be at level 0).
int size() const
SIZE in diagram above.
void set_truncated_key_and_block(Key newkey, int new_comp, int truncate_size, uint4 n)
void set_key_and_block(Key newkey, uint4 n)
void form_null_key(uint4 n)
Form an item with a null key and with block number n in the tag.
void set_block_given_by(uint4 n)
Set this item's tag to point to block n (this block should not be at level 0).
static void setD(uint8_t *q, int c, int x)
uint8_t * init(unsigned block_size)
bool rewrite
true if the block is not the same as on disk, and so needs rewriting
int c
offset in the block's directory
uint4 get_n() const
Get the block number.
const uint8_t * clone(const Cursor &o)
const uint8_t * get_p() const
Get pointer to block.
int size() const
SIZE in diagram above.
bool last_component() const
void append_chunk(std::string *tag) const
bool get_compressed() const
bool decompress_chunk(CompressionStream &comp_stream, string &tag) const
static void setD(uint8_t *q, int c, int x)
void set_num_entries(glass_tablesize_t n)
const std::string & get_free_list() const
void init(unsigned blocksize_, uint4 compress_min_)
glass_tablesize_t get_num_entries() const
void set_blocksize(unsigned b)
bool get_sequential() const
void set_level(int level_)
unsigned get_blocksize() const
void set_sequential(bool f)
void set_free_list(const std::string &s)
uint4 get_compress_min() const
void set_root_is_fake(bool f)
glass_block_t get_root() const
bool get_root_is_fake() const
void set_root(glass_block_t root_)
Indicates an attempt to access a closed database.
DatabaseCorruptError indicates database corruption was detected.
DatabaseError indicates some sort of database related error.
DatabaseModifiedError indicates a database was modified.
DatabaseOpeningError indicates failure to open a database.
InvalidOperationError indicates the API was used in an invalid way.
UnimplementedError indicates an attempt to use an unimplemented feature.
Constants in the Xapian namespace.
#define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS)
#define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS)
#define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS)
#define LOGCALL_VOID(CATEGORY, FUNC, PARAMS)
#define LOGCALL_DTOR(CATEGORY, CLASS)
Hierarchy of classes which Xapian can throw as exceptions.
Utility functions for testing files.
bool file_exists(const char *path)
Test if a file exists.
Interface to Btree cursors.
Definitions, types, etc for use inside glass.
#define GLASS_BTREE_CURSOR_LEVELS
Allow for this many levels in the B-tree.
#define GLASS_MIN_BLOCKSIZE
Minimum B-tree block size.
uint4 glass_revision_number_t
The revision number of a glass database.
#define GLASS_TABLE_EXTENSION
Glass table extension.
static void throw_corrupt(const char *message)
#define SEQ_START_POINT
Flip to sequential addition block-splitting after this number of observed sequential additions (in ne...
static uint8_t * zeroed_new(size_t size)
int find_in_branch_(const uint8_t *p, ITEM item, int c)
#define GLASS_BTREE_MAX_KEY_LEN
The largest possible value of a key_len.
void io_read_block(int fd, char *p, size_t n, off_t b, off_t o)
Read block b size n bytes into buffer p from file descriptor fd, offset o.
int io_open_block_wr(const char *filename, bool anew)
Open a block-based file for writing.
bool io_unlink(const std::string &filename)
Delete a file.
void io_write_block(int fd, const char *p, size_t n, off_t b, off_t o)
Write block b size n bytes from buffer p to file descriptor fd, offset o.
Wrappers for low-level POSIX I/O routines.
bool io_readahead_block(int, size_t, off_t, off_t=0)
Readahead block b size n bytes from file descriptor fd.
int io_open_block_rd(const char *filename)
Open a block-based file for reading.
int DIR_END(const uint8_t *b)
const int LEVEL_FREELIST
Freelist blocks have their level set to LEVEL_FREELIST.
void SET_DIR_END(uint8_t *b, int x)
int compare(ITEM1 a, ITEM2 b)
Compare two items by their keys.
int TOTAL_FREE(const uint8_t *b)
void SET_LEVEL(uint8_t *b, int x)
int GET_LEVEL(const uint8_t *b)
void SET_REVISION(uint8_t *b, uint4 rev)
int MAX_FREE(const uint8_t *b)
const size_t BLOCK_CAPACITY
Even for items of at maximum size, it must be possible to get this number of items in a block.
const int BYTES_PER_BLOCK_NUMBER
uint4 REVISION(const uint8_t *b)
void SET_TOTAL_FREE(uint8_t *b, int x)
void SET_MAX_FREE(uint8_t *b, int x)
string str(int value)
Convert int to std::string.
int revision()
Report the revision of the library which the program is linked with.
XAPIAN_REVISION_TYPE rev
Revision number of a database.
const int DB_DANGEROUS
Update the database in-place.
Various assertion macros.
#define AssertEqParanoid(A, B)
#define AssertRel(A, REL, B)
Pack types into strings and unpack them again.
void pack_uint(std::string &s, U value)
Append an encoded unsigned integer to a string.
Provides wrappers with POSIXy semantics.
Convert types to std::string.
Various handy string-related helpers.
#define STRINGIZE(X)
The STRINGIZE macro converts its parameter into a string constant.
functions for reading and writing different width words