/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2019 Telegram Systems LLP */ #pragma once #include "vm/cells/Cell.h" #include "vm/cells/PrunnedCell.h" #include "common/AtomicRef.h" #include #include "td/utils/port/thread_local.h" #include "td/utils/HazardPointers.h" #include "td/utils/optional.h" namespace vm { template class ExtCell : public Cell { private: struct PrivateTag {}; public: static td::Result>> create(const PrunnedCellInfo& prunned_cell_info, ExtraT&& extra) { TRY_RESULT(prunned_cell, PrunnedCell::create(prunned_cell_info, std::move(extra))); return Ref>(true, std::move(prunned_cell), PrivateTag{}); } ExtCell(Ref> prunned_cell, PrivateTag) : prunned_cell_(std::move(prunned_cell)) { get_thread_safe_counter().add(1); get_thread_safe_counter_unloaded().add(prunned_cell_.load_unsafe().not_null()); } ~ExtCell() { get_thread_safe_counter().add(-1); get_thread_safe_counter_unloaded().add(-static_cast(prunned_cell_.load_unsafe().not_null())); } LevelMask get_level_mask() const override { return CellView(this)->get_level_mask(); } td::Result load_cell() const override { TRY_RESULT(data_cell, load_data_cell()); return LoadedCell{std::move(data_cell), {}, {}}; } td::uint32 get_virtualization() const override { return 0; } CellUsageTree::NodePtr get_tree_node() const override { return {}; } bool is_loaded() const override { return CellView(this)->is_loaded(); } private: mutable td::AtomicRef data_cell_; mutable td::AtomicRef> prunned_cell_; static td::NamedThreadSafeCounter::CounterRef get_thread_safe_counter() { static auto res = td::NamedThreadSafeCounter::get_default().get_counter("ExtCell"); return res; } static td::NamedThreadSafeCounter::CounterRef get_thread_safe_counter_unloaded() { static auto res = td::NamedThreadSafeCounter::get_default().get_counter("ExtCell.unloaded"); return res; } struct CellView { CellView(const ExtCell* cell) { cell_ = cell->data_cell_.get_unsafe(); if (cell_) { return; } prunned_cell_ = cell->prunned_cell_.load(); if (!prunned_cell_.is_null()) { cell_ = &*prunned_cell_; return; } cell_ = cell->data_cell_.get_unsafe(); DCHECK(cell_); } const Cell* operator->() const { return cell_; } td::Ref> prunned_cell_; const Cell* cell_; }; const Hash do_get_hash(td::uint32 level) const override { return CellView(this)->get_hash(level); } td::uint16 do_get_depth(td::uint32 level) const override { return CellView(this)->get_depth(level); } td::Result> load_data_cell() const { auto data_cell = data_cell_.get_unsafe(); if (data_cell) { return Ref(data_cell); } auto prunned_cell = prunned_cell_.load(); if (prunned_cell.is_null()) { data_cell = data_cell_.get_unsafe(); DCHECK(data_cell); return Ref(data_cell); } TRY_RESULT(new_data_cell, Loader::load_data_cell(*this, prunned_cell->get_extra())); TRY_STATUS(prunned_cell->check_equals_unloaded(new_data_cell)); if (data_cell_.store_if_empty(new_data_cell)) { prunned_cell_.store({}); get_thread_safe_counter_unloaded().add(-1); } return data_cell_.load_unsafe(); } }; } // namespace vm