mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	* Add fift-based disassembler * Fift improvements: namespaces, hashmaps, flow controls * Fift: add lib with better block structuring and more * Minor changes in fift HashMap + tests (#643) * Minor changes in fift HashMap * Add tests for extended fift --------- Co-authored-by: OmicronTau <omicron@ton.org> Co-authored-by: Tolya <1449561+tolya-yanot@users.noreply.github.com> Co-authored-by: SpyCheese <mikle98@yandex.ru>
		
			
				
	
	
		
			371 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			371 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|     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 <http://www.gnu.org/licenses/>.
 | |
| */
 | |
| #include "HashMap.h"
 | |
| #include "td/utils/Random.h"
 | |
| #include "IntCtx.h"
 | |
| 
 | |
| namespace fift {
 | |
| using td::Ref;
 | |
| 
 | |
| DictKey::DictKey(vm::StackEntry se) {
 | |
|   auto tp = tp_ = se.type();
 | |
|   switch (tp) {
 | |
|     case Type::t_int:
 | |
|       ref_ = se.as_int();
 | |
|       break;
 | |
|     case Type::t_atom:
 | |
|       ref_ = se.as_atom();
 | |
|       break;
 | |
|     case Type::t_string:
 | |
|       ref_ = se.as_string_ref();
 | |
|       break;
 | |
|     case Type::t_bytes:
 | |
|       ref_ = se.as_bytes_ref();
 | |
|       break;
 | |
|     case Type::t_null:
 | |
|       break;
 | |
|     default:
 | |
|       throw IntError{"unsupported key type"};
 | |
|   }
 | |
|   compute_hash();
 | |
| }
 | |
| 
 | |
| DictKey::operator vm::StackEntry() const& {
 | |
|   switch (tp_) {
 | |
|     case Type::t_int:
 | |
|       return value<td::CntInt256>();
 | |
|     case Type::t_atom:
 | |
|       return value<vm::Atom>();
 | |
|     case Type::t_string:
 | |
|     case Type::t_bytes:
 | |
|       return {value<td::Cnt<std::string>>(), tp_ == Type::t_bytes};
 | |
|     default:
 | |
|       return {};
 | |
|   }
 | |
| }
 | |
| 
 | |
| DictKey::operator vm::StackEntry() && {
 | |
|   switch (tp_) {
 | |
|     case Type::t_int:
 | |
|       return move_value<td::CntInt256>();
 | |
|     case Type::t_atom:
 | |
|       return move_value<vm::Atom>();
 | |
|     case Type::t_string:
 | |
|     case Type::t_bytes:
 | |
|       return {move_value<td::Cnt<std::string>>(), tp_ == Type::t_bytes};
 | |
|     default:
 | |
|       return {};
 | |
|   }
 | |
| }
 | |
| 
 | |
| std::ostream& operator<<(std::ostream& os, const DictKey& dkey) {
 | |
|   return os << vm::StackEntry(dkey).to_string();
 | |
| }
 | |
| 
 | |
| int DictKey::cmp_internal(const DictKey& other) const {
 | |
|   if (tp_ != other.tp_) {
 | |
|     return tp_ < other.tp_ ? -1 : 1;
 | |
|   }
 | |
|   switch (tp_) {
 | |
|     case Type::t_int:
 | |
|       return td::cmp(value<td::CntInt256>(), other.value<td::CntInt256>());
 | |
|     case Type::t_atom: {
 | |
|       int u = value<vm::Atom>()->index(), v = other.value<vm::Atom>()->index();
 | |
|       return u == v ? 0 : (u < v ? -1 : 1);
 | |
|     }
 | |
|     case Type::t_string:
 | |
|     case Type::t_bytes:
 | |
|       return value<td::Cnt<std::string>>()->compare(*other.value<td::Cnt<std::string>>());
 | |
|     default:
 | |
|       return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| int DictKey::cmp(const DictKey& other) const {
 | |
|   if (hash_ < other.hash_) {
 | |
|     return -1;
 | |
|   } else if (hash_ > other.hash_) {
 | |
|     return 1;
 | |
|   } else {
 | |
|     return cmp_internal(other);
 | |
|   }
 | |
| }
 | |
| 
 | |
| DictKey::keyhash_t DictKey::compute_str_hash(DictKey::keyhash_t h, const char* str, std::size_t len) {
 | |
|   const char* end = str + len;
 | |
|   while (str < end) {
 | |
|     h = h * StrHash + (unsigned char)*str++;
 | |
|   }
 | |
|   return h;
 | |
| }
 | |
| 
 | |
| DictKey::keyhash_t DictKey::compute_int_hash(td::AnyIntView<> x) {
 | |
|   keyhash_t h = IntHash0;
 | |
|   for (int i = 0; i < x.size(); i++) {
 | |
|     h = h * MixConst3 + x.digits[i];
 | |
|   }
 | |
|   return h * MixConst4;
 | |
| }
 | |
| 
 | |
| DictKey::keyhash_t DictKey::compute_hash() {
 | |
|   switch (tp_) {
 | |
|     case Type::t_int:
 | |
|       return hash_ = compute_int_hash(value<td::CntInt256>()->as_any_int());
 | |
|     case Type::t_atom:
 | |
|       return hash_ = value<vm::Atom>()->index() * MixConst1 + MixConst2;
 | |
|     case Type::t_string:
 | |
|     case Type::t_bytes: {
 | |
|       auto ref = value<td::Cnt<std::string>>();
 | |
|       return hash_ = compute_str_hash(tp_, ref->data(), ref->size());
 | |
|     }
 | |
|     default:
 | |
|       return hash_ = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| const Hashmap* Hashmap::lookup_key_aux(const Hashmap* root, const DictKey& key) {
 | |
|   if (key.is_null()) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   while (root) {
 | |
|     int r = key.cmp(root->key_);
 | |
|     if (!r) {
 | |
|       break;
 | |
|     }
 | |
|     root = (r < 0 ? root->left_.get() : root->right_.get());
 | |
|   }
 | |
|   return root;
 | |
| }
 | |
| 
 | |
| Ref<Hashmap> Hashmap::lookup_key(Ref<Hashmap> root, const DictKey& key) {
 | |
|   return Ref<Hashmap>(lookup_key_aux(root.get(), key));
 | |
| }
 | |
| 
 | |
| vm::StackEntry Hashmap::get_key(Ref<Hashmap> root, const DictKey& key) {
 | |
|   auto node = lookup_key_aux(root.get(), key);
 | |
|   if (node) {
 | |
|     return node->value_;
 | |
|   } else {
 | |
|     return {};
 | |
|   }
 | |
| }
 | |
| 
 | |
| std::pair<Ref<Hashmap>, vm::StackEntry> Hashmap::get_remove_key(Ref<Hashmap> root, const DictKey& key) {
 | |
|   if (root.is_null() || key.is_null()) {
 | |
|     return {std::move(root), {}};
 | |
|   }
 | |
|   vm::StackEntry val;
 | |
|   auto res = root->get_remove_internal(key, val);
 | |
|   if (val.is_null()) {
 | |
|     return {std::move(root), {}};
 | |
|   } else {
 | |
|     return {std::move(res), std::move(val)};
 | |
|   }
 | |
| }
 | |
| 
 | |
| Ref<Hashmap> Hashmap::remove_key(Ref<Hashmap> root, const DictKey& key) {
 | |
|   if (root.is_null() || key.is_null()) {
 | |
|     return root;
 | |
|   }
 | |
|   vm::StackEntry val;
 | |
|   auto res = root->get_remove_internal(key, val);
 | |
|   if (val.is_null()) {
 | |
|     return root;
 | |
|   } else {
 | |
|     return res;
 | |
|   }
 | |
| }
 | |
| 
 | |
| Ref<Hashmap> Hashmap::get_remove_internal(const DictKey& key, vm::StackEntry& val) const {
 | |
|   int r = key.cmp(key_);
 | |
|   if (!r) {
 | |
|     val = value_;
 | |
|     return merge(left_, right_);
 | |
|   } else if (r < 0) {
 | |
|     if (left_.is_null()) {
 | |
|       return {};
 | |
|     } else {
 | |
|       auto res = left_->get_remove_internal(key, val);
 | |
|       if (val.is_null()) {
 | |
|         return res;
 | |
|       } else {
 | |
|         return td::make_ref<Hashmap>(key_, value_, std::move(res), right_, y_);
 | |
|       }
 | |
|     }
 | |
|   } else if (right_.is_null()) {
 | |
|     return {};
 | |
|   } else {
 | |
|     auto res = right_->get_remove_internal(key, val);
 | |
|     if (val.is_null()) {
 | |
|       return res;
 | |
|     } else {
 | |
|       return td::make_ref<Hashmap>(key_, value_, left_, std::move(res), y_);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| Ref<Hashmap> Hashmap::merge(Ref<Hashmap> a, Ref<Hashmap> b) {
 | |
|   if (a.is_null()) {
 | |
|     return b;
 | |
|   } else if (b.is_null()) {
 | |
|     return a;
 | |
|   } else if (a->y_ > b->y_) {
 | |
|     auto& aa = a.write();
 | |
|     aa.right_ = merge(std::move(aa.right_), std::move(b));
 | |
|     return a;
 | |
|   } else {
 | |
|     auto& bb = b.write();
 | |
|     bb.left_ = merge(std::move(a), std::move(bb.left_));
 | |
|     return b;
 | |
|   }
 | |
| }
 | |
| 
 | |
| Ref<Hashmap> Hashmap::set(Ref<Hashmap> root, const DictKey& key, vm::StackEntry value) {
 | |
|   if (!key.is_null() && !replace(root, key, value) && !value.is_null()) {
 | |
|     insert(root, key, value, new_y());
 | |
|   }
 | |
|   return root;
 | |
| }
 | |
| 
 | |
| bool Hashmap::replace(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value) {
 | |
|   if (root.is_null() || key.is_null()) {
 | |
|     return false;
 | |
|   }
 | |
|   if (value.is_null()) {
 | |
|     auto res = root->get_remove_internal(key, value);
 | |
|     if (value.is_null()) {
 | |
|       return false;
 | |
|     } else {
 | |
|       root = std::move(res);
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   bool found = false;
 | |
|   auto res = root->replace_internal(key, std::move(value), found);
 | |
|   if (found) {
 | |
|     root = std::move(res);
 | |
|   }
 | |
|   return found;
 | |
| }
 | |
| 
 | |
| Ref<Hashmap> Hashmap::replace_internal(const DictKey& key, const vm::StackEntry& value, bool& found) const {
 | |
|   int r = key.cmp(key_);
 | |
|   if (!r) {
 | |
|     found = true;
 | |
|     return td::make_ref<Hashmap>(key_, value, left_, right_, y_);
 | |
|   } else if (r < 0) {
 | |
|     if (left_.is_null()) {
 | |
|       found = false;
 | |
|       return {};
 | |
|     }
 | |
|     auto res = left_->replace_internal(key, value, found);
 | |
|     if (!found) {
 | |
|       return {};
 | |
|     }
 | |
|     return td::make_ref<Hashmap>(key_, value_, std::move(res), right_, y_);
 | |
|   } else {
 | |
|     if (right_.is_null()) {
 | |
|       found = false;
 | |
|       return {};
 | |
|     }
 | |
|     auto res = right_->replace_internal(key, value, found);
 | |
|     if (!found) {
 | |
|       return {};
 | |
|     }
 | |
|     return td::make_ref<Hashmap>(key_, value_, left_, std::move(res), y_);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void Hashmap::insert(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value, long long y) {
 | |
|   if (root.is_null()) {
 | |
|     root = td::make_ref<Hashmap>(key, std::move(value), empty(), empty(), y);
 | |
|     return;
 | |
|   }
 | |
|   if (root->y_ <= y) {
 | |
|     auto res = split(std::move(root), key);
 | |
|     root = td::make_ref<Hashmap>(key, std::move(value), std::move(res.first), std::move(res.second), y);
 | |
|     return;
 | |
|   }
 | |
|   int r = key.cmp(root->key_);
 | |
|   CHECK(r);
 | |
|   insert(r < 0 ? root.write().left_ : root.write().right_, key, std::move(value), y);
 | |
| }
 | |
| 
 | |
| std::pair<Ref<Hashmap>, Ref<Hashmap>> Hashmap::split(Ref<Hashmap> root, const DictKey& key, bool cmpv) {
 | |
|   if (root.is_null()) {
 | |
|     return {{}, {}};
 | |
|   }
 | |
|   int r = key.cmp(root->key_);
 | |
|   if (r < (int)cmpv) {
 | |
|     if (root->left_.is_null()) {
 | |
|       return {{}, std::move(root)};
 | |
|     }
 | |
|     auto res = split(root->left_, key, cmpv);
 | |
|     return {std::move(res.first),
 | |
|             td::make_ref<Hashmap>(root->key_, root->value_, std::move(res.second), root->right_, root->y_)};
 | |
|   } else {
 | |
|     if (root->right_.is_null()) {
 | |
|       return {std::move(root), {}};
 | |
|     }
 | |
|     auto res = split(root->right_, key, cmpv);
 | |
|     return {td::make_ref<Hashmap>(root->key_, root->value_, root->left_, std::move(res.first), root->y_),
 | |
|             std::move(res.second)};
 | |
|   }
 | |
| }
 | |
| 
 | |
| long long Hashmap::new_y() {
 | |
|   return td::Random::fast_uint64();
 | |
| }
 | |
| 
 | |
| bool HashmapIterator::unwind(Ref<Hashmap> root) {
 | |
|   if (root.is_null()) {
 | |
|     return false;
 | |
|   }
 | |
|   while (true) {
 | |
|     auto left = root->lr(down_);
 | |
|     if (left.is_null()) {
 | |
|       cur_ = std::move(root);
 | |
|       return true;
 | |
|     }
 | |
|     stack_.push_back(std::move(root));
 | |
|     root = std::move(left);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool HashmapIterator::next() {
 | |
|   if (cur_.not_null()) {
 | |
|     cur_ = cur_->rl(down_);
 | |
|     if (cur_.not_null()) {
 | |
|       while (true) {
 | |
|         auto left = cur_->lr(down_);
 | |
|         if (left.is_null()) {
 | |
|           return true;
 | |
|         }
 | |
|         stack_.push_back(std::move(cur_));
 | |
|         cur_ = std::move(left);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (stack_.empty()) {
 | |
|     return false;
 | |
|   }
 | |
|   cur_ = std::move(stack_.back());
 | |
|   stack_.pop_back();
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| }  // namespace fift
 |