15#ifndef PSTSDK_LTP_WRITER_H
16#define PSTSDK_LTP_WRITER_H
42 heap_writer(db_writer<T>& writer,
node_id nid)
43 : m_writer(writer), m_ref(writer.node_ref(nid)),
44 m_blocks(writer.external_blocks(m_ref.data)) { }
47 heap_writer(db_writer<T>& writer,
const typename db_writer<T>::data_ref& ref)
48 : m_writer(writer), m_ref(ref), m_blocks(writer.external_blocks(ref.data)) { }
51 const typename db_writer<T>::data_ref& ref()
const {
return m_ref; }
57 std::vector<byte> read_alloc(
heap_id id);
60 void write_alloc(
heap_id id,
const std::vector<byte>& data);
69 void shrink_alloc(
heap_id id,
size_t size);
72 std::vector<byte> read_block(
uint page) {
return m_writer.read_block(m_blocks.at(page)); }
73 void write_block(
uint page,
const std::vector<byte>& data)
74 { m_writer.write_block(m_blocks.at(page), data); }
75 void locate(
heap_id id,
uint& page,
size_t& offset,
size_t& size);
77 db_writer<T>& m_writer;
78 typename db_writer<T>::data_ref m_ref;
79 std::vector<block_id> m_blocks;
103void tc_remove_row(db_writer<T>& writer,
node_id nid,
row_id id);
111void tc_remove_row(db_writer<T>& writer,
const typename db_writer<T>::data_ref& table,
row_id id);
121 std::vector<byte>
first = read_block(0);
122 const disk::heap_first_header* header =
123 reinterpret_cast<const disk::heap_first_header*
>(&
first[0]);
126 throw database_corrupt(
"invalid heap signature");
128 return header->root_id;
137 std::vector<byte> block = read_block(page);
138 const disk::heap_page_header* header =
139 reinterpret_cast<const disk::heap_page_header*
>(&block[0]);
140 const disk::heap_page_map* map =
141 reinterpret_cast<const disk::heap_page_map*
>(&block[header->page_map_offset]);
143 if(index >= map->num_allocs)
144 throw std::length_error(
"heap index past num_allocs");
146 offset = map->allocs[index];
147 size = map->allocs[index + 1] - map->allocs[index];
156 locate(
id, page, offset, size);
158 std::vector<byte> block = read_block(page);
159 return std::vector<byte>(block.begin() + offset, block.begin() + offset + size);
168 locate(
id, page, offset, size);
170 if(data.size() != size)
171 throw can_not_resize(
"write_alloc cannot change an allocation's size");
173 std::vector<byte> block = read_block(page);
175 memcpy(&block[offset], &data[0], size);
176 write_block(page, block);
185 locate(
id, page, offset, current);
188 throw can_not_resize(
"shrink_alloc cannot grow an allocation");
192 const size_t freed = current - size;
195 std::vector<byte> block = read_block(page);
196 disk::heap_page_header* header =
reinterpret_cast<disk::heap_page_header*
>(&block[0]);
197 disk::heap_page_map* map =
198 reinterpret_cast<disk::heap_page_map*
>(&block[header->page_map_offset]);
200 const size_t used = map->allocs[map->num_allocs];
202 memmove(&block[offset + size], &block[offset + current], used - offset - current);
203 memset(&block[used - freed], 0, freed);
205 for(
uint i = index + 1; i <= map->num_allocs; ++i)
206 map->allocs[i] = (
ushort)(map->allocs[i] - freed);
211 write_block(page, block);
224inline bool cell_is_hnid(
ushort type)
235 return (type & 0x1000) != 0;
239inline void row_heap_cells(
const std::vector<byte>& header,
const byte* row,
240 size_t exists_at, std::vector<heap_id>& cells,
241 std::vector<node_id>* subnodes = 0)
243 const disk::tc_header* tc =
reinterpret_cast<const disk::tc_header*
>(&header[0]);
245 for(
byte i = 0; i < tc->num_columns; ++i)
247 const disk::column_description& column = tc->columns[i];
249 if(column.size !=
sizeof(
heapnode_id) || !cell_is_hnid(column.type))
252 if(!
test_bit(row + exists_at, column.bit_offset))
256 memcpy(&hnid, row + column.offset,
sizeof(hnid));
262 cells.push_back(hnid);
264 subnodes->push_back(hnid);
278 matrix_writer(db_writer<T>& writer, heap_writer<T>& heap, heapnode_id matrix,
280 : m_writer(writer), m_heap(heap), m_matrix(matrix),
281 m_cb_per_row(cb_per_row), m_inline(!
is_subnode_id(matrix)), m_rows_per_block(0)
286 m_blocks = writer.external_blocks(root());
288 throw database_corrupt(
"row matrix subnode has no blocks");
290 m_rows_per_block = writer.read_block(m_blocks[0]).size() / cb_per_row;
291 if(m_rows_per_block == 0)
292 throw database_corrupt(
"row matrix block smaller than one row");
298 return m_heap.read_alloc(m_matrix).size() / m_cb_per_row;
300 return (m_blocks.size() - 1) * m_rows_per_block + last_block_rows();
303 std::vector<byte> read_row(
size_t index)
305 std::vector<byte> block = load(index);
306 const size_t at = offset_of(index);
307 return std::vector<byte>(block.begin() + at, block.begin() + at + m_cb_per_row);
310 void write_row(
size_t index,
const std::vector<byte>& row)
312 std::vector<byte> block = load(index);
313 memcpy(&block[offset_of(index)], &row[0], m_cb_per_row);
322 const size_t left = rows() - 1;
323 m_heap.shrink_alloc(m_matrix, left * m_cb_per_row);
327 const size_t left_in_block = last_block_rows() - 1;
328 const size_t size = m_writer.read_block(m_blocks.back()).size();
329 const size_t shrunk = left_in_block ? size - m_cb_per_row : 0;
331 if(!m_writer.shrink_data_tail(root(), shrunk))
335 return m_blocks.empty();
346 return m_writer.subnode_ref(m_heap.ref().sub, (node_id)m_matrix).data;
349 size_t last_block_rows()
351 return m_writer.read_block(m_blocks.back()).size() / m_cb_per_row;
354 size_t block_of(
size_t index)
const {
return m_inline ? 0 : index / m_rows_per_block; }
355 size_t offset_of(
size_t index)
const
357 return (m_inline ? index : index % m_rows_per_block) * m_cb_per_row;
360 std::vector<byte> load(
size_t index)
362 return m_inline ? m_heap.read_alloc(m_matrix)
363 : m_writer.read_block(m_blocks[block_of(index)]);
366 void store(
size_t index,
const std::vector<byte>& block)
369 m_heap.write_alloc(m_matrix, block);
371 m_writer.write_block(m_blocks[block_of(index)], block);
374 db_writer<T>& m_writer;
375 heap_writer<T>& m_heap;
379 size_t m_rows_per_block;
380 std::vector<block_id> m_blocks;
386inline void free_row_subnodes(db_writer<T>& writer,
387 const typename db_writer<T>::data_ref& table,
388 const std::vector<node_id>& subnodes)
390 for(
size_t i = 0; i < subnodes.size(); ++i)
391 writer.subnode_remove(table.sub, subnodes[i]);
408 size_t leaf_stride()
const {
return key_size + value_size; }
409 size_t nonleaf_stride()
const {
return key_size +
sizeof(
heap_id); }
410 size_t value_offset(uint index)
const {
return index * leaf_stride() + key_size; }
414inline bth_layout bth_read_layout(heap_writer<T>& heap,
heap_id bth_root)
416 std::vector<byte> raw = heap.read_alloc(bth_root);
417 const disk::bth_header* header =
reinterpret_cast<const disk::bth_header*
>(&raw[0]);
420 throw database_corrupt(
"invalid BTH signature");
423 layout.header = bth_root;
424 layout.key_size = header->key_size;
425 layout.value_size = header->entry_size;
426 layout.levels = header->num_levels;
427 layout.root = header->root;
432inline ulong bth_read_at(
const std::vector<byte>& alloc,
size_t offset,
size_t width)
435 memcpy(&value, &alloc[offset], width);
439inline void bth_write_at(std::vector<byte>& alloc,
size_t offset,
size_t width,
ulong value)
441 memcpy(&alloc[offset], &value, width);
445inline void bth_descend(heap_writer<T>& heap,
const bth_layout& layout,
ulong key,
446 std::vector<heap_id>& path, std::vector<uint>& indices)
450 for(
byte level = layout.levels; level > 0; --level)
452 std::vector<byte> alloc = heap.read_alloc(current);
453 const uint count = (
uint)(alloc.size() / layout.nonleaf_stride());
455 if(count == 0 || key < bth_read_at(alloc, 0, layout.key_size))
456 throw key_not_found<ulong>(key);
459 while(position + 1 < count &&
460 bth_read_at(alloc, (position + 1) * layout.nonleaf_stride(), layout.key_size) <= key)
463 path.push_back(current);
464 indices.push_back(position);
465 current = (
heap_id)bth_read_at(alloc, position * layout.nonleaf_stride() + layout.key_size,
469 std::vector<byte> alloc = heap.read_alloc(current);
470 const uint count = (
uint)(alloc.size() / layout.leaf_stride());
472 for(
uint i = 0; i < count; ++i)
474 if(bth_read_at(alloc, i * layout.leaf_stride(), layout.key_size) != key)
477 path.push_back(current);
478 indices.push_back(i);
482 throw key_not_found<ulong>(key);
489inline void bth_empty(heap_writer<T>& heap,
const bth_layout& layout)
491 std::vector<byte> raw = heap.read_alloc(layout.header);
492 disk::bth_header* header =
reinterpret_cast<disk::bth_header*
>(&raw[0]);
494 header->num_levels = 0;
495 heap.write_alloc(layout.header, raw);
500inline void bth_remove_key(heap_writer<T>& heap,
const bth_layout& layout,
ulong key)
502 std::vector<heap_id> path;
503 std::vector<uint> indices;
504 bth_descend(heap, layout, key, path, indices);
506 const size_t stride = layout.leaf_stride();
507 std::vector<byte> alloc = heap.read_alloc(path.back());
508 const uint count = (
uint)(alloc.size() / stride);
509 const uint index = indices.back();
511 if(index + 1 < count)
512 memmove(&alloc[index * stride], &alloc[(index + 1) * stride],
513 (count - index - 1) * stride);
515 heap.write_alloc(path.back(), alloc);
516 heap.shrink_alloc(path.back(), (count - 1) * stride);
518 if(count == 1 && path.size() == 1)
520 bth_empty(heap, layout);
526 if(index != 0 || path.size() < 2)
529 std::vector<byte> survivors = heap.read_alloc(path.back());
530 const ulong first = bth_read_at(survivors, 0, layout.key_size);
532 for(
size_t depth = path.size() - 1; depth-- > 0;)
534 std::vector<byte> parent = heap.read_alloc(path[depth]);
535 bth_write_at(parent, indices[depth] * layout.nonleaf_stride(), layout.key_size,
first);
536 heap.write_alloc(path[depth], parent);
538 if(indices[depth] != 0)
547 const size_t nonleaf = layout.nonleaf_stride();
548 for(
size_t depth = path.size() - 1; depth-- > 0;)
550 std::vector<byte> parent = heap.read_alloc(path[depth]);
551 const uint parent_count = (
uint)(parent.size() / nonleaf);
552 const uint parent_index = indices[depth];
554 if(parent_index + 1 < parent_count)
555 memmove(&parent[parent_index * nonleaf], &parent[(parent_index + 1) * nonleaf],
556 (parent_count - parent_index - 1) * nonleaf);
558 heap.write_alloc(path[depth], parent);
559 heap.shrink_alloc(path[depth], (parent_count - 1) * nonleaf);
565 bth_empty(heap, layout);
575 heap_writer<T> heap(writer, nid);
576 detail::bth_layout layout = detail::bth_read_layout(heap, heap.root_id());
578 std::vector<heap_id> path;
579 std::vector<uint> indices;
582 try { detail::bth_descend(heap, layout,
id, path, indices); }
583 catch(key_not_found<ulong>&) {
throw key_not_found<prop_id>(
id); }
585 std::vector<byte> alloc = heap.read_alloc(path.back());
586 const size_t value_at = layout.value_offset(indices.back());
590 const ulong type = detail::bth_read_at(alloc, value_at,
sizeof(
ushort));
592 throw not_implemented(
"pc_set_inline only handles inline fixed width properties");
594 detail::bth_write_at(alloc, value_at +
sizeof(
ushort),
sizeof(
ulong), value);
595 heap.write_alloc(path.back(), alloc);
601 tc_remove_row(writer, writer.node_ref(nid),
id);
606 const typename db_writer<T>::data_ref& table,
row_id id)
608 heap_writer<T> heap(writer, table);
609 const heap_id root = heap.root_id();
611 std::vector<byte> raw = heap.read_alloc(root);
612 const disk::tc_header* header =
reinterpret_cast<const disk::tc_header*
>(&raw[0]);
615 throw database_corrupt(
"not a table context");
619 const heap_id row_btree = header->row_btree_id;
620 const heapnode_id matrix_id = header->row_matrix_id;
622 if(matrix_id == 0 || cb_per_row == 0)
623 throw key_not_found<row_id>(
id);
625 detail::bth_layout layout = detail::bth_read_layout(heap, row_btree);
626 detail::matrix_writer<T> matrix(writer, heap, matrix_id, cb_per_row);
628 const size_t count = matrix.rows();
630 throw key_not_found<row_id>(
id);
632 std::vector<heap_id> path;
633 std::vector<uint> indices;
634 detail::bth_descend(heap, layout,
id, path, indices);
636 std::vector<byte> leaf = heap.read_alloc(path.back());
637 const size_t target = detail::bth_read_at(leaf, layout.value_offset(indices.back()),
639 const size_t last = count - 1;
642 throw database_corrupt(
"row index points past the matrix");
647 std::vector<byte> doomed_row = matrix.read_row(target);
648 std::vector<heap_id> doomed_cells;
649 std::vector<node_id> doomed_subnodes;
650 detail::row_heap_cells(raw, &doomed_row[0], exists_at, doomed_cells, &doomed_subnodes);
654 memcpy(&at_target, &doomed_row[0],
sizeof(
row_id));
656 throw database_corrupt(
"row index and row matrix disagree");
660 std::vector<byte> moving;
661 std::vector<heap_id> moved_path;
662 std::vector<uint> moved_indices;
666 moving = matrix.read_row(last);
668 memcpy(&moved, &moving[0],
sizeof(
row_id));
669 detail::bth_descend(heap, layout, moved, moved_path, moved_indices);
675 matrix.write_row(target, moving);
677 std::vector<byte> moved_leaf = heap.read_alloc(moved_path.back());
678 detail::bth_write_at(moved_leaf, layout.value_offset(moved_indices.back()),
679 layout.value_size, (
ulong)target);
680 heap.write_alloc(moved_path.back(), moved_leaf);
683 const bool emptied = matrix.drop_last_row();
684 detail::bth_remove_key(heap, layout,
id);
688 std::vector<heap_id> kept;
689 for(
size_t r = 0; r < last; ++r)
691 std::vector<byte> survivor = matrix.read_row(r);
692 detail::row_heap_cells(raw, &survivor[0], exists_at, kept);
695 for(
size_t i = 0; i < doomed_cells.size(); ++i)
696 if(std::find(kept.begin(), kept.end(), doomed_cells[i]) == kept.end())
697 heap.shrink_alloc(doomed_cells[i], 0);
699 detail::free_row_subnodes(writer, table, doomed_subnodes);
703 for(
size_t i = 0; i < doomed_cells.size(); ++i)
704 heap.shrink_alloc(doomed_cells[i], 0);
706 detail::free_row_subnodes(writer, table, doomed_subnodes);
710 detail::bth_empty(heap, layout);
712 std::vector<byte> header_raw = heap.read_alloc(root);
713 reinterpret_cast<disk::tc_header*
>(&header_raw[0])->row_matrix_id = 0;
714 heap.write_alloc(root, header_raw);
717 writer.subnode_remove(table.sub, (
node_id)matrix_id);
Contains references to other bth_node allocations.
void first(btree_iter_impl< K, V > &iter) const
Positions the iterator at the first element on this tree.
The exceptions used by pstsdk.
ulong get_heap_page(heap_id id)
Get the heap page from the heap id.
ulong get_heap_index(heap_id id)
Get the index from the heap id.
bool is_heap_id(heapnode_id id)
Inspects a heapnode_id (also known as a HNID) to determine if it is a heap_id (HID)
bool is_subnode_id(heapnode_id id)
Inspects a heapnode_id (also known as a HNID) to determine if it is a node_id (NID)
bool test_bit(const byte *pbytes, ulong bit)
Test to see if the specified bit in the buffer is set.
Contains the definition of all in memory representations of disk structures.
In place edits of an open store.
Primitive structures defined by MS-PST and MAPI.