/* 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 "td/utils/Span.h" #include "td/utils/ThreadSafeCounter.h" namespace vm { class DataCell : public Cell { public: DataCell(const DataCell& other) = delete; ~DataCell() override; static void store_depth(td::uint8* dest, td::uint16 depth) { td::bitstring::bits_store_long(dest, depth, depth_bits); } static td::uint16 load_depth(const td::uint8* src) { return td::bitstring::bits_load_ulong(src, depth_bits) & 0xff; } protected: struct Info { unsigned bits_; // d1 unsigned char refs_count_ : 3; bool is_special_ : 1; unsigned char level_mask_ : 3; unsigned char hash_count_ : 3; unsigned char virtualization_ : 3; unsigned char d1() const { return d1(LevelMask{level_mask_}); } unsigned char d1(LevelMask level_mask) const { // d1 = refs_count + 8 * is_special + 32 * level // + 16 * with_hashes - for seriazlization // d1 = 7 + 16 + 32 * l - for absent cells return static_cast(refs_count_ + 8 * is_special_ + 32 * level_mask.get_mask()); } unsigned char d2() const { auto res = static_cast((bits_ / 8) * 2); if ((bits_ & 7) != 0) { return static_cast(res + 1); } return res; } size_t get_hashes_offset() const { return 0; } size_t get_refs_offset() const { return get_hashes_offset() + hash_bytes * hash_count_; } size_t get_depth_offset() const { return get_refs_offset() + refs_count_ * sizeof(Cell*); } size_t get_data_offset() const { return get_depth_offset() + sizeof(td::uint16) * hash_count_; } size_t get_storage_size() const { return get_data_offset() + (bits_ + 7) / 8; } const Hash* get_hashes(const char* storage) const { return reinterpret_cast(storage + get_hashes_offset()); } Hash* get_hashes(char* storage) const { return reinterpret_cast(storage + get_hashes_offset()); } const td::uint16* get_depth(const char* storage) const { return reinterpret_cast(storage + get_depth_offset()); } td::uint16* get_depth(char* storage) const { return reinterpret_cast(storage + get_depth_offset()); } const unsigned char* get_data(const char* storage) const { return reinterpret_cast(storage + get_data_offset()); } unsigned char* get_data(char* storage) const { return reinterpret_cast(storage + get_data_offset()); } Cell* const* get_refs(const char* storage) const { return reinterpret_cast(storage + get_refs_offset()); } Cell** get_refs(char* storage) const { return reinterpret_cast(storage + get_refs_offset()); } }; Info info_; virtual char* get_storage() = 0; virtual const char* get_storage() const = 0; // TODO: we may also save three different pointers void destroy_storage(char* storage); explicit DataCell(Info info); Cell* get_ref_raw_ptr(unsigned idx) const { DCHECK(idx < get_refs_cnt()); return info_.get_refs(get_storage())[idx]; } public: td::Result load_cell() const override { return LoadedCell{Ref{this}, {}, {}}; } unsigned get_refs_cnt() const { return info_.refs_count_; } unsigned get_bits() const { return info_.bits_; } unsigned size_refs() const { return info_.refs_count_; } unsigned size() const { return info_.bits_; } const unsigned char* get_data() const { return info_.get_data(get_storage()); } Ref get_ref(unsigned idx) const { if (idx >= get_refs_cnt()) { return Ref{}; } return Ref(get_ref_raw_ptr(idx)); } td::uint32 get_virtualization() const override { return info_.virtualization_; } CellUsageTree::NodePtr get_tree_node() const override { return {}; } bool is_loaded() const override { return true; } LevelMask get_level_mask() const override { return LevelMask{info_.level_mask_}; } bool is_special() const { return info_.is_special_; } SpecialType special_type() const; int get_serialized_size(bool with_hashes = false) const { return ((get_bits() + 23) >> 3) + (with_hashes ? get_level_mask().get_hashes_count() * (hash_bytes + depth_bytes) : 0); } int serialize(unsigned char* buff, int buff_size, bool with_hashes = false) const; std::string serialize() const; std::string to_hex() const; static td::int64 get_total_data_cells() { return get_thread_safe_counter().sum(); } template void store(StorerT& storer) const { storer.template store_binary(info_.d1()); storer.template store_binary(info_.d2()); storer.store_slice(td::Slice(get_data(), (get_bits() + 7) / 8)); } protected: static constexpr auto max_storage_size = max_refs * sizeof(void*) + (max_level + 1) * hash_bytes + max_bytes; private: static td::NamedThreadSafeCounter::CounterRef get_thread_safe_counter() { static auto res = td::NamedThreadSafeCounter::get_default().get_counter("DataCell"); return res; } static std::unique_ptr create_empty_data_cell(Info info); const Hash do_get_hash(td::uint32 level) const override; td::uint16 do_get_depth(td::uint32 level) const override; friend class CellBuilder; static td::Result> create(td::ConstBitPtr data, unsigned bits, td::Span> refs, bool special); static td::Result> create(td::ConstBitPtr data, unsigned bits, td::MutableSpan> refs, bool special); }; std::ostream& operator<<(std::ostream& os, const DataCell& c); } // namespace vm