mirror of
https://github.com/danog/ton.git
synced 2024-12-02 09:28:02 +01:00
982 lines
32 KiB
C++
982 lines
32 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/>.
|
|
|
|
Copyright 2017-2019 Telegram Systems LLP
|
|
*/
|
|
#pragma once
|
|
#include <iostream>
|
|
#include "vm/cellslice.h"
|
|
|
|
namespace tlb {
|
|
|
|
using td::Ref;
|
|
using vm::CellSlice;
|
|
|
|
struct PrettyPrinter;
|
|
|
|
class TLB {
|
|
public:
|
|
virtual ~TLB() = default;
|
|
virtual int get_size(const vm::CellSlice& cs) const {
|
|
return -1;
|
|
}
|
|
virtual bool skip(vm::CellSlice& cs) const {
|
|
return cs.skip_ext(get_size(cs));
|
|
}
|
|
virtual bool validate(const vm::CellSlice& cs, bool weak = false) const {
|
|
return cs.have_ext(get_size(cs));
|
|
}
|
|
virtual bool validate_exact(const vm::CellSlice& cs, bool weak = false) const {
|
|
return (int)cs.size_ext() == get_size(cs);
|
|
}
|
|
bool validate_csr(Ref<vm::CellSlice> cs_ref, bool weak = false) const {
|
|
return cs_ref.not_null() && validate_skip_exact(cs_ref.write(), weak);
|
|
}
|
|
Ref<vm::CellSlice> fetch(vm::CellSlice& cs) const {
|
|
return cs.fetch_subslice_ext(get_size(cs));
|
|
}
|
|
Ref<vm::CellSlice> prefetch(const vm::CellSlice& cs) const {
|
|
return cs.prefetch_subslice_ext(get_size(cs));
|
|
}
|
|
virtual Ref<vm::CellSlice> validate_fetch(vm::CellSlice& cs, bool weak = false) const {
|
|
return validate(cs, weak) ? cs.fetch_subslice_ext(get_size(cs)) : Ref<vm::CellSlice>{};
|
|
}
|
|
virtual Ref<vm::CellSlice> validate_prefetch(const vm::CellSlice& cs, bool weak = false) const {
|
|
return validate(cs, weak) ? cs.prefetch_subslice_ext(get_size(cs)) : Ref<vm::CellSlice>{};
|
|
}
|
|
bool fetch_to(vm::CellSlice& cs, Ref<vm::CellSlice>& res) const {
|
|
return (res = fetch(cs)).not_null();
|
|
}
|
|
bool validate_fetch_to(vm::CellSlice& cs, Ref<vm::CellSlice>& res, bool weak = false) const {
|
|
return (res = validate_fetch(cs, weak)).not_null();
|
|
}
|
|
bool store_from(vm::CellBuilder& cb, Ref<vm::CellSlice> field) const {
|
|
return field.not_null() && get_size(*field) == (int)field->size_ext() && cb.append_cellslice_bool(std::move(field));
|
|
}
|
|
bool validate_store_from(vm::CellBuilder& cb, Ref<vm::CellSlice> field, bool weak = false) const {
|
|
if (field.is_null()) {
|
|
return false;
|
|
}
|
|
vm::CellSlice cs{*field};
|
|
return validate_skip(cs, weak) && cs.empty_ext() && cb.append_cellslice_bool(std::move(field));
|
|
}
|
|
virtual bool extract(vm::CellSlice& cs) const {
|
|
return cs.only_ext(get_size(cs));
|
|
}
|
|
virtual bool validate_extract(vm::CellSlice& cs, bool weak = false) const {
|
|
return validate(cs, weak) && extract(cs);
|
|
}
|
|
int get_size_by_skip(const vm::CellSlice& cs) const {
|
|
vm::CellSlice copy{cs};
|
|
return skip(copy) ? copy.subtract_base_ext(cs) : -1;
|
|
}
|
|
virtual bool validate_skip(vm::CellSlice& cs, bool weak = false) const {
|
|
return validate(cs, weak) && skip(cs);
|
|
}
|
|
bool validate_skip_exact(vm::CellSlice& cs, bool weak = false) const {
|
|
return validate_skip(cs, weak) && cs.empty_ext();
|
|
}
|
|
bool validate_by_skip(const vm::CellSlice& cs, bool weak = false) const {
|
|
vm::CellSlice copy{cs};
|
|
return validate_skip(copy, weak);
|
|
}
|
|
bool validate_by_skip_exact(const vm::CellSlice& cs, bool weak = false) const {
|
|
vm::CellSlice copy{cs};
|
|
return validate_skip_exact(copy, weak);
|
|
}
|
|
bool extract_by_skip(vm::CellSlice& cs) const {
|
|
vm::CellSlice copy{cs};
|
|
return skip(copy) && cs.cut_tail(copy);
|
|
}
|
|
bool validate_extract_by_skip(vm::CellSlice& cs, bool weak = false) const {
|
|
vm::CellSlice copy{cs};
|
|
return validate_skip(copy, weak) && cs.cut_tail(copy);
|
|
}
|
|
Ref<vm::CellSlice> validate_fetch_by_skip(vm::CellSlice& cs, bool weak = false) const {
|
|
Ref<vm::CellSlice> copy{true, cs};
|
|
return validate_skip(cs, weak) && copy.unique_write().cut_tail(cs) ? copy : Ref<vm::CellSlice>{};
|
|
}
|
|
Ref<vm::CellSlice> validate_prefetch_by_skip(const vm::CellSlice& cs, bool weak = false) const {
|
|
vm::CellSlice copy{cs};
|
|
return validate_skip(copy, false) ? cs.prefetch_subslice_ext(copy.subtract_base_ext(cs)) : Ref<vm::CellSlice>{};
|
|
}
|
|
virtual bool skip_copy(vm::CellBuilder& cb, vm::CellSlice& cs) const {
|
|
return cb.append_cellslice_bool(fetch(cs));
|
|
}
|
|
virtual bool copy(vm::CellBuilder& cb, const vm::CellSlice& cs) const {
|
|
return cb.append_cellslice_bool(prefetch(cs));
|
|
}
|
|
virtual bool always_special() const {
|
|
return false;
|
|
}
|
|
virtual int get_tag(const vm::CellSlice& cs) const {
|
|
return -1;
|
|
}
|
|
virtual int check_tag(const vm::CellSlice& cs) const {
|
|
return get_tag(cs);
|
|
}
|
|
bool has_valid_tag(const vm::CellSlice& cs) const {
|
|
return check_tag(cs) >= 0;
|
|
}
|
|
virtual long long as_int(const vm::CellSlice& cs) const {
|
|
return -1;
|
|
}
|
|
virtual unsigned long long as_uint(const vm::CellSlice& cs) const {
|
|
return static_cast<unsigned long long>(-1);
|
|
}
|
|
virtual td::RefInt256 as_integer(const vm::CellSlice& cs) const {
|
|
return {};
|
|
}
|
|
virtual td::RefInt256 as_integer_skip(vm::CellSlice& cs) const {
|
|
return {};
|
|
}
|
|
virtual td::RefInt256 as_integer(Ref<vm::CellSlice> cs) const {
|
|
return as_integer(*cs);
|
|
}
|
|
bool as_integer_skip_to(vm::CellSlice& cs, td::RefInt256& res) const {
|
|
return (res = as_integer_skip(cs)).not_null();
|
|
}
|
|
bool as_integer_to(const vm::CellSlice& cs, td::RefInt256& res) const {
|
|
return (res = as_integer(cs)).not_null();
|
|
}
|
|
bool as_integer_to(Ref<vm::CellSlice> cs_ref, td::RefInt256& res) const {
|
|
return (res = as_integer(std::move(cs_ref))).not_null();
|
|
}
|
|
bool validate_ref(Ref<vm::Cell> cell_ref, bool weak = false) const {
|
|
return cell_ref.not_null() && validate_ref_internal(std::move(cell_ref), weak);
|
|
}
|
|
bool force_validate_ref(Ref<vm::Cell> cell_ref) const {
|
|
return cell_ref.not_null() && validate_ref_internal(std::move(cell_ref), false);
|
|
}
|
|
bool validate_skip_ref(vm::CellSlice& cs, bool weak = false) const {
|
|
return validate_ref(cs.fetch_ref(), weak);
|
|
}
|
|
virtual bool null_value(vm::CellBuilder& cb) const {
|
|
return false;
|
|
}
|
|
virtual bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const {
|
|
return false;
|
|
}
|
|
virtual bool store_long(vm::CellBuilder& cb, long long value) const {
|
|
return store_integer_value(cb, td::BigInt256{value});
|
|
}
|
|
virtual bool store_integer_ref(vm::CellBuilder& cb, td::RefInt256 value) const {
|
|
return value.not_null() && store_integer_value(cb, *value);
|
|
}
|
|
virtual bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const {
|
|
td::RefInt256 x = as_integer_skip(cs1), y = as_integer_skip(cs2);
|
|
return x.not_null() && y.not_null() && store_integer_ref(cb, x += std::move(y));
|
|
}
|
|
// result: -1 = error, 0 = ok (zero), 1 = ok
|
|
virtual int sub_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const {
|
|
td::RefInt256 x = as_integer_skip(cs1), y = as_integer_skip(cs2);
|
|
return x.not_null() && y.not_null() && store_integer_ref(cb, x -= std::move(y)) ? (td::sgn(x) ? 1 : 0) : -1;
|
|
}
|
|
template <typename... Args>
|
|
bool unpack(Ref<vm::CellSlice> cs_ref, Args&... args) const {
|
|
return cs_ref.not_null() && unpack(cs_ref.write(), args...) && cs_ref->empty_ext();
|
|
}
|
|
virtual std::ostream& print_type(std::ostream& os) const {
|
|
return os << "<unknown-TLB-type>";
|
|
}
|
|
virtual bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const;
|
|
virtual bool print(PrettyPrinter& pp, const vm::CellSlice& cs) const {
|
|
vm::CellSlice cs_copy{cs};
|
|
return print_skip(pp, cs_copy);
|
|
}
|
|
bool print_special(PrettyPrinter& pp, vm::CellSlice& cs) const;
|
|
bool print_ref(PrettyPrinter& pp, Ref<vm::Cell> cell_ref) const;
|
|
bool print(PrettyPrinter& pp, Ref<vm::CellSlice> cs_ref) const {
|
|
return print(pp, *cs_ref);
|
|
}
|
|
bool print_skip(std::ostream& os, vm::CellSlice& cs, int indent = 0) const;
|
|
bool print(std::ostream& os, const vm::CellSlice& cs, int indent = 0) const;
|
|
bool print(std::ostream& os, Ref<vm::CellSlice> cs_ref, int indent = 0) const {
|
|
return print(os, *cs_ref, indent);
|
|
}
|
|
bool print_ref(std::ostream& os, Ref<vm::Cell> cell_ref, int indent = 0) const;
|
|
std::string as_string_skip(vm::CellSlice& cs, int indent = 0) const;
|
|
std::string as_string(const vm::CellSlice& cs, int indent = 0) const;
|
|
std::string as_string(Ref<vm::CellSlice> cs_ref, int indent = 0) const {
|
|
return cs_ref.not_null() ? as_string(*cs_ref, indent) : "<null>";
|
|
}
|
|
std::string as_string_ref(Ref<vm::Cell> cell_ref, int indent = 0) const;
|
|
|
|
protected:
|
|
bool validate_ref_internal(Ref<vm::Cell> cell_ref, bool weak = false) const;
|
|
};
|
|
|
|
static inline std::ostream& operator<<(std::ostream& os, const TLB& type) {
|
|
return type.print_type(os);
|
|
}
|
|
|
|
struct TLB_Complex : TLB {
|
|
bool skip(vm::CellSlice& cs) const override {
|
|
return validate_skip(cs);
|
|
}
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override = 0;
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return get_size_by_skip(cs);
|
|
}
|
|
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
|
|
return validate_by_skip(cs, weak);
|
|
}
|
|
bool validate_exact(const vm::CellSlice& cs, bool weak = false) const override {
|
|
return validate_by_skip_exact(cs, weak);
|
|
}
|
|
bool extract(vm::CellSlice& cs) const override {
|
|
return extract_by_skip(cs);
|
|
}
|
|
bool validate_extract(vm::CellSlice& cs, bool weak = false) const override {
|
|
return validate_extract_by_skip(cs, weak);
|
|
}
|
|
Ref<vm::CellSlice> validate_fetch(vm::CellSlice& cs, bool weak = false) const override {
|
|
return validate_fetch_by_skip(cs, weak);
|
|
}
|
|
Ref<vm::CellSlice> validate_prefetch(const vm::CellSlice& cs, bool weak = false) const override {
|
|
return validate_prefetch_by_skip(cs, weak);
|
|
}
|
|
td::RefInt256 as_integer(const vm::CellSlice& cs) const override {
|
|
vm::CellSlice copy{cs};
|
|
auto res = as_integer_skip(copy);
|
|
return res.not_null() && copy.empty_ext() ? std::move(res) : td::RefInt256{};
|
|
}
|
|
td::RefInt256 as_integer(Ref<vm::CellSlice> cs) const override {
|
|
auto res = as_integer_skip(cs.write());
|
|
return res.not_null() && cs->empty_ext() ? std::move(res) : td::RefInt256{};
|
|
}
|
|
};
|
|
|
|
static inline bool add_chk(int x, int y, int z) {
|
|
return x + y == z && z >= 0;
|
|
}
|
|
|
|
static inline bool add_r1(int& x, int y, int z) {
|
|
return z >= y && (x = z - y) >= 0;
|
|
}
|
|
|
|
static inline bool add_r3(int& x, int y, int& z) {
|
|
return (z = (x + y)) >= 0;
|
|
}
|
|
|
|
static inline bool mul_chk(int x, int y, int z) {
|
|
return (long long)x * y == z;
|
|
}
|
|
|
|
static inline bool mul_r1(int& x, int y, int z) {
|
|
return y && !(z % y) && (x = z / y) >= 0;
|
|
}
|
|
|
|
static inline bool mul_r3(int x, int y, int& z) {
|
|
unsigned long long t = (unsigned long long)x * y;
|
|
if (t <= 0x7fffffff) {
|
|
z = (int)t;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline int mul_bound(int x, int y) {
|
|
unsigned long long t = (unsigned long long)x * y;
|
|
return t <= 0x7fffffff ? (int)t : 0x7fffffff;
|
|
}
|
|
|
|
// templatized unpack functions
|
|
template <typename R, typename... Args>
|
|
bool unpack(vm::CellSlice& cs, R& rec, Args&... args) {
|
|
return (typename R::type_class{}).unpack(cs, rec, args...);
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
bool unpack_exact(vm::CellSlice& cs, R& rec, Args&... args) {
|
|
return (typename R::type_class{}).unpack(cs, rec, args...) && cs.empty_ext();
|
|
}
|
|
|
|
template <typename T, typename R, typename... Args>
|
|
bool type_unpack(vm::CellSlice& cs, const T& type, R& rec, Args&... args) {
|
|
return type.unpack(cs, rec, args...);
|
|
}
|
|
|
|
template <typename T, typename R, typename... Args>
|
|
bool type_unpack_exact(vm::CellSlice& cs, const T& type, R& rec, Args&... args) {
|
|
return type.unpack(cs, rec, args...) && cs.empty_ext();
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
bool csr_unpack(Ref<vm::CellSlice> csr, R& rec, Args&... args) {
|
|
return (typename R::type_class{}).unpack(csr.write(), rec, args...) && csr->empty_ext();
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
bool csr_unpack_safe(Ref<vm::CellSlice> csr, R& rec, Args&... args) {
|
|
return csr.not_null() && (typename R::type_class{}).unpack(csr.write(), rec, args...) && csr->empty_ext();
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
bool unpack_cell(Ref<vm::Cell> cell, R& rec, Args&... args) {
|
|
vm::CellSlice cs = vm::load_cell_slice(std::move(cell));
|
|
return cs.is_valid() && (typename R::type_class{}).unpack(cs, rec, args...) && cs.empty_ext();
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
bool unpack_cell_inexact(Ref<vm::Cell> cell, R& rec, Args&... args) {
|
|
vm::CellSlice cs = vm::load_cell_slice(std::move(cell));
|
|
return cs.is_valid() && (typename R::type_class{}).unpack(cs, rec, args...);
|
|
}
|
|
|
|
template <typename T, typename R, typename... Args>
|
|
bool type_unpack_cell(Ref<vm::Cell> cell, const T& type, R& rec, Args&... args) {
|
|
vm::CellSlice cs = vm::load_cell_slice(std::move(cell));
|
|
return cs.is_valid() && type.unpack(cs, rec, args...) && cs.empty_ext();
|
|
}
|
|
|
|
template <typename T, typename R, typename... Args>
|
|
bool csr_type_unpack(Ref<vm::CellSlice> csr, const T& type, R& rec, Args&... args) {
|
|
return type.unpack(csr.write(), rec, args...) && csr->empty_ext();
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
bool csr_unpack_inexact(Ref<vm::CellSlice> csr, R& rec, Args&... args) {
|
|
return (typename R::type_class{}).unpack(csr.write(), rec, args...);
|
|
}
|
|
|
|
template <typename T, typename R, typename... Args>
|
|
bool csr_type_unpack_inexact(Ref<vm::CellSlice> csr, const T& type, R& rec, Args&... args) {
|
|
return type.unpack(csr.write(), rec, args...);
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
bool csr_unpack_skip(Ref<vm::CellSlice>& csr, R& rec, Args&... args) {
|
|
return (typename R::type_class{}).unpack(csr.write(), rec, args...);
|
|
}
|
|
|
|
template <typename T, typename R, typename... Args>
|
|
bool csr_type_unpack_skip(Ref<vm::CellSlice>& csr, const T& type, R& rec, Args&... args) {
|
|
return type.unpack(csr.write(), rec, args...);
|
|
}
|
|
|
|
// templatized pack functions
|
|
template <typename R, typename... Args>
|
|
bool pack(vm::CellBuilder& cb, const R& rec, Args&... args) {
|
|
return (typename R::type_class{}).pack(cb, rec, args...);
|
|
}
|
|
|
|
template <typename T, typename R, typename... Args>
|
|
bool type_pack(vm::CellBuilder& cb, const T& type, const R& rec, Args&... args) {
|
|
return type.pack(cb, rec, args...);
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
bool pack_cell(Ref<vm::Cell>& cell, const R& rec, Args&... args) {
|
|
vm::CellBuilder cb;
|
|
return pack(cb, rec, args...) && cb.finalize_to(cell);
|
|
}
|
|
|
|
template <typename T, typename R, typename... Args>
|
|
bool type_pack_cell(Ref<vm::Cell>& cell, const T& type, const R& rec, Args&... args) {
|
|
vm::CellBuilder cb;
|
|
return type.pack(cb, rec, args...) && cb.finalize_to(cell);
|
|
}
|
|
|
|
template <typename R, typename... Args>
|
|
bool csr_pack(Ref<vm::CellSlice>& csr, const R& rec, Args&... args) {
|
|
vm::CellBuilder cb;
|
|
Ref<vm::Cell> cell;
|
|
return pack(cb, rec, args...) && cb.finalize_to(cell) && (csr = vm::load_cell_slice_ref(std::move(cell))).not_null();
|
|
}
|
|
|
|
template <typename T, typename R, typename... Args>
|
|
bool csr_type_pack(Ref<vm::CellSlice>& csr, const T& type, const R& rec, Args&... args) {
|
|
vm::CellBuilder cb;
|
|
Ref<vm::Cell> cell;
|
|
return type.pack(cb, rec, args...) && cb.finalize_to(cell) &&
|
|
(csr = vm::load_cell_slice_ref(std::move(cell))).not_null();
|
|
}
|
|
|
|
// templatized store_from function
|
|
|
|
template <typename T, typename... Args>
|
|
bool store_from(vm::CellBuilder& cb, const T& tlb_type, Ref<vm::CellSlice> field, Args&... args) {
|
|
if (field.is_null()) {
|
|
return false;
|
|
}
|
|
vm::CellSlice cs{*field};
|
|
return tlb_type.skip(cs, args...) && cs.empty_ext() && cb.append_cellslice_bool(std::move(field));
|
|
}
|
|
|
|
} // namespace tlb
|
|
|
|
namespace tlb {
|
|
|
|
struct PrettyPrinter {
|
|
std::ostream& os;
|
|
int indent;
|
|
int level;
|
|
bool failed;
|
|
bool nl_used;
|
|
int mode;
|
|
PrettyPrinter(std::ostream& _os, int _indent = 0, int _mode = 1)
|
|
: os(_os), indent(_indent), level(0), failed(false), nl_used(false), mode(_mode) {
|
|
}
|
|
~PrettyPrinter();
|
|
bool ok() const {
|
|
return !failed && !level;
|
|
}
|
|
bool fail_unless(bool res) {
|
|
if (!res) {
|
|
failed = true;
|
|
}
|
|
return res;
|
|
}
|
|
bool fail(std::string msg);
|
|
bool nl(int delta = 0);
|
|
bool raw_nl(int delta = 0);
|
|
bool mkindent(int delta = 0);
|
|
bool mode_nl();
|
|
bool open(std::string msg = "");
|
|
bool close();
|
|
bool close(std::string msg);
|
|
bool field(std::string name);
|
|
bool field();
|
|
bool field_int(long long value);
|
|
bool field_int(long long value, std::string name);
|
|
bool field_uint(unsigned long long value);
|
|
bool field_uint(unsigned long long value, std::string name);
|
|
bool out(std::string str) {
|
|
os << str;
|
|
return true;
|
|
}
|
|
bool out_int(long long value) {
|
|
os << value;
|
|
return true;
|
|
}
|
|
bool out_uint(unsigned long long value) {
|
|
os << value;
|
|
return true;
|
|
}
|
|
bool out_integer(td::RefInt256 value) {
|
|
if (value.not_null()) {
|
|
os << std::move(value);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
bool cons(std::string str) {
|
|
return out(str);
|
|
}
|
|
bool fetch_bits_field(vm::CellSlice& cs, int n);
|
|
bool fetch_bits_field(vm::CellSlice& cs, int n, std::string name);
|
|
bool fetch_int_field(vm::CellSlice& cs, int n);
|
|
bool fetch_int_field(vm::CellSlice& cs, int n, std::string name);
|
|
bool fetch_uint_field(vm::CellSlice& cs, int n);
|
|
bool fetch_uint_field(vm::CellSlice& cs, int n, std::string name);
|
|
bool fetch_int256_field(vm::CellSlice& cs, int n);
|
|
bool fetch_int256_field(vm::CellSlice& cs, int n, std::string name);
|
|
bool fetch_uint256_field(vm::CellSlice& cs, int n);
|
|
bool fetch_uint256_field(vm::CellSlice& cs, int n, std::string name);
|
|
template <typename T>
|
|
PrettyPrinter& operator<<(const T& value) {
|
|
os << value;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
} // namespace tlb
|
|
|
|
namespace tlb {
|
|
|
|
struct False final : TLB {
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return -1;
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "False";
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
extern const False t_False;
|
|
|
|
struct True final : TLB {
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return 0;
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "True";
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override {
|
|
return pp.out("true");
|
|
}
|
|
};
|
|
|
|
extern const True t_True;
|
|
|
|
struct Unit final : TLB {
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return 0;
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "Unit";
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override {
|
|
return pp.out("()");
|
|
}
|
|
};
|
|
|
|
struct FwdT final : TLB {
|
|
const TLB& X;
|
|
FwdT(const TLB& _X) : X(_X) {
|
|
}
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return X.get_size(cs);
|
|
}
|
|
bool skip(vm::CellSlice& cs) const override {
|
|
return X.skip(cs);
|
|
}
|
|
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
|
|
return X.validate(cs, weak);
|
|
}
|
|
Ref<vm::CellSlice> validate_fetch(vm::CellSlice& cs, bool weak = false) const override {
|
|
return X.validate_fetch(cs, weak);
|
|
}
|
|
Ref<vm::CellSlice> validate_prefetch(const vm::CellSlice& cs, bool weak = false) const override {
|
|
return X.validate_prefetch(cs, weak);
|
|
}
|
|
bool extract(vm::CellSlice& cs) const override {
|
|
return X.extract(cs);
|
|
}
|
|
bool validate_extract(vm::CellSlice& cs, bool weak = false) const override {
|
|
return X.validate_extract(cs, weak);
|
|
}
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
|
|
return X.validate_skip(cs, weak);
|
|
}
|
|
bool skip_copy(vm::CellBuilder& cb, vm::CellSlice& cs) const override {
|
|
return X.skip_copy(cb, cs);
|
|
}
|
|
bool copy(vm::CellBuilder& cb, const vm::CellSlice& cs) const override {
|
|
return X.copy(cb, cs);
|
|
}
|
|
int get_tag(const vm::CellSlice& cs) const override {
|
|
return X.get_tag(cs);
|
|
}
|
|
long long as_int(const vm::CellSlice& cs) const override {
|
|
return X.as_int(cs);
|
|
}
|
|
unsigned long long as_uint(const vm::CellSlice& cs) const override {
|
|
return X.as_uint(cs);
|
|
}
|
|
td::RefInt256 as_integer(const vm::CellSlice& cs) const override {
|
|
return X.as_integer(cs);
|
|
}
|
|
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override {
|
|
return X.as_integer_skip(cs);
|
|
}
|
|
td::RefInt256 as_integer(Ref<vm::CellSlice> cs) const override {
|
|
return X.as_integer(std::move(cs));
|
|
}
|
|
bool null_value(vm::CellBuilder& cb) const override {
|
|
return X.null_value(cb);
|
|
}
|
|
bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override {
|
|
return X.store_integer_value(cb, value);
|
|
}
|
|
bool store_integer_ref(vm::CellBuilder& cb, td::RefInt256 value) const override {
|
|
return X.store_integer_ref(cb, std::move(value));
|
|
}
|
|
bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override {
|
|
return X.add_values(cb, cs1, cs2);
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return X.print_type(os);
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override {
|
|
return X.print_skip(pp, cs);
|
|
}
|
|
};
|
|
|
|
extern const Unit t_Unit;
|
|
|
|
struct Bool final : TLB {
|
|
enum { bool_false = 0, bool_true = 1 };
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return 1;
|
|
}
|
|
int get_tag(const vm::CellSlice& cs) const override {
|
|
return (int)cs.prefetch_ulong(1);
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "Bool";
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
extern const Bool t_Bool;
|
|
|
|
struct NatWidth final : TLB {
|
|
int n;
|
|
NatWidth(int _n) : n(_n) {
|
|
}
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return n <= 32 ? n : -1;
|
|
}
|
|
td::RefInt256 as_integer(const vm::CellSlice& cs) const override {
|
|
return cs.prefetch_int256(n, false);
|
|
}
|
|
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override {
|
|
return cs.fetch_int256(n, false);
|
|
}
|
|
unsigned long long as_uint(const vm::CellSlice& cs) const override {
|
|
return n <= 32 ? cs.prefetch_ulong(n) : -1;
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "(## " << n << ')';
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
extern const NatWidth t_Nat;
|
|
|
|
struct NatLess final : TLB {
|
|
int n, w;
|
|
NatLess(int _n) : n(_n - 1), w(32 - td::count_leading_zeroes32(_n - 1)) {
|
|
}
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return n >= 0 ? w : -1;
|
|
}
|
|
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
|
|
return n >= 0 && (unsigned)cs.prefetch_ulong(w) <= (unsigned)n;
|
|
}
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
|
|
return n >= 0 && (unsigned)cs.fetch_ulong(w) <= (unsigned)n;
|
|
}
|
|
unsigned long long as_uint(const vm::CellSlice& cs) const override {
|
|
unsigned long long r = cs.prefetch_ulong(w);
|
|
return n >= 0 && (unsigned)r <= (unsigned)n ? r : std::numeric_limits<td::uint64>::max();
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "(#< " << n << ')';
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
struct NatLeq final : TLB {
|
|
int n, w;
|
|
NatLeq(int _n) : n(_n), w(32 - td::count_leading_zeroes32(_n)) {
|
|
}
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return n >= 0 ? w : -1;
|
|
}
|
|
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
|
|
return n >= 0 && (unsigned)cs.prefetch_ulong(w) <= (unsigned)n;
|
|
}
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
|
|
return n >= 0 && (unsigned)cs.fetch_ulong(w) <= (unsigned)n;
|
|
}
|
|
unsigned long long as_uint(const vm::CellSlice& cs) const override {
|
|
unsigned long long r = cs.prefetch_ulong(w);
|
|
return n >= 0 && (unsigned)r <= (unsigned)n ? r : std::numeric_limits<td::uint64>::max();
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "(#<= " << n << ')';
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
struct TupleT final : TLB_Complex {
|
|
int n;
|
|
const TLB& X;
|
|
TupleT(int _n, const TLB& _X) : n(_n), X(_X) {
|
|
}
|
|
bool skip(vm::CellSlice& cs) const override;
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
|
|
int get_tag(const vm::CellSlice& cs) const override {
|
|
return 0;
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
struct CondT final : TLB_Complex {
|
|
int n;
|
|
const TLB& X;
|
|
CondT(int _n, const TLB& _X) : n(_n), X(_X) {
|
|
}
|
|
bool skip(vm::CellSlice& cs) const override {
|
|
return !n || X.skip(cs);
|
|
}
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
|
|
return !n || (n > 0 && X.validate_skip(cs, weak));
|
|
}
|
|
int get_tag(const vm::CellSlice& cs) const override {
|
|
return 0;
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "(CondT " << n << ' ' << X << ')';
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
template <typename T>
|
|
struct Cond final : TLB_Complex {
|
|
int n;
|
|
T field_type;
|
|
template <typename... Args>
|
|
Cond(int _n, Args... args) : n(_n), field_type(args...) {
|
|
}
|
|
bool skip(vm::CellSlice& cs) const override {
|
|
return !n || field_type.skip(cs);
|
|
}
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
|
|
return !n || (n > 0 && field_type.validate_skip(cs, weak));
|
|
}
|
|
int get_tag(const vm::CellSlice& cs) const override {
|
|
return 0;
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "(Cond " << n << ' ' << field_type << ')';
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override {
|
|
return (n > 0 ? field_type.print_skip(pp, cs) : (!n && pp.out("()")));
|
|
}
|
|
};
|
|
|
|
struct Int final : TLB {
|
|
int n;
|
|
Int(int _n) : n(_n) {
|
|
}
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return n;
|
|
}
|
|
td::RefInt256 as_integer(const vm::CellSlice& cs) const override {
|
|
return cs.prefetch_int256(n, true);
|
|
}
|
|
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override {
|
|
return cs.fetch_int256(n, true);
|
|
}
|
|
long long as_int(const vm::CellSlice& cs) const override {
|
|
return n <= 64 ? cs.prefetch_long(n) : (1ULL << 63);
|
|
}
|
|
bool null_value(vm::CellBuilder& cb) const override {
|
|
return cb.store_zeroes_bool(n);
|
|
}
|
|
bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override {
|
|
return cb.store_int256_bool(value, n, true);
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "int" << n;
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
extern const Int t_int8, t_int16, t_int24, t_int32, t_int64, t_int128, t_int256, t_int257;
|
|
|
|
struct UInt final : TLB {
|
|
int n;
|
|
UInt(int _n) : n(_n) {
|
|
}
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return n;
|
|
}
|
|
td::RefInt256 as_integer(const vm::CellSlice& cs) const override {
|
|
return cs.prefetch_int256(n, false);
|
|
}
|
|
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override {
|
|
return cs.fetch_int256(n, false);
|
|
}
|
|
unsigned long long as_uint(const vm::CellSlice& cs) const override {
|
|
return n <= 64 ? cs.prefetch_ulong(n) : -1;
|
|
}
|
|
bool null_value(vm::CellBuilder& cb) const override {
|
|
return cb.store_zeroes_bool(n);
|
|
}
|
|
bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override {
|
|
return cb.store_int256_bool(value, n, false);
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "uint" << n;
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
extern const UInt t_uint8, t_uint16, t_uint24, t_uint32, t_uint64, t_uint128, t_uint256;
|
|
|
|
struct Bits final : TLB {
|
|
int n;
|
|
Bits(int _n) : n(_n) {
|
|
}
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return n;
|
|
}
|
|
bool null_value(vm::CellBuilder& cb) const override {
|
|
return cb.store_zeroes_bool(n);
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "bits" << n;
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
template <class T>
|
|
struct Maybe : TLB_Complex {
|
|
T field_type;
|
|
template <typename... Args>
|
|
Maybe(Args... args) : field_type(args...) {
|
|
}
|
|
bool skip(vm::CellSlice& cs) const override;
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
|
|
int get_tag(const vm::CellSlice& cs) const override {
|
|
return cs.have(1) ? (int)cs.prefetch_ulong(1) : -1;
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "(Maybe " << field_type << ')';
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
template <class T>
|
|
bool Maybe<T>::skip(vm::CellSlice& cs) const {
|
|
int t = get_tag(cs);
|
|
if (t > 0) {
|
|
return cs.advance(1) && field_type.skip(cs);
|
|
} else if (!t) {
|
|
return cs.advance(1);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
bool Maybe<T>::validate_skip(vm::CellSlice& cs, bool weak) const {
|
|
int t = get_tag(cs);
|
|
if (t > 0) {
|
|
return cs.advance(1) && field_type.validate_skip(cs, weak);
|
|
} else if (!t) {
|
|
return cs.advance(1);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
bool Maybe<T>::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
|
|
if (!get_tag(cs)) {
|
|
return cs.advance(1) && pp.out("nothing");
|
|
} else {
|
|
return cs.advance(1) && pp.open("just ") && field_type.print_skip(pp, cs) && pp.close();
|
|
}
|
|
}
|
|
|
|
struct RefAnything final : TLB {
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return 0x10000;
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "^Cell";
|
|
}
|
|
};
|
|
|
|
extern const RefAnything t_RefCell;
|
|
|
|
struct Anything final : TLB {
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return cs.size_ext();
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "Any";
|
|
}
|
|
};
|
|
|
|
extern const Anything t_Anything;
|
|
|
|
template <class T>
|
|
struct RefTo final : TLB {
|
|
T ref_type;
|
|
template <typename... Args>
|
|
RefTo(Args... args) : ref_type(args...) {
|
|
}
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return 0x10000;
|
|
}
|
|
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
|
|
return cs.size_refs() ? ref_type.validate_ref(cs.prefetch_ref(), weak) : false;
|
|
}
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
|
|
return ref_type.validate_skip_ref(cs, weak);
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << '^' << ref_type;
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override {
|
|
return pp.out("^") && ref_type.print_ref(pp, cs.fetch_ref());
|
|
}
|
|
};
|
|
|
|
struct RefT final : TLB {
|
|
const TLB& X;
|
|
RefT(const TLB& _X) : X(_X) {
|
|
}
|
|
int get_size(const vm::CellSlice& cs) const override {
|
|
return 0x10000;
|
|
}
|
|
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
|
|
return X.validate_ref(cs.prefetch_ref(), weak);
|
|
}
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
|
|
return X.validate_skip_ref(cs, weak);
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << '^' << X;
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override {
|
|
return pp.out("^") && X.print_ref(pp, cs.fetch_ref());
|
|
}
|
|
};
|
|
|
|
template <class T1, class T2>
|
|
struct Either final : TLB_Complex {
|
|
T1 left_type;
|
|
T2 right_type;
|
|
bool skip(vm::CellSlice& cs) const override {
|
|
return cs.have(1) ? (cs.fetch_ulong(1) ? right_type.skip(cs) : left_type.skip(cs)) : false;
|
|
}
|
|
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
|
|
return cs.have(1) ? (cs.fetch_ulong(1) ? right_type.validate_skip(cs, weak) : left_type.validate_skip(cs, weak))
|
|
: false;
|
|
}
|
|
int get_tag(const vm::CellSlice& cs) const override {
|
|
return (int)cs.prefetch_ulong(1);
|
|
}
|
|
std::ostream& print_type(std::ostream& os) const override {
|
|
return os << "(Either " << left_type << ' ' << right_type << ')';
|
|
}
|
|
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
|
|
};
|
|
|
|
template <class T1, class T2>
|
|
bool Either<T1, T2>::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
|
|
if (!get_tag(cs)) {
|
|
return cs.advance(1) && pp.open("left ") && left_type.print_skip(pp, cs) && pp.close();
|
|
} else {
|
|
return cs.advance(1) && pp.open("right ") && right_type.print_skip(pp, cs) && pp.close();
|
|
}
|
|
}
|
|
|
|
} // namespace tlb
|