@@ -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-
821796td::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