Skip to content

Commit 32189f1

Browse files
committed
[compression-depth-balance] Optimize decompression
1 parent 88a23bc commit 32189f1

File tree

1 file changed

+119
-144
lines changed

1 file changed

+119
-144
lines changed

crypto/vm/boc-compression.cpp

Lines changed: 119 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -656,168 +656,143 @@ td::Result<std::vector<td::Ref<vm::Cell>>> boc_decompress_improved_structure_lz4
656656
bit_reader.advance(remaining_data_bits);
657657
}
658658

659-
// Build cell tree
660-
std::vector<td::Ref<vm::Cell>> nodes(node_count);
659+
// Build cell tree
660+
std::vector<td::Ref<vm::Cell>> nodes(node_count);
661661

662-
// Helper: write ShardAccounts augmentation (DepthBalanceInfo with grams) into builder
663-
auto write_depth_balance_grams = [&](vm::CellBuilder& cb, const td::RefInt256& grams) -> bool {
664-
if (!cb.store_zeroes_bool(2)) { // empty HmLabel
665-
return false;
666-
}
667-
if (!cb.store_zeroes_bool(5)) { // split_depth = 0 (<= 30)
668-
return false;
669-
}
670-
if (!block::tlb::t_CurrencyCollection.pack_special(cb, grams, td::Ref<vm::Cell>())) {
671-
return false;
672-
}
673-
return true;
674-
};
675-
676-
// Helper: detect MerkleUpdate (is_special AND first byte == 0x04) without finalizing
677-
auto is_merkle_update_node = [&](size_t idx) -> bool {
678-
if (!is_special[idx]) {
679-
return false;
680-
}
681-
// Need at least one full byte in data to read the tag
682-
if (cell_builders[idx].get_bits() < 8) {
683-
return false;
684-
}
685-
unsigned first_byte = cell_builders[idx].get_data()[0];
686-
return first_byte == 0x04;
687-
};
688-
689-
// Recursively build RIGHT subtree under MerkleUpdate, pairing with LEFT subtree, computing sum diffs.
690-
// Note: left_idx can be SIZE_MAX (as -1 sentinel) when the left subtree has no corresponding child.
691-
// Sum is accumulated into sum_diff_out (if non-null), similar to compression flow.
692-
std::function<td::Status(size_t, size_t, td::RefInt256*)> build_right_under_mu =
693-
[&](size_t right_idx, size_t left_idx, td::RefInt256* sum_diff_out) -> td::Status {
694-
if (nodes[right_idx].not_null()) {
695-
if (left_idx != std::numeric_limits<size_t>::max() && sum_diff_out) {
696-
vm::CellSlice cs_left(NoVm(), nodes[left_idx]);
697-
vm::CellSlice cs_right(NoVm(), nodes[right_idx]);
698-
td::RefInt256 vertex_diff = process_shard_accounts_vertex(cs_left, cs_right);
699-
if (vertex_diff.not_null()) {
700-
*sum_diff_out += vertex_diff;
662+
// Helper: write ShardAccounts augmentation (DepthBalanceInfo with grams) into builder
663+
auto write_depth_balance_grams = [&](vm::CellBuilder& cb, const td::RefInt256& grams) -> bool {
664+
if (!cb.store_zeroes_bool(7)) { // empty HmLabel and split_depth
665+
return false;
666+
}
667+
if (!block::tlb::t_CurrencyCollection.pack_special(cb, grams, td::Ref<vm::Cell>())) {
668+
return false;
669+
}
670+
return true;
671+
};
672+
673+
// Helper: detect MerkleUpdate (is_special AND first byte == 0x04) without finalizing
674+
auto is_merkle_update_node = [&](size_t idx) -> bool {
675+
if (!is_special[idx]) {
676+
return false;
677+
}
678+
// Need at least one full byte in data to read the tag
679+
if (cell_builders[idx].get_bits() < 8) {
680+
return false;
681+
}
682+
unsigned first_byte = cell_builders[idx].get_data()[0];
683+
return first_byte == 0x04;
684+
};
685+
686+
// Helper: finalize a node by storing refs and finalizing the builder
687+
auto finalize_node = [&](size_t idx) -> td::Status {
688+
try {
689+
for (int j = 0; j < cell_refs_cnt[idx]; ++j) {
690+
cell_builders[idx].store_ref(nodes[boc_graph[idx][j]]);
701691
}
692+
try {
693+
nodes[idx] = cell_builders[idx].finalize(is_special[idx]);
694+
} catch (vm::CellBuilder::CellWriteError& e) {
695+
return td::Status::Error(PSTRING() << "BOC decompression failed: failed to finalize node (CellWriteError)");
696+
}
697+
} catch (vm::VmError& e) {
698+
return td::Status::Error(PSTRING() << "BOC decompression failed: failed to finalize node (VmError)");
702699
}
703700
return td::Status::OK();
704-
}
705-
// Build children first
706-
td::RefInt256 sum_child_diff = td::make_refint(0);
707-
for (int j = 0; j < cell_refs_cnt[right_idx]; ++j) {
708-
size_t right_child = boc_graph[right_idx][j];
709-
size_t left_child = (left_idx != std::numeric_limits<size_t>::max() && j < cell_refs_cnt[left_idx])
710-
? boc_graph[left_idx][j]
711-
: std::numeric_limits<size_t>::max();
712-
TRY_STATUS(build_right_under_mu(right_child, left_child, &sum_child_diff));
713-
}
714-
// If this vertex was depth-balance-compressed, reconstruct its data from left + children sum
715-
if (is_depth_balance[right_idx] && left_idx != std::numeric_limits<size_t>::max()) {
716-
// Extract left grams
717-
vm::CellSlice cs_left(NoVm(), nodes[left_idx]);
718-
td::RefInt256 left_grams = extract_balance_from_depth_balance_info(cs_left);
719-
if (left_grams.is_null()) {
720-
return td::Status::Error("BOC decompression failed: depth-balance left vertex has no grams");
721-
}
722-
td::RefInt256 expected_right_grams = left_grams;
723-
expected_right_grams += sum_child_diff;
724-
// Write reconstructed augmentation into builder
725-
if (!write_depth_balance_grams(cell_builders[right_idx], expected_right_grams)) {
726-
return td::Status::Error("BOC decompression failed: failed to write depth-balance grams");
701+
};
702+
703+
// Recursively build right subtree under MerkleUpdate, pairing with left subtree, computing sum diffs.
704+
// Sum is accumulated into sum_diff_out (if non-null), similar to compression flow.
705+
std::function<td::Status(size_t, size_t, td::RefInt256*)> build_right_under_mu =
706+
[&](size_t right_idx, size_t left_idx, td::RefInt256* sum_diff_out) -> td::Status {
707+
if (nodes[right_idx].not_null()) {
708+
if (left_idx != std::numeric_limits<size_t>::max() && sum_diff_out) {
709+
vm::CellSlice cs_left(NoVm(), nodes[left_idx]);
710+
vm::CellSlice cs_right(NoVm(), nodes[right_idx]);
711+
td::RefInt256 vertex_diff = process_shard_accounts_vertex(cs_left, cs_right);
712+
if (vertex_diff.not_null()) {
713+
*sum_diff_out += vertex_diff;
714+
}
715+
}
716+
return td::Status::OK();
727717
}
728-
}
729-
730-
// Store children refs and finalize this right node
731-
try {
718+
td::RefInt256 cur_right_left_diff;
719+
// Build children first
720+
td::RefInt256 sum_child_diff = td::make_refint(0);
732721
for (int j = 0; j < cell_refs_cnt[right_idx]; ++j) {
733-
cell_builders[right_idx].store_ref(nodes[boc_graph[right_idx][j]]);
722+
size_t right_child = boc_graph[right_idx][j];
723+
size_t left_child = (left_idx != std::numeric_limits<size_t>::max() && j < cell_refs_cnt[left_idx])
724+
? boc_graph[left_idx][j]
725+
: std::numeric_limits<size_t>::max();
726+
TRY_STATUS(build_right_under_mu(right_child, left_child, &sum_child_diff));
734727
}
735-
try {
736-
nodes[right_idx] = cell_builders[right_idx].finalize(is_special[right_idx]);
737-
} catch (vm::CellBuilder::CellWriteError& e) {
738-
return td::Status::Error("BOC decompression failed: failed to finalize right vertex (CellWriteError)");
728+
// If this vertex was depth-balance-compressed, reconstruct its data from left + children sum
729+
if (is_depth_balance[right_idx]) {
730+
vm::CellSlice cs_left(NoVm(), nodes[left_idx]);
731+
td::RefInt256 left_grams = extract_balance_from_depth_balance_info(cs_left);
732+
if (left_grams.is_null()) {
733+
return td::Status::Error("BOC decompression failed: depth-balance left vertex has no grams");
734+
}
735+
td::RefInt256 expected_right_grams = left_grams;
736+
expected_right_grams += sum_child_diff;
737+
if (!write_depth_balance_grams(cell_builders[right_idx], expected_right_grams)) {
738+
return td::Status::Error("BOC decompression failed: failed to write depth-balance grams");
739+
}
740+
cur_right_left_diff = sum_child_diff;
739741
}
740-
} catch (vm::VmError& e) {
741-
return td::Status::Error("BOC decompression failed: failed to finalize right vertex (VmError)");
742-
}
743-
744-
// Compute this vertex diff (right - left) to propagate upward
745-
if (left_idx != std::numeric_limits<size_t>::max()) {
746-
vm::CellSlice cs_left(NoVm(), nodes[left_idx]);
747-
vm::CellSlice cs_right(NoVm(), nodes[right_idx]);
748-
td::RefInt256 vertex_diff = process_shard_accounts_vertex(cs_left, cs_right);
749-
if (sum_diff_out && vertex_diff.not_null()) {
750-
*sum_diff_out += vertex_diff;
742+
743+
// Store children refs and finalize this right node
744+
TRY_STATUS(finalize_node(right_idx));
745+
746+
// Compute this vertex diff (right - left) to propagate upward
747+
if (cur_right_left_diff.is_null() &&left_idx != std::numeric_limits<size_t>::max()) {
748+
vm::CellSlice cs_left(NoVm(), nodes[left_idx]);
749+
vm::CellSlice cs_right(NoVm(), nodes[right_idx]);
750+
cur_right_left_diff = process_shard_accounts_vertex(cs_left, cs_right);
751+
}
752+
if (sum_diff_out && cur_right_left_diff.not_null()) {
753+
*sum_diff_out += cur_right_left_diff;
751754
}
752-
}
753-
return td::Status::OK();
754-
};
755-
756-
// General recursive build that handles MerkleUpdate by pairing left/right subtrees
757-
std::function<td::Status(size_t)> build_node = [&](size_t idx) -> td::Status {
758-
if (nodes[idx].not_null()) {
759755
return td::Status::OK();
760-
}
761-
// If this node is a MerkleUpdate (special and first byte == 0x04),
762-
// build left subtree normally first, then right subtree paired with left
763-
if (cell_refs_cnt[idx] >= 2) {
764-
size_t left_idx = boc_graph[idx][0];
765-
size_t right_idx = boc_graph[idx][1];
756+
};
757+
758+
// General recursive build that handles MerkleUpdate by pairing left/right subtrees
759+
std::function<td::Status(size_t)> build_node = [&](size_t idx) -> td::Status {
760+
if (nodes[idx].not_null()) {
761+
return td::Status::OK();
762+
}
763+
// If this node is a MerkleUpdate, build left subtree normally first, then right subtree paired with left
766764
if (is_merkle_update_node(idx)) {
767-
// Build left subtree
765+
size_t left_idx = boc_graph[idx][0];
766+
size_t right_idx = boc_graph[idx][1];
768767
TRY_STATUS(build_node(left_idx));
769-
// Build right subtree under MU pairing with left
770-
td::RefInt256 right_sum_diff = td::make_refint(0);
771-
TRY_STATUS(build_right_under_mu(right_idx, left_idx, &right_sum_diff));
772-
// Finalize current MU node with built children
773-
try {
774-
for (int j = 0; j < cell_refs_cnt[idx]; ++j) {
775-
cell_builders[idx].store_ref(nodes[boc_graph[idx][j]]);
776-
}
777-
try {
778-
nodes[idx] = cell_builders[idx].finalize(is_special[idx]);
779-
} catch (vm::CellBuilder::CellWriteError& e) {
780-
return td::Status::Error("BOC decompression failed: failed to finalize MU node (CellWriteError)");
781-
}
782-
} catch (vm::VmError& e) {
783-
return td::Status::Error("BOC decompression failed: failed to finalize MU node (VmError)");
784-
}
768+
TRY_STATUS(build_right_under_mu(right_idx, left_idx, nullptr));
769+
TRY_STATUS(finalize_node(idx));
785770
return td::Status::OK();
771+
} else {
772+
// Default: build children normally then finalize
773+
for (int j = 0; j < cell_refs_cnt[idx]; ++j) {
774+
TRY_STATUS(build_node(boc_graph[idx][j]));
775+
}
786776
}
777+
778+
TRY_STATUS(finalize_node(idx));
779+
return td::Status::OK();
780+
};
781+
782+
// Build from roots using DFS
783+
for (size_t index : root_indexes) {
784+
TRY_STATUS(build_node(index));
787785
}
788-
// Default: build children normally then finalize
789-
for (int j = 0; j < cell_refs_cnt[idx]; ++j) {
790-
TRY_STATUS(build_node(boc_graph[idx][j]));
791-
}
792-
try {
793-
for (int j = 0; j < cell_refs_cnt[idx]; ++j) {
794-
cell_builders[idx].store_ref(nodes[boc_graph[idx][j]]);
795-
}
796-
try {
797-
nodes[idx] = cell_builders[idx].finalize(is_special[idx]);
798-
} catch (vm::CellBuilder::CellWriteError& e) {
799-
return td::Status::Error("BOC decompression failed: failed to finalize node (CellWriteError)");
800-
}
801-
} catch (vm::VmError& e) {
802-
return td::Status::Error("BOC decompression failed: failed to finalize node (VmError)");
786+
787+
std::vector<td::Ref<vm::Cell>> root_nodes;
788+
root_nodes.reserve(root_count);
789+
for (size_t index : root_indexes) {
790+
root_nodes.push_back(nodes[index]);
803791
}
804-
return td::Status::OK();
805-
};
806-
807-
// Build from roots using DFS
808-
for (size_t index : root_indexes) {
809-
TRY_STATUS(build_node(index));
792+
793+
return root_nodes;
810794
}
811795

812-
std::vector<td::Ref<vm::Cell>> root_nodes;
813-
root_nodes.reserve(root_count);
814-
for (size_t index : root_indexes) {
815-
root_nodes.push_back(nodes[index]);
816-
}
817-
818-
return root_nodes;
819-
}
820-
821796
td::Result<td::BufferSlice> boc_compress(const std::vector<td::Ref<vm::Cell>>& boc_roots, CompressionAlgorithm algo) {
822797
// Check for empty input
823798
if (boc_roots.empty()) {

0 commit comments

Comments
 (0)