/* 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 "common/bitstring.h" #include "vm/cells.h" #include "vm/cellslice.h" #include "vm/stack.hpp" #include namespace vm { using td::BitSlice; using td::Ref; namespace dict { struct LabelParser { enum { chk_none = 0, chk_min = 1, chk_size = 2, chk_all = 3 }; Ref remainder; int l_offs; int l_same; int l_bits; unsigned s_bits; LabelParser(Ref cs, int max_label_len, int auto_validate = chk_all); LabelParser(Ref cell, int max_label_len, int auto_validate = chk_all); int is_valid() const { return l_offs; } void validate() const; void validate_simple(int n) const; void validate_ext(int n) const; bool is_prefix_of(td::ConstBitPtr key, int len) const; bool has_prefix(td::ConstBitPtr key, int len) const; int common_prefix_len(td::ConstBitPtr key, int len) const; int extract_label_to(td::BitPtr to); int copy_label_prefix_to(td::BitPtr to, int max_len) const; td::ConstBitPtr bits() const { return remainder->data_bits(); } td::ConstBitPtr bits_end() const { return bits() + l_bits; } void skip_label() { remainder.write().advance(s_bits); } void clear() { remainder.clear(); } private: bool parse_label(CellSlice& cs, int max_label_len); }; struct AugmentationData { virtual ~AugmentationData() = default; virtual bool skip_extra(vm::CellSlice& cs) const = 0; virtual bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& val_cs) const = 0; virtual bool eval_fork(vm::CellBuilder& cb, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const = 0; virtual bool eval_empty(vm::CellBuilder& cb) const = 0; virtual bool check_leaf(vm::CellSlice& cs, vm::CellSlice& val_cs) const; virtual bool check_fork(vm::CellSlice& cs, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const; virtual bool check_empty(vm::CellSlice& cs) const; virtual bool check_leaf_key_extra(vm::CellSlice& val_cs, vm::CellSlice& extra_cs, td::ConstBitPtr key, int key_len) const { return check_leaf(extra_cs, val_cs); } Ref extract_extra(vm::CellSlice& cs) const; Ref extract_extra(Ref cs_ref) const; bool extract_extra_to(vm::CellSlice& cs, Ref& extra_csr) const { return (extra_csr = extract_extra(cs)).not_null(); } bool extract_extra_to(Ref cs_ref, Ref& extra_csr) const { return (extra_csr = extract_extra(std::move(cs_ref))).not_null(); } bool extract_extra_to(vm::CellSlice& cs, vm::CellSlice& extra) const; }; static inline bool store_cell_dict(vm::CellBuilder& cb, Ref dict_root) { return dict_root.not_null() ? cb.store_long_bool(1, 1) && cb.store_ref_bool(std::move(dict_root)) : cb.store_long_bool(0, 1); } } // namespace dict struct CombineError {}; // thrown by Dictionary::combine_with struct CombineErrorValue { int arg_; }; struct DictNonEmpty {}; struct DictAdvance {}; class DictionaryBase { protected: mutable Ref root; Ref root_cell; int key_bits; mutable int flags; enum { f_valid = 1, f_root_cached = 2, f_invalid = 0x80 }; public: enum class SetMode : int { Set = 3, Replace = 1, Add = 2 }; enum { max_key_bits = 1023, max_key_bytes = (max_key_bits + 7) / 8 }; typedef std::function store_value_func_t; DictionaryBase(int _n, bool validate = true); DictionaryBase(Ref _root, int _n, bool validate = true); DictionaryBase(const CellSlice& root_cs, int _n, bool validate = true); DictionaryBase(DictAdvance, CellSlice& root_cs, int _n, bool validate = true); DictionaryBase(Ref cell, int _n, bool validate = true); DictionaryBase(DictNonEmpty, Ref _root, int _n, bool validate = true); DictionaryBase(DictNonEmpty, const CellSlice& root_cs, int _n, bool validate = true); virtual ~DictionaryBase() = default; static Ref construct_root_from(const CellSlice& root_node_cs); Ref get_root() const; Ref extract_root() &&; Ref get_root_cell() const { return root_cell; } Ref extract_root_cell() && { return std::move(root_cell); } bool append_dict_to_bool(CellBuilder& cb) &&; bool append_dict_to_bool(CellBuilder& cb) const &; int get_key_bits() const { return key_bits; } bool is_valid() const { return flags & f_valid; } void reset() { root.clear(); root_cell.clear(); flags = f_valid; } virtual bool validate(); void force_validate(); bool is_empty() const { return root_cell.is_null(); } static Ref get_empty_dictionary(); protected: bool init_root_for_nonempty(const CellSlice& cs); bool invalidate() { flags |= f_invalid; return false; } bool compute_root() const; static Ref new_empty_dictionary(); void set_root_cell(Ref cell) { root_cell = std::move(cell); flags &= ~f_root_cached; } }; class DictionaryFixed : public DictionaryBase { public: typedef std::function filter_func_t; typedef std::function, Ref)> simple_combine_func_t; typedef std::function, Ref, td::ConstBitPtr, int)> combine_func_t; typedef std::function, td::ConstBitPtr, int)> foreach_func_t; typedef std::function, Ref)> scan_diff_func_t; DictionaryFixed(int _n, bool validate = true) : DictionaryBase(_n, validate) { } DictionaryFixed(Ref _root, int _n, bool validate = true) : DictionaryBase(std::move(_root), _n, validate) { } DictionaryFixed(const CellSlice& root_cs, int _n, bool validate = true) : DictionaryBase(root_cs, _n, validate) { } DictionaryFixed(DictAdvance, CellSlice& root_cs, int _n, bool validate = true) : DictionaryBase(DictAdvance(), root_cs, _n, validate) { } DictionaryFixed(Ref cell, int _n, bool validate = true) : DictionaryBase(std::move(cell), _n, validate) { } DictionaryFixed(DictNonEmpty, Ref _root, int _n, bool validate = true) : DictionaryBase(DictNonEmpty(), std::move(_root), _n, validate) { } DictionaryFixed(DictNonEmpty, const CellSlice& root_cs, int _n, bool validate = true) : DictionaryBase(DictNonEmpty(), root_cs, _n, validate) { } static BitSlice integer_key(td::RefInt256 x, unsigned n, bool sgnd = true, unsigned char buffer[128] = 0, bool quiet = false); static bool integer_key_simple(td::RefInt256 x, unsigned n, bool sgnd, td::BitPtr buffer, bool quiet = false); bool key_exists(td::ConstBitPtr key, int key_len); bool int_key_exists(long long key); bool uint_key_exists(unsigned long long key); Ref lookup(td::ConstBitPtr key, int key_len); Ref lookup_delete(td::ConstBitPtr key, int key_len); Ref get_minmax_key(td::BitPtr key_buffer, int key_len, bool fetch_max = false, bool invert_first = false); Ref extract_minmax_key(td::BitPtr key_buffer, int key_len, bool fetch_max = false, bool invert_first = false); Ref lookup_nearest_key(td::BitPtr key_buffer, int key_len, bool fetch_next = false, bool allow_eq = false, bool invert_first = false); bool has_common_prefix(td::ConstBitPtr prefix, int prefix_len); int get_common_prefix(td::BitPtr buffer, unsigned buffer_len); bool cut_prefix_subdict(td::ConstBitPtr prefix, int prefix_len, bool remove_prefix = false); Ref extract_prefix_subdict_root(td::ConstBitPtr prefix, int prefix_len, bool remove_prefix = false); bool check_for_each(const foreach_func_t& foreach_func, bool invert_first = false); int filter(filter_func_t check); bool combine_with(DictionaryFixed& dict2, const combine_func_t& combine_func, int mode = 0); bool combine_with(DictionaryFixed& dict2, const simple_combine_func_t& simple_combine_func, int mode = 0); bool combine_with(DictionaryFixed& dict2); bool scan_diff(DictionaryFixed& dict2, const scan_diff_func_t& diff_func, int check_augm = 0); bool validate_check(const foreach_func_t& foreach_func, bool invert_first = false); bool validate_all(); template bool key_exists(const T& key) { return key_exists(key.bits(), key.size()); } template Ref lookup(const T& key) { return lookup(key.bits(), key.size()); } template Ref lookup_delete(const T& key) { return lookup_delete(key.bits(), key.size()); } template Ref get_minmax_key(T& key_buffer, bool fetch_max = false, bool invert_first = false) { return get_minmax_key(key_buffer.bits(), key_buffer.size(), fetch_max, invert_first); } protected: virtual int label_mode() const { return dict::LabelParser::chk_all; } virtual Ref finish_create_leaf(CellBuilder& cb, const CellSlice& value) const; virtual Ref finish_create_fork(CellBuilder& cb, Ref c1, Ref c2, int n) const; virtual bool check_fork(CellSlice& cs, Ref c1, Ref c2, int n) const { return true; } virtual bool check_leaf(CellSlice& cs, td::ConstBitPtr key, int key_len) const { return true; } bool check_leaf(Ref cs_ref, td::ConstBitPtr key, int key_len) const { return check_leaf(cs_ref.write(), key, key_len); } bool check_fork_raw(Ref cs_ref, int n) const; private: std::pair, Ref> dict_lookup_delete(Ref dict, td::ConstBitPtr key, int n) const; Ref dict_lookup_minmax(Ref dict, td::BitPtr key_buffer, int n, int mode) const; Ref dict_lookup_nearest(Ref dict, td::BitPtr key_buffer, int n, bool allow_eq, int mode) const; std::pair, bool> extract_prefix_subdict_internal(Ref dict, td::ConstBitPtr prefix, int prefix_len, bool remove_prefix = false) const; bool dict_check_for_each(Ref dict, td::BitPtr key_buffer, int n, int total_key_len, const foreach_func_t& foreach_func, bool invert_first = false) const; std::pair, int> dict_filter(Ref dict, td::BitPtr key, int n, const filter_func_t& check_leaf) const; Ref dict_combine_with(Ref dict1, Ref dict2, td::BitPtr key_buffer, int n, int total_key_len, const combine_func_t& combine_func, int mode = 0, int skip1 = 0, int skip2 = 0) const; bool dict_scan_diff(Ref dict1, Ref dict2, td::BitPtr key_buffer, int n, int total_key_len, const scan_diff_func_t& diff_func, int mode = 0, int skip1 = 0, int skip2 = 0) const; bool dict_validate_check(Ref dict, td::BitPtr key_buffer, int n, int total_key_len, const foreach_func_t& foreach_func, bool invert_first = false) const; }; class Dictionary final : public DictionaryFixed { public: typedef std::function)> simple_map_func_t; typedef std::function, td::ConstBitPtr, int)> map_func_t; Dictionary(int _n, bool validate = true) : DictionaryFixed(_n, validate) { } Dictionary(Ref _root, int _n, bool validate = true) : DictionaryFixed(std::move(_root), _n, validate) { } Dictionary(const CellSlice& root_cs, int _n, bool validate = true) : DictionaryFixed(root_cs, _n, validate) { } Dictionary(DictAdvance, CellSlice& root_cs, int _n, bool validate = true) : DictionaryFixed(DictAdvance(), root_cs, _n, validate) { } Dictionary(Ref cell, int _n, bool validate = true) : DictionaryFixed(std::move(cell), _n, validate) { } Dictionary(DictNonEmpty, Ref _root, int _n, bool validate = true) : DictionaryFixed(DictNonEmpty(), std::move(_root), _n, validate) { } Dictionary(DictNonEmpty, const CellSlice& root_cs, int _n, bool validate = true) : DictionaryFixed(DictNonEmpty(), root_cs, _n, validate) { } Ref lookup_ref(td::ConstBitPtr key, int key_len); Ref lookup_delete_ref(td::ConstBitPtr key, int key_len); bool set(td::ConstBitPtr key, int key_len, Ref value, SetMode mode = SetMode::Set); bool set_ref(td::ConstBitPtr key, int key_len, Ref val_ref, SetMode mode = SetMode::Set); bool set_builder(td::ConstBitPtr key, int key_len, Ref val_b, SetMode mode = SetMode::Set); bool set_builder(td::ConstBitPtr key, int key_len, const CellBuilder& val_b, SetMode mode = SetMode::Set); bool set_gen(td::ConstBitPtr key, int key_len, const store_value_func_t& store_val, SetMode mode = SetMode::Set); Ref lookup_set(td::ConstBitPtr key, int key_len, Ref value, SetMode mode = SetMode::Set); Ref lookup_set_ref(td::ConstBitPtr key, int key_len, Ref val_ref, SetMode mode = SetMode::Set); Ref lookup_set_builder(td::ConstBitPtr key, int key_len, Ref val_b, SetMode mode = SetMode::Set); Ref lookup_set_gen(td::ConstBitPtr key, int key_len, const store_value_func_t& store_val, SetMode mode = SetMode::Set); Ref get_minmax_key_ref(td::BitPtr key_buffer, int key_len, bool fetch_max = false, bool invert_first = false); Ref extract_minmax_key_ref(td::BitPtr key_buffer, int key_len, bool fetch_max = false, bool invert_first = false); void map(const map_func_t& map_func); void map(const simple_map_func_t& simple_map_func); template Ref lookup_ref(const T& key) { return lookup_ref(key.bits(), key.size()); } template Ref lookup_delete_ref(const T& key) { return lookup_delete_ref(key.bits(), key.size()); } template bool set(const T& key, Ref value, SetMode mode = SetMode::Set) { return set(key.bits(), key.size(), std::move(value), mode); } template bool set_ref(const T& key, Ref val_ref, SetMode mode = SetMode::Set) { return set_ref(key.bits(), key.size(), std::move(val_ref), mode); } template bool set_builder(const T& key, const CellBuilder& val_b, SetMode mode = SetMode::Set) { return set_builder(key.bits(), key.size(), val_b, mode); } template bool set_builder(const T& key, Ref val_ref, SetMode mode = SetMode::Set) { return set_builder(key.bits(), key.size(), std::move(val_ref), mode); } template Ref lookup_set(const T& key, Ref value, SetMode mode = SetMode::Set) { return lookup_set(key.bits(), key.size(), std::move(value), mode); } template Ref lookup_set_ref(const T& key, Ref val_ref, SetMode mode = SetMode::Set) { return lookup_set_ref(key.bits(), key.size(), std::move(val_ref), mode); } template Ref lookup_set_builder(const T& key, const CellBuilder& val_b, SetMode mode = SetMode::Set) { return lookup_set_builder(key.bits(), key.size(), val_b, mode); } template Ref lookup_set_builder(const T& key, Ref val_ref, SetMode mode = SetMode::Set) { return lookup_set_builder(key.bits(), key.size(), std::move(val_ref), mode); } private: bool check_fork(CellSlice& cs, Ref c1, Ref c2, int n) const override { return cs.empty_ext(); } static Ref extract_value_ref(Ref cs); std::pair, int> dict_filter(Ref dict, td::BitPtr key, int n, const filter_func_t& check_leaf) const; }; class PrefixDictionary final : public DictionaryBase { public: PrefixDictionary(int _n, bool validate = true) : DictionaryBase(_n, validate) { } PrefixDictionary(Ref _root, int _n, bool validate = true) : DictionaryBase(std::move(_root), _n, validate) { } PrefixDictionary(Ref cell, int _n, bool validate = true) : DictionaryBase(std::move(cell), _n, validate) { } Ref lookup(td::ConstBitPtr key, int key_len); std::pair, int> lookup_prefix(td::ConstBitPtr key, int key_len); Ref lookup_delete(td::ConstBitPtr key, int key_len); bool set(td::ConstBitPtr key, int key_len, Ref value, SetMode mode = SetMode::Set); bool set_builder(td::ConstBitPtr key, int key_len, Ref val_b, SetMode mode = SetMode::Set); bool set_gen(td::ConstBitPtr key, int key_len, const store_value_func_t& store_val, SetMode mode = SetMode::Set); }; using dict::AugmentationData; class AugmentedDictionary final : public DictionaryFixed { const AugmentationData& aug; public: typedef std::function, Ref, td::ConstBitPtr, int)> foreach_extra_func_t; // return value of traverse_func: < 0 = error, 0 = skip, 1 = visit only left, 2 = visit only right, 5 = visit right, then left, 6 = visit left, then right // for leaf nodes, all >0 values mean accept and return node as the final result, 0 = skip (continue scanning) typedef std::function extra, Ref value)> traverse_func_t; AugmentedDictionary(int _n, const AugmentationData& _aug, bool validate = true); AugmentedDictionary(Ref _root, int _n, const AugmentationData& _aug, bool validate = true); AugmentedDictionary(Ref cell, int _n, const AugmentationData& _aug, bool validate = true); AugmentedDictionary(DictNonEmpty, Ref _root, int _n, const AugmentationData& _aug, bool validate = true); Ref get_empty_dictionary() const; Ref get_root() const; Ref extract_root() &&; bool append_dict_to_bool(CellBuilder& cb) &&; bool append_dict_to_bool(CellBuilder& cb) const &; Ref get_root_extra() const; Ref lookup(td::ConstBitPtr key, int key_len); Ref lookup_ref(td::ConstBitPtr key, int key_len); Ref lookup_with_extra(td::ConstBitPtr key, int key_len); std::pair, Ref> lookup_extra(td::ConstBitPtr key, int key_len); std::pair, Ref> lookup_ref_extra(td::ConstBitPtr key, int key_len); Ref lookup_delete(td::ConstBitPtr key, int key_len); Ref lookup_delete_ref(td::ConstBitPtr key, int key_len); Ref lookup_delete_with_extra(td::ConstBitPtr key, int key_len); std::pair, Ref> lookup_delete_extra(td::ConstBitPtr key, int key_len); std::pair, Ref> lookup_delete_ref_extra(td::ConstBitPtr key, int key_len); bool set(td::ConstBitPtr key, int key_len, const CellSlice& value, SetMode mode = SetMode::Set); bool set(td::ConstBitPtr key, int key_len, Ref value, SetMode mode = SetMode::Set); bool set_ref(td::ConstBitPtr key, int key_len, Ref val_ref, SetMode mode = SetMode::Set); bool set_builder(td::ConstBitPtr key, int key_len, const CellBuilder& value, SetMode mode = SetMode::Set); bool check_for_each_extra(const foreach_extra_func_t& foreach_extra_func, bool invert_first = false); std::pair, Ref> traverse_extra(td::BitPtr key_buffer, int key_len, const traverse_func_t& traverse_node); bool validate_check_extra(const foreach_extra_func_t& foreach_extra_func, bool invert_first = false); bool validate() override; template Ref lookup(const T& key) { return lookup(key.bits(), key.size()); } template Ref lookup_ref(const T& key) { return lookup_ref(key.bits(), key.size()); } template bool set(const T& key, Ref val_ref, SetMode mode = SetMode::Set) { return set(key.bits(), key.size(), std::move(val_ref), mode); } template bool set(const T& key, const CellSlice& value, SetMode mode = SetMode::Set) { return set(key.bits(), key.size(), value, mode); } template bool set_ref(const T& key, Ref val_ref, SetMode mode = SetMode::Set) { return set_ref(key.bits(), key.size(), std::move(val_ref), mode); } template bool set_builder(const T& key, const CellBuilder& val_b, SetMode mode = SetMode::Set) { return set_builder(key.bits(), key.size(), val_b, mode); } template Ref lookup_delete(const T& key) { return lookup_delete(key.bits(), key.size()); } template Ref lookup_delete_ref(const T& key) { return lookup_delete_ref(key.bits(), key.size()); } Ref extract_value(Ref value_extra) const; Ref extract_value_ref(Ref value_extra) const; std::pair, Ref> decompose_value_extra(Ref value_extra) const; std::pair, Ref> decompose_value_ref_extra(Ref value_extra) const; private: bool compute_root() const; Ref get_node_extra(Ref cell_ref, int n) const; bool check_leaf(CellSlice& cs, td::ConstBitPtr key, int key_len) const override; bool check_fork(CellSlice& cs, Ref c1, Ref c2, int n) const override; Ref finish_create_leaf(CellBuilder& cb, const CellSlice& value) const override; Ref finish_create_fork(CellBuilder& cb, Ref c1, Ref c2, int n) const override; std::pair, bool> dict_set(Ref dict, td::ConstBitPtr key, int n, const CellSlice& value, SetMode mode = SetMode::Set) const; int label_mode() const override { return dict::LabelParser::chk_size; } std::pair, Ref> dict_traverse_extra(Ref dict, td::BitPtr key_buffer, int n, const traverse_func_t& traverse_node) const; }; } // namespace vm