13#ifndef PSTSDK_NDB_WRITER_H
14#define PSTSDK_NDB_WRITER_H
42 explicit db_writer(
const std::shared_ptr<database_impl<T> >& db)
43 : m_db(db), m_dirty(false) { }
48 std::vector<byte> read_block(
block_id bid);
58 void write_block(
block_id bid,
const std::vector<byte>& payload);
106 std::vector<block_id> external_blocks(
block_id bid);
118 bool shrink_data_tail(
block_id bid,
size_t new_size);
138 void zero_extent(
ulonglong address,
size_t size);
148 static uint bt_count(
const std::vector<byte>& page) {
return page[bt_meta]; }
149 static uint bt_entry_size(
const std::vector<byte>& page) {
return page[bt_meta + 2]; }
150 static uint bt_level(
const std::vector<byte>& page) {
return page[bt_meta + 3]; }
153 static T bt_key(
const std::vector<byte>& page,
uint index);
154 static void bt_set_key(std::vector<byte>& page,
uint index, T key);
156 static ulonglong bt_child(
const std::vector<byte>& page,
uint index);
157 static int bt_search(
const std::vector<byte>& page, T key);
159 std::vector<byte> read_page_raw(
ulonglong address);
160 void write_page_raw(
ulonglong address, std::vector<byte>& page);
162 void bt_descend(
ulonglong root, T key, std::vector<ulonglong>& path, std::vector<uint>& indices);
165 void bt_propagate_key(
const std::vector<ulonglong>& path,
const std::vector<uint>& indices,
166 size_t depth, T key);
170 void collect_data_tree(
block_id bid, std::vector<block_id>& blocks);
171 void collect_subnode_tree(
block_id bid, std::vector<block_id>& blocks);
173 void block_children(
block_id bid, std::vector<block_id>& children);
176 void plan_release(
block_id root, std::map<block_id, ushort>& remaining,
177 std::map<block_id, block_info>& info, std::vector<block_id>& order);
179 void apply_release(
const std::map<block_id, ushort>& remaining,
180 const std::map<block_id, block_info>& info,
181 const std::vector<block_id>& order);
183 void collect_pages(
ulonglong address, std::vector<ulonglong>& pages);
188 ulonglong nbt_root()
const {
return m_db->get_header().root_info.brefNBT.ib; }
189 ulonglong bbt_root()
const {
return m_db->get_header().root_info.brefBBT.ib; }
191 void stamp_header_crc();
193 std::shared_ptr<database_impl<T> > m_db;
205 block_info bi = m_db->lookup_block_info(bid);
206 std::vector<byte> buffer = m_db->read_block_data(bi);
207 buffer.resize(bi.size);
223 block_info bi = m_db->lookup_block_info(bid);
225 if(payload.size() > bi.size)
226 throw can_not_resize(
"write_block cannot grow a block");
229 throw shared_block(
"cannot edit a block in place while it is shared");
231 const size_t new_cb = payload.size();
232 const size_t old_aligned = disk::align_disk<T>(bi.size);
233 const size_t new_aligned = disk::align_disk<T>(new_cb);
235 std::vector<byte> buffer(new_aligned, 0);
238 memcpy(&buffer[0], &payload[0], new_cb);
251 disk::block_trailer<T>* bt =
reinterpret_cast<disk::block_trailer<T>*
>(
252 &buffer[0] + new_aligned -
sizeof(disk::block_trailer<T>));
261 if(new_aligned < old_aligned)
262 zero_extent(bi.address, old_aligned);
264 m_db->get_file().write(buffer, bi.address);
267 if(new_cb != bi.size)
268 bbt_set_size(bid, (
ushort)new_cb);
275 memcpy(&key, &page[index * bt_entry_size(page)],
sizeof(T));
282 memcpy(&page[index * bt_entry_size(page)], &key,
sizeof(T));
289 memcpy(&address, &page[index * bt_entry_size(page) + 2 *
sizeof(T)],
sizeof(T));
299 uint end = bt_count(page);
300 uint mid = (start + end) / 2;
304 T current = bt_key(page, mid);
308 else if(current == key)
313 mid = (start + end) / 2;
323 m_db->get_file().read(page, address);
330 disk::page<T>* p =
reinterpret_cast<disk::page<T>*
>(&page[0]);
334 m_db->get_file().write(page, address);
338 m_db->reset_page_cache();
344 std::vector<ulonglong>& path,
345 std::vector<uint>& indices)
349 bool descending =
false;
353 std::vector<byte> page = read_page_raw(address);
357 if(descending && bt_level(page) >= above)
358 throw database_corrupt(
"btree level did not decrease");
360 above = bt_level(page);
363 int position = bt_search(page, key);
366 throw key_not_found<T>(key);
368 path.push_back(address);
369 indices.push_back((
uint)position);
371 if(bt_level(page) == 0)
373 if(bt_key(page, (
uint)position) != key)
374 throw key_not_found<T>(key);
378 address = bt_child(page, (
uint)position);
384 const std::vector<uint>& indices,
389 size_t parent = depth - 1;
390 std::vector<byte> page = read_page_raw(path[parent]);
391 bt_set_key(page, indices[parent], key);
392 write_page_raw(path[parent], page);
395 if(indices[parent] != 0)
405 std::vector<ulonglong> path;
406 std::vector<uint> indices;
407 bt_descend(root, key, path, indices);
412 bool empties_root =
true;
413 for(
size_t depth = 0; depth < path.size() && empties_root; ++depth)
415 std::vector<byte> page = read_page_raw(path[depth]);
416 empties_root = bt_count(page) == 1;
419 throw database_corrupt(
"btree root emptied");
421 for(
size_t depth = path.size(); depth-- > 0;)
423 std::vector<byte> page = read_page_raw(path[depth]);
424 const uint count = bt_count(page);
425 const uint entry_size = bt_entry_size(page);
426 const uint index = indices[depth];
428 if(index + 1 < count)
429 memmove(&page[index * entry_size], &page[(index + 1) * entry_size],
430 (count - index - 1) * entry_size);
431 memset(&page[(count - 1) * entry_size], 0, entry_size);
432 page[bt_meta] = (
byte)(count - 1);
436 write_page_raw(path[depth], page);
441 bt_propagate_key(path, indices, depth, bt_key(page, 0));
450 throw database_corrupt(
"btree root emptied");
459 std::vector<ulonglong> path;
460 std::vector<uint> indices;
461 bt_descend(root, key, path, indices);
463 address = path.back();
464 index = indices.back();
470 bt_remove(nbt_root(), (T)nid);
486 std::vector<byte> page = read_page_raw(address);
487 disk::bbt_leaf_entry<T>* entry =
488 reinterpret_cast<disk::bbt_leaf_entry<T>*
>(&page[index * bt_entry_size(page)]);
490 write_page_raw(address, page);
500 std::vector<byte> page = read_page_raw(address);
501 disk::bbt_leaf_entry<T>* entry =
502 reinterpret_cast<disk::bbt_leaf_entry<T>*
>(&page[index * bt_entry_size(page)]);
503 entry->ref_count = count;
504 write_page_raw(address, page);
513 blocks.push_back(bid);
518 std::vector<byte> raw = read_block(bid);
519 const disk::extended_block<T>* xblock =
520 reinterpret_cast<const disk::extended_block<T>*
>(&raw[0]);
523 throw unexpected_block(
"expected an extended block in a data tree");
525 for(
ushort i = 0; i < xblock->count; ++i)
526 collect_data_tree(xblock->bid[i], blocks);
535 blocks.push_back(bid);
537 std::vector<byte> raw = read_block(bid);
538 const disk::sub_block<T, disk::sub_leaf_entry<T> >* sblock =
539 reinterpret_cast<const disk::sub_block<T, disk::sub_leaf_entry<T>
>*>(&raw[0]);
542 throw unexpected_block(
"expected a subnode block in a subnode tree");
544 if(sblock->level == 0)
546 for(
ushort i = 0; i < sblock->count; ++i)
550 collect_data_tree(sblock->entry[i].data, blocks);
551 collect_subnode_tree(sblock->entry[i].sub, blocks);
557 const disk::sub_block<T, disk::sub_nonleaf_entry<T> >* nonleaf =
558 reinterpret_cast<const disk::sub_block<T, disk::sub_nonleaf_entry<T>
>*>(&raw[0]);
560 for(
ushort i = 0; i < nonleaf->count; ++i)
561 collect_subnode_tree(nonleaf->entry[i].sub_block_bid, blocks);
567 std::vector<block_id> tree;
568 collect_data_tree(bid, tree);
570 std::vector<block_id> external;
571 for(
size_t i = 0; i < tree.size(); ++i)
573 external.push_back(tree[i]);
582 const node_info ni = m_db->lookup_node_info(nid);
584 ref.data = ni.data_bid;
585 ref.sub = ni.sub_bid;
593 std::vector<block_id> pending;
594 std::set<block_id> seen;
595 pending.push_back(tree);
597 while(!pending.empty())
599 const block_id bid = pending.back();
609 std::vector<byte> raw = read_block(bid);
610 const disk::sub_block<T, disk::sub_leaf_entry<T> >* leaf =
611 reinterpret_cast<const disk::sub_block<T, disk::sub_leaf_entry<T>
>*>(&raw[0]);
615 const disk::sub_block<T, disk::sub_nonleaf_entry<T> >* nonleaf =
616 reinterpret_cast<const disk::sub_block<T, disk::sub_nonleaf_entry<T>
>*>(&raw[0]);
618 for(
ushort i = 0; i < nonleaf->count; ++i)
619 pending.push_back(nonleaf->entry[i].sub_block_bid);
624 for(
ushort i = 0; i < leaf->count; ++i)
626 if(leaf->entry[i].nid != sub)
630 ref.data = leaf->entry[i].data;
631 ref.sub = leaf->entry[i].sub;
636 throw key_not_found<node_id>(sub);
643 throw key_not_found<node_id>(sub);
645 std::vector<byte> raw = read_block(tree);
646 disk::sub_block<T, disk::sub_leaf_entry<T> >* leaf =
647 reinterpret_cast<disk::sub_block<T, disk::sub_leaf_entry<T>
>*>(&raw[0]);
651 disk::sub_block<T, disk::sub_nonleaf_entry<T> >* nonleaf =
652 reinterpret_cast<disk::sub_block<T, disk::sub_nonleaf_entry<T>
>*>(&raw[0]);
657 while(i + 1 < nonleaf->count && nonleaf->entry[i + 1].nid_key <= sub)
660 if(nonleaf->count == 0 || sub < nonleaf->entry[0].nid_key)
661 throw key_not_found<node_id>(sub);
664 block_id child = nonleaf->entry[i].sub_block_bid;
666 if(!subnode_remove(child, sub))
669 if(i + 1 < nonleaf->count)
670 memmove(&nonleaf->entry[i], &nonleaf->entry[i + 1],
671 (nonleaf->count - i - 1) *
sizeof(disk::sub_nonleaf_entry<T>));
672 memset(&nonleaf->entry[nonleaf->count - 1], 0,
sizeof(disk::sub_nonleaf_entry<T>));
675 if(nonleaf->count == 0)
678 write_block(tree, raw);
679 release_block(child);
684 for(
ushort i = 0; i < leaf->count; ++i)
686 if(leaf->entry[i].nid != sub)
689 const block_id data = leaf->entry[i].data;
690 const block_id owned = leaf->entry[i].sub;
692 if(i + 1 < leaf->count)
693 memmove(&leaf->entry[i], &leaf->entry[i + 1],
694 (leaf->count - i - 1) *
sizeof(disk::sub_leaf_entry<T>));
695 memset(&leaf->entry[leaf->count - 1], 0,
sizeof(disk::sub_leaf_entry<T>));
700 write_block(tree, raw);
702 std::map<block_id, ushort> remaining;
703 std::map<block_id, block_info> info;
704 std::vector<block_id> order;
705 plan_release(data, remaining, info, order);
706 plan_release(owned, remaining, info, order);
707 apply_release(remaining, info, order);
709 return leaf->count == 0;
712 throw key_not_found<node_id>(sub);
723 std::vector<byte> payload = read_block(bid);
724 payload.resize(new_size);
725 write_block(bid, payload);
729 std::vector<byte> raw = read_block(bid);
730 disk::extended_block<T>* xblock =
reinterpret_cast<disk::extended_block<T>*
>(&raw[0]);
733 throw unexpected_block(
"expected an extended block in a data tree");
735 if(xblock->count == 0)
739 const block_id child = xblock->bid[last];
740 const size_t was = logical_size(child);
742 if(!shrink_data_tail(child, new_size))
744 xblock->total_size -= (
ulong)(was - new_size);
745 write_block(bid, raw);
751 if(xblock->count == 1)
754 xblock->total_size -= (
ulong)was;
755 xblock->bid[last] = 0;
758 write_block(bid, raw);
759 release_block(child);
770 return m_db->lookup_block_info(bid).size;
772 std::vector<byte> raw = read_block(bid);
773 return reinterpret_cast<const disk::extended_block<T>*
>(&raw[0])->total_size;
782 std::vector<byte> raw = read_block(bid);
786 const disk::extended_block<T>* xblock =
787 reinterpret_cast<const disk::extended_block<T>*
>(&raw[0]);
789 for(
ushort i = 0; i < xblock->count; ++i)
790 children.push_back(xblock->bid[i]);
796 throw unexpected_block(
"unknown internal block type");
798 const disk::sub_block<T, disk::sub_leaf_entry<T> >* leaf =
799 reinterpret_cast<const disk::sub_block<T, disk::sub_leaf_entry<T>
>*>(&raw[0]);
803 for(
ushort i = 0; i < leaf->count; ++i)
805 children.push_back(leaf->entry[i].data);
806 children.push_back(leaf->entry[i].sub);
812 const disk::sub_block<T, disk::sub_nonleaf_entry<T> >* nonleaf =
813 reinterpret_cast<const disk::sub_block<T, disk::sub_nonleaf_entry<T>
>*>(&raw[0]);
815 for(
ushort i = 0; i < nonleaf->count; ++i)
816 children.push_back(nonleaf->entry[i].sub_block_bid);
825 std::map<block_id, ushort>& remaining,
826 std::map<block_id, block_info>& info,
827 std::vector<block_id>& order)
829 std::vector<block_id> pending;
830 pending.push_back(root);
832 while(!pending.empty())
834 const block_id raw = pending.back();
842 if(remaining.find(bid) == remaining.end())
844 const block_info bi = m_db->lookup_block_info(bid);
846 remaining[bid] = bi.ref_count;
847 order.push_back(bid);
851 throw database_corrupt(
"block released more often than it is referenced");
858 block_children(bid, pending);
864 const std::map<block_id, block_info>& info,
865 const std::vector<block_id>& order)
867 for(
size_t i = 0; i < order.size(); ++i)
870 const ushort count = remaining.find(bid)->second;
874 bbt_set_ref_count(bid, count);
878 const block_info& bi = info.find(bid)->second;
880 const size_t extent = disk::align_disk<T>(bi.size);
883 zero_extent(address, extent);
890 std::map<block_id, ushort> remaining;
891 std::map<block_id, block_info> info;
892 std::vector<block_id> order;
894 plan_release(bid, remaining, info, order);
895 apply_release(remaining, info, order);
901 const node_info ni = m_db->lookup_node_info(nid);
903 std::map<block_id, ushort> remaining;
904 std::map<block_id, block_info> info;
905 std::vector<block_id> order;
907 plan_release(ni.data_bid, remaining, info, order);
908 plan_release(ni.sub_bid, remaining, info, order);
911 apply_release(remaining, info, order);
917 pages.push_back(address);
919 std::vector<byte> page = read_page_raw(address);
920 if(bt_level(page) == 0)
923 for(
uint i = 0; i < bt_count(page); ++i)
924 collect_pages(bt_child(page, i), pages);
937 try { m_db->get_file().read(page, address); }
938 catch(std::out_of_range&) {
return false; }
940 const disk::page<T>* p =
reinterpret_cast<const disk::page<T>*
>(&page[0]);
942 if(p->trailer.page_type != p->trailer.page_type_repeat)
945 switch(p->trailer.page_type)
965 const ulonglong eof = m_db->get_header().root_info.ibFileEof;
968 std::vector<std::pair<ulonglong, ulonglong> > live;
971 live.push_back(std::make_pair((
ulonglong)0, first));
973 std::vector<ulonglong> pages;
974 collect_pages(nbt_root(), pages);
975 collect_pages(bbt_root(), pages);
976 for(
size_t i = 0; i < pages.size(); ++i)
979 std::shared_ptr<bbt_page> root = m_db->read_bbt_root();
981 live.push_back(std::make_pair((*i).address,
982 (*i).address + disk::align_disk<T>((*i).size)));
984 std::sort(live.begin(), live.end());
986 std::vector<std::pair<ulonglong, ulonglong> > keep;
989 for(
size_t i = 0; i <= live.size(); ++i)
991 const ulonglong stop = i < live.size() ? live[i].first : eof;
993 ulonglong page = at < first ? first : at;
998 if(looks_like_page(page))
1001 if(i < live.size() && live[i].second > at)
1002 at = live[i].second;
1005 live.insert(live.end(), keep.begin(), keep.end());
1006 std::sort(live.begin(), live.end());
1011 for(
size_t i = 0; i < live.size(); ++i)
1013 if(live[i].first > at)
1015 zero_extent(at, (
size_t)(live[i].first - at));
1016 wiped += live[i].first - at;
1019 if(live[i].second > at)
1020 at = live[i].second;
1025 zero_extent(at, (
size_t)(eof - at));
1038 const size_t chunk = 1024 * 1024;
1039 std::vector<byte> zeroes(size < chunk ? size : chunk, 0);
1041 for(
ulonglong at = address; at < address + size; at += zeroes.size())
1043 const ulonglong left = address + size - at;
1044 if(left < zeroes.size())
1045 zeroes.resize((
size_t)left);
1047 m_db->get_file().write(zeroes, at);
1059 disk::header<T>& h = m_db->get_header();
1064 std::vector<byte> buffer(
sizeof(disk::header<T>));
1065 memcpy(&buffer[0], &h,
sizeof(disk::header<T>));
1066 m_db->get_file().write(buffer, 0);
1068 m_db->reset_page_cache();
1075 disk::header<ulong>& h = m_db->get_header();
1077 reinterpret_cast<byte*
>(&h) + disk::header_crc_locations<ulong>::start,
1078 disk::header_crc_locations<ulong>::length);
1084 disk::header<ulonglong>& h = m_db->get_header();
1086 reinterpret_cast<byte*
>(&h) + disk::header_crc_locations<ulonglong>::partial_start,
1087 disk::header_crc_locations<ulonglong>::partial_length);
1089 reinterpret_cast<byte*
>(&h) + disk::header_crc_locations<ulonglong>::full_start,
1090 disk::header_crc_locations<ulonglong>::full_length);
Contains references to other bth_node allocations.
const_iterator begin() const
Returns a STL style iterator positioned at the first entry.
The exceptions used by pstsdk.
void permute(void *pdata, ulong cb, bool encrypt)
Modifies the data block in place, according to the permute method.
void cyclic(void *pdata, ulong cb, ulong key)
Modifies the data block in place, according to the cyclic method.
boost::uint64_t ulonglong
const uint block_id_attached_bit
The attached bit indicates a block is referenced in memory This is an implementation detail,...
Contains the definition of all in memory representations of disk structures.
const_btree_node_iter< block_id, block_info > const_blockinfo_iterator
Primitive structures defined by MS-PST and MAPI.
static const size_t page_data_size
Amount of usable space in a page.