1
0
mirror of https://github.com/danog/ton.git synced 2024-11-26 20:14:55 +01:00
ton/crypto/vm/dictops.cpp
2019-09-07 14:33:36 +04:00

812 lines
30 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
*/
#include <functional>
#include "vm/log.h"
#include "vm/opctable.h"
#include "vm/stack.hpp"
#include "vm/continuation.h"
#include "vm/excno.hpp"
#include "common/bigint.hpp"
#include "common/refint.h"
#include "vm/dictops.h"
#include "vm/dict.h"
namespace vm {
template <typename T>
void push_dict(Stack& stack, T&& dict) {
stack.push_maybe_cell(std::move(dict).extract_root_cell());
}
template <typename T>
void push_dict(Stack& stack, const T& dict) {
stack.push_maybe_cell(dict.get_root_cell());
}
int exec_dict_empty(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICTEMPTY";
auto dict = stack.pop_cellslice();
if (!dict->have(1)) {
throw VmError{Excno::cell_und};
}
stack.push_smallint(~dict->prefetch_long(1));
return 0;
}
int exec_store_dict(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute STDICT";
stack.check_underflow(2);
auto cb = stack.pop_builder();
auto d = stack.pop_maybe_cell();
if (!cb.write().store_maybe_ref(std::move(d))) {
throw VmError{Excno::cell_ov};
}
stack.push_builder(std::move(cb));
return 0;
}
int dict_nonempty(const CellSlice& dict) {
if (!dict.have(1)) {
return -1;
}
int res = (int)dict.prefetch_ulong(1);
return dict.have_refs(res) ? res : -1;
}
int dict_nonempty_chk(const CellSlice& dict) {
int res = dict_nonempty(dict);
if (res < 0) {
throw VmError{Excno::cell_und};
}
return res;
}
int exec_skip_dict(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute SKIPDICT\n";
auto dict = stack.pop_cellslice();
int res = dict_nonempty_chk(*dict);
dict.write().advance_ext(1, res);
stack.push_cellslice(std::move(dict));
return 0;
}
int exec_load_optref(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute LDOPTREF\n";
auto dict = stack.pop_cellslice();
int res = dict_nonempty_chk(*dict);
dict.write().advance(1);
if (res) {
auto cell = dict.write().fetch_ref();
stack.push_cellslice(std::move(dict));
stack.push_cell(std::move(cell));
} else {
stack.push_cellslice(std::move(dict));
}
stack.push_smallint(-res);
return 0;
}
int exec_preload_optref(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute PLDOPTREF\n";
auto dict = stack.pop_cellslice();
int res = dict_nonempty_chk(*dict);
if (res) {
stack.push_cell(dict->prefetch_ref());
}
stack.push_smallint(-res);
return 0;
}
int exec_load_dict_slice(VmState* st, unsigned args) {
bool preload = args & 1, quiet = args & 2;
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << (preload ? "P" : "") << "LDDICTS" << (quiet ? "Q\n" : "\n");
auto cs = stack.pop_cellslice();
int res = dict_nonempty(*cs);
if (res < 0) {
if (!quiet) {
throw VmError{Excno::cell_und};
}
if (!preload) {
stack.push_cellslice(std::move(cs));
}
} else {
if (preload) {
stack.push_cellslice(cs->prefetch_subslice(1, res));
} else {
stack.push_cellslice(cs.write().fetch_subslice(1, res));
stack.push_cellslice(std::move(cs));
}
}
if (quiet) {
stack.push_bool(res >= 0);
}
return 0;
}
int exec_load_dict(VmState* st, unsigned args) {
bool preload = args & 1, quiet = args & 2;
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << (preload ? "P" : "") << "LDDICT" << (quiet ? "Q\n" : "\n");
auto cs = stack.pop_cellslice();
int res = dict_nonempty(*cs);
if (res < 0) {
if (!quiet) {
throw VmError{Excno::cell_und};
}
if (!preload) {
stack.push_cellslice(std::move(cs));
}
} else {
stack.push_maybe_cell(res ? cs->prefetch_ref() : Ref<Cell>{});
if (!preload) {
cs.write().advance_ext(1, res);
stack.push_cellslice(std::move(cs));
}
}
if (quiet) {
stack.push_bool(res >= 0);
}
return 0;
}
std::string dump_dictop(unsigned args, const char* name) {
std::ostringstream os{"DICT"};
if (args & 4) {
os << (args & 2 ? 'U' : 'I');
}
os << name;
if (args & 1) {
os << "REF";
}
return os.str();
}
std::string dump_dictop2(unsigned args, const char* name) {
std::ostringstream os{"DICT"};
if (args & 2) {
os << (args & 1 ? 'U' : 'I');
}
os << name;
return os.str();
}
std::string dump_subdictop2(unsigned args, const char* name) {
std::ostringstream os{"SUBDICT"};
if (args & 2) {
os << (args & 1 ? 'U' : 'I');
}
os << name;
return os.str();
}
int exec_dict_get(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 4 ? (args & 2 ? "U" : "I") : "") << "GET" << (args & 1 ? "REF" : "");
stack.check_underflow(3);
int n = stack.pop_smallint_range(Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
BitSlice key;
unsigned char buffer[Dictionary::max_key_bytes];
if (args & 4) {
key = dict.integer_key(stack.pop_int(), n, !(args & 2), buffer, true);
if (!key.is_valid()) {
stack.push_smallint(0);
return 0;
}
} else {
key = stack.pop_cellslice()->prefetch_bits(n);
}
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
if (args & 1) {
auto value = dict.lookup_ref(key);
if (value.not_null()) {
stack.push_cell(std::move(value));
stack.push_smallint(-1);
} else {
stack.push_smallint(0);
}
} else {
auto value = dict.lookup(key);
if (value.not_null()) {
stack.push_cellslice(std::move(value));
stack.push_smallint(-1);
} else {
stack.push_smallint(0);
}
}
return 0;
}
int exec_dict_get_optref(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 2 ? (args & 1 ? "U" : "I") : "") << "GETOPTREF";
stack.check_underflow(3);
int n = stack.pop_smallint_range(Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
BitSlice key;
unsigned char buffer[Dictionary::max_key_bytes];
if (args & 2) {
key = dict.integer_key(stack.pop_int(), n, !(args & 1), buffer, true);
if (!key.is_valid()) {
stack.push_null();
return 0;
}
} else {
key = stack.pop_cellslice()->prefetch_bits(n);
}
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
stack.push_maybe_cell(dict.lookup_ref(key));
return 0;
}
int exec_dict_set(VmState* st, unsigned args, Dictionary::SetMode mode, const char* name, bool bld = false) {
args <<= (bld ? 1 : 0);
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 4 ? (args & 2 ? "U" : "I") : "") << name
<< (args & 1 ? "REF" : (bld ? "B" : ""));
stack.check_underflow(4);
int n = stack.pop_smallint_range(Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
BitSlice key;
unsigned char buffer[Dictionary::max_key_bytes];
if (args & 4) {
key = dict.integer_key(stack.pop_int(), n, !(args & 2), buffer);
} else {
key = stack.pop_cellslice()->prefetch_bits(n);
}
bool res;
if (bld) {
auto new_value = stack.pop_builder();
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
res = dict.set_builder(key, std::move(new_value), mode);
} else if (!(args & 1)) {
auto new_value = stack.pop_cellslice();
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
res = dict.set(key, std::move(new_value), mode);
} else {
auto new_value_ref = stack.pop_cell();
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
res = dict.set_ref(key, std::move(new_value_ref), mode);
}
push_dict(stack, std::move(dict));
if (mode == Dictionary::SetMode::Set) {
st->ensure_throw(res);
} else {
stack.push_bool(res);
}
return 0;
}
int exec_dict_setget(VmState* st, unsigned args, Dictionary::SetMode mode, const char* name, bool bld = false) {
args <<= (bld ? 1 : 0);
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 4 ? (args & 2 ? "U" : "I") : "") << name
<< (args & 1 ? "REF\n" : (bld ? "B\n" : "\n"));
stack.check_underflow(4);
int n = stack.pop_smallint_range(Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
BitSlice key;
unsigned char buffer[Dictionary::max_key_bytes];
if (args & 4) {
key = dict.integer_key(stack.pop_int(), n, !(args & 2), buffer);
} else {
key = stack.pop_cellslice()->prefetch_bits(n);
}
bool ok_f = (mode != Dictionary::SetMode::Add);
if (bld) {
auto new_value = stack.pop_builder();
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
auto res = dict.lookup_set_builder(key, std::move(new_value), mode);
push_dict(stack, std::move(dict));
if (res.not_null()) {
stack.push_cellslice(std::move(res));
stack.push_bool(ok_f);
} else {
stack.push_bool(!ok_f);
}
} else if (!(args & 1)) {
auto new_value = stack.pop_cellslice();
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
auto res = dict.lookup_set(key, std::move(new_value), mode);
push_dict(stack, std::move(dict));
if (res.not_null()) {
stack.push_cellslice(std::move(res));
stack.push_bool(ok_f);
} else {
stack.push_bool(!ok_f);
}
} else {
auto new_value_ref = stack.pop_cell();
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
auto res = dict.lookup_set_ref(key, std::move(new_value_ref), mode);
push_dict(stack, std::move(dict));
if (res.not_null()) {
stack.push_cell(std::move(res));
stack.push_bool(ok_f);
} else {
stack.push_bool(!ok_f);
}
}
return 0;
}
int exec_dict_delete(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 2 ? (args & 1 ? "U" : "I") : "") << "DEL\n";
stack.check_underflow(3);
int n = stack.pop_smallint_range(Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
BitSlice key;
unsigned char buffer[Dictionary::max_key_bytes];
if (args & 2) {
key = dict.integer_key(stack.pop_int(), n, !(args & 1), buffer);
if (!key.is_valid()) {
push_dict(stack, std::move(dict));
stack.push_smallint(0);
return 0;
}
} else {
key = stack.pop_cellslice()->prefetch_bits(n);
}
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
bool res = dict.lookup_delete(key).not_null();
push_dict(stack, std::move(dict));
stack.push_bool(res);
return 0;
}
int exec_dict_deleteget(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 4 ? (args & 2 ? "U" : "I") : "") << "DELGET" << (args & 1 ? "REF\n" : "\n");
stack.check_underflow(3);
int n = stack.pop_smallint_range(Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
BitSlice key;
unsigned char buffer[Dictionary::max_key_bytes];
if (args & 4) {
key = dict.integer_key(stack.pop_int(), n, !(args & 2), buffer);
if (!key.is_valid()) {
push_dict(stack, std::move(dict));
stack.push_smallint(0);
return 0;
}
} else {
key = stack.pop_cellslice()->prefetch_bits(n);
}
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
if (!(args & 1)) {
auto res = dict.lookup_delete(key);
push_dict(stack, std::move(dict));
bool ok = res.not_null();
if (ok) {
stack.push_cellslice(std::move(res));
}
stack.push_bool(ok);
} else {
auto res = dict.lookup_delete_ref(key);
push_dict(stack, std::move(dict));
bool ok = res.not_null();
if (ok) {
stack.push_cell(std::move(res));
}
stack.push_bool(ok);
}
return 0;
}
int exec_dict_setget_optref(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 2 ? (args & 1 ? "U" : "I") : "") << "SETGETOPTREF";
stack.check_underflow(4);
int n = stack.pop_smallint_range(Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
BitSlice key;
unsigned char buffer[Dictionary::max_key_bytes];
if (args & 2) {
key = dict.integer_key(stack.pop_int(), n, !(args & 1), buffer);
} else {
key = stack.pop_cellslice()->prefetch_bits(n);
}
auto new_value = stack.pop_maybe_cell();
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key"};
}
Ref<vm::Cell> value;
if (new_value.not_null()) {
value = dict.lookup_set_ref(key, std::move(new_value));
} else {
value = dict.lookup_delete_ref(key);
}
push_dict(stack, std::move(dict));
stack.push_maybe_cell(std::move(value));
return 0;
}
int exec_dict_getmin(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 4 ? (args & 2 ? "U" : "I") : "") << (args & 16 ? "REM" : "")
<< (args & 8 ? "MAX" : "MIN") << (args & 1 ? "REF\n" : "\n");
stack.check_underflow(2);
int n = stack.pop_smallint_range(args & 4 ? (args & 2 ? 256 : 257) : Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
unsigned char buffer[Dictionary::max_key_bytes];
bool flip_first = !(args & 2);
if (!(args & 1)) {
auto res = (args & 16) ? dict.extract_minmax_key(buffer, n, args & 8, flip_first)
: dict.get_minmax_key(buffer, n, args & 8, flip_first);
if (args & 16) {
push_dict(stack, std::move(dict));
}
if (res.is_null()) {
stack.push_bool(false);
return 0;
}
stack.push_cellslice(std::move(res));
} else {
auto res = (args & 16) ? dict.extract_minmax_key_ref(buffer, n, args & 8, flip_first)
: dict.get_minmax_key_ref(buffer, n, args & 8, flip_first);
if (args & 16) {
push_dict(stack, std::move(dict));
}
if (res.is_null()) {
stack.push_bool(false);
return 0;
}
stack.push_cell(std::move(res));
}
if (args & 4) {
td::RefInt256 x{true};
x.unique_write().import_bits(td::ConstBitPtr{buffer}, n, !(args & 2));
stack.push_int(std::move(x));
} else {
stack.push_cellslice(Ref<CellSlice>{true, CellBuilder().store_bits(td::ConstBitPtr{buffer}, n).finalize()});
}
stack.push_bool(true);
return 0;
}
std::string dump_dictop_getnear(CellSlice& cs, unsigned args) {
std::ostringstream os{"DICT"};
if (args & 8) {
os << (args & 4 ? 'U' : 'I');
}
os << "GET" << (args & 2 ? "PREV" : "NEXT") << (args & 1 ? "EQ" : "");
return os.str();
}
int exec_dict_getnear(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 8 ? (args & 4 ? "U" : "I") : "") << "GET" << (args & 2 ? "PREV" : "NEXT")
<< (args & 1 ? "EQ\n" : "\n");
stack.check_underflow(3);
int n = stack.pop_smallint_range(args & 8 ? (args & 4 ? 256 : 257) : Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
unsigned char buffer[Dictionary::max_key_bytes];
bool sgnd = !(args & 4), go_up = !(args & 2), allow_eq = args & 1;
if (!(args & 8)) {
auto key_hint = stack.pop_cellslice()->prefetch_bits(n);
if (!key_hint.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key hint"};
}
td::BitPtr{buffer}.copy_from(key_hint.bits(), n);
key_hint.forget();
auto res = dict.lookup_nearest_key(td::BitPtr{buffer}, n, go_up, allow_eq, false);
if (res.is_null()) {
stack.push_bool(false);
return 0;
}
stack.push_cellslice(std::move(res));
stack.push_cellslice(Ref<CellSlice>{true, CellBuilder().store_bits(td::ConstBitPtr{buffer}, n).finalize()});
} else {
auto key = stack.pop_int_finite();
Ref<CellSlice> res;
if (key->export_bits(td::BitPtr{buffer}, n, sgnd)) {
res = dict.lookup_nearest_key(buffer, n, go_up, allow_eq, sgnd);
} else if ((td::sgn(key) >= 0) ^ go_up) {
res = dict.get_minmax_key(buffer, n, !go_up, sgnd);
}
if (res.is_null()) {
stack.push_bool(false);
return 0;
}
stack.push_cellslice(std::move(res));
key.write().import_bits(td::ConstBitPtr{buffer}, n, sgnd);
stack.push_int(std::move(key));
}
stack.push_bool(true);
return 0;
}
int exec_pfx_dict_set(VmState* st, Dictionary::SetMode mode, const char* name) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute PFXDICT" << name;
stack.check_underflow(3);
int n = stack.pop_smallint_range(PrefixDictionary::max_key_bits);
PrefixDictionary dict{stack.pop_maybe_cell(), n};
auto key_slice = stack.pop_cellslice();
auto new_value = stack.pop_cellslice();
bool res = dict.set(key_slice->data_bits(), key_slice->size(), std::move(new_value), mode);
push_dict(stack, std::move(dict));
stack.push_bool(res);
return 0;
}
int exec_pfx_dict_delete(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute PFXDICTDEL\n";
stack.check_underflow(2);
int n = stack.pop_smallint_range(PrefixDictionary::max_key_bits);
PrefixDictionary dict{stack.pop_maybe_cell(), n};
auto key_slice = stack.pop_cellslice();
bool res = dict.lookup_delete(key_slice->data_bits(), key_slice->size()).not_null();
push_dict(stack, std::move(dict));
stack.push_bool(res);
return 0;
}
int exec_dict_get_exec(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DICT" << (args & 1 ? 'U' : 'I') << "GET" << (args & 2 ? "EXEC\n" : "JMP\n");
stack.check_underflow(3);
int n = stack.pop_smallint_range(Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
unsigned char buffer[Dictionary::max_key_bytes];
dict.integer_key_simple(stack.pop_int(), n, !(args & 1), td::BitPtr{buffer});
auto value = dict.lookup(td::BitPtr{buffer}, n);
if (value.not_null()) {
Ref<OrdCont> cont{true, std::move(value), st->get_cp()};
return (args & 2) ? st->call(std::move(cont)) : st->jump(std::move(cont));
} else {
return 0;
}
}
std::string dump_dict_get_exec(CellSlice& cs, unsigned args) {
return std::string{"DICT"} + (args & 1 ? 'U' : 'I') + "GET" + (args & 2 ? "EXEC" : "JMP");
}
int exec_push_const_dict(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
if (!cs.have(pfx_bits)) {
throw VmError{Excno::inv_opcode, "not enough data bits for a DICTPUSHCONST instruction"};
}
if (!cs.have_refs(1)) {
throw VmError{Excno::inv_opcode, "not enough references for a DICTPUSHCONST instruction"};
}
Stack& stack = st->get_stack();
cs.advance(pfx_bits - 11);
auto slice = cs.fetch_subslice(1, 1);
int n = (int)cs.fetch_ulong(10);
VM_LOG(st) << "execute DICTPUSHCONST " << n << " (" << slice << ")";
stack.push_cell(slice->prefetch_ref());
stack.push_smallint(n);
return 0;
}
std::string dump_push_const_dict(CellSlice& cs, int pfx_bits, const char* name) {
if (!cs.have(pfx_bits, 1)) {
return "";
}
cs.advance(pfx_bits - 11);
auto slice = cs.fetch_subslice(1, 1);
int n = (int)cs.fetch_ulong(10);
std::ostringstream os{name};
os << ' ' << n << " (";
slice->dump_hex(os, false);
os << ')';
return os.str();
}
int compute_len_push_const_dict(const CellSlice& cs, unsigned args, int pfx_bits) {
if (!cs.have(pfx_bits, 1)) {
return 0;
}
return 0x10000 + pfx_bits;
}
int exec_pfx_dict_get(VmState* st, int op, const char* name_suff) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute PFXDICTGET" << name_suff;
stack.check_underflow(3);
int n = stack.pop_smallint_range(PrefixDictionary::max_key_bits);
PrefixDictionary dict{stack.pop_maybe_cell(), n};
auto cs = stack.pop_cellslice();
auto res = dict.lookup_prefix(cs->data_bits(), cs->size());
if (res.first.is_null()) {
if (op & 1) {
throw VmError{Excno::cell_und, "cannot parse a prefix belonging to a given prefix code dictionary"};
}
stack.push_cellslice(std::move(cs));
if (!op) {
stack.push_bool(false);
}
return 0;
}
stack.push_cellslice(cs.write().fetch_subslice(res.second));
if (!(op & 2)) {
stack.push_cellslice(std::move(res.first));
}
stack.push_cellslice(std::move(cs));
if (!op) {
stack.push_bool(true);
return 0;
}
if (op == 1) {
return 0;
}
Ref<OrdCont> cont{true, std::move(res.first), st->get_cp()};
return op & 1 ? st->call(std::move(cont)) : st->jump(std::move(cont));
}
int exec_const_pfx_dict_switch(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
if (!cs.have(pfx_bits)) {
throw VmError{Excno::inv_opcode, "not enough data bits for a PFXDICTSWITCH instruction"};
}
if (!cs.have_refs(1)) {
throw VmError{Excno::inv_opcode, "not enough references for a PFXDICTSWITCH instruction"};
}
Stack& stack = st->get_stack();
cs.advance(pfx_bits - 11);
auto dict_slice = cs.fetch_subslice(1, 1);
int n = (int)cs.fetch_ulong(10);
VM_LOG(st) << "execute PFXDICTSWITCH " << n << " (" << dict_slice << ")";
PrefixDictionary dict{std::move(dict_slice), n};
auto cs1 = stack.pop_cellslice();
auto res = dict.lookup_prefix(cs1->data_bits(), cs1->size());
if (res.first.is_null()) {
stack.push_cellslice(std::move(cs1));
return 0;
} else {
stack.push_cellslice(cs1.write().fetch_subslice(res.second));
stack.push_cellslice(std::move(cs1));
Ref<OrdCont> cont{true, std::move(res.first), st->get_cp()};
return st->jump(std::move(cont));
}
}
int exec_subdict_get(VmState* st, unsigned args) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute SUBDICT" << (args & 2 ? (args & 1 ? "U" : "I") : "") << (args & 4 ? "RP" : "") << "GET";
stack.check_underflow(4);
int n = stack.pop_smallint_range(Dictionary::max_key_bits);
Dictionary dict{stack.pop_maybe_cell(), n};
int mk = (args & 2 ? (args & 1 ? 256 : 257) : Dictionary::max_key_bits);
int k = stack.pop_smallint_range(std::min(mk, n));
BitSlice key;
unsigned char buffer[Dictionary::max_key_bytes];
if (args & 2) {
key = dict.integer_key(stack.pop_int(), k, !(args & 1), buffer, true);
} else {
key = stack.pop_cellslice()->prefetch_bits(k);
}
if (!key.is_valid()) {
throw VmError{Excno::cell_und, "not enough bits for a dictionary key prefix"};
}
if (!dict.cut_prefix_subdict(key.bits(), k, args & 4)) {
throw VmError{Excno::dict_err, "cannot construct subdictionary by key prefix"};
}
push_dict(stack, std::move(dict));
return 0;
}
void register_dictionary_ops(OpcodeTable& cp0) {
using namespace std::placeholders;
cp0.insert(OpcodeInstr::mksimple(0xf400, 16, "STDICT", exec_store_dict))
.insert(OpcodeInstr::mksimple(0xf401, 16, "SKIPDICT", exec_skip_dict))
.insert(OpcodeInstr::mksimple(0xf402, 16, "LDDICTS", std::bind(exec_load_dict_slice, _1, 0)))
.insert(OpcodeInstr::mksimple(0xf403, 16, "PLDDICTS", std::bind(exec_load_dict_slice, _1, 1)))
.insert(OpcodeInstr::mksimple(0xf404, 16, "LDDICT", std::bind(exec_load_dict, _1, 0)))
.insert(OpcodeInstr::mksimple(0xf405, 16, "PLDDICT", std::bind(exec_load_dict, _1, 1)))
.insert(OpcodeInstr::mksimple(0xf406, 16, "LDDICTQ", std::bind(exec_load_dict, _1, 2)))
.insert(OpcodeInstr::mksimple(0xf407, 16, "PLDDICTQ", std::bind(exec_load_dict, _1, 3)))
.insert(OpcodeInstr::mkfixedrange(0xf40a, 0xf410, 16, 3, std::bind(dump_dictop, _2, "GET"), exec_dict_get))
.insert(OpcodeInstr::mkfixedrange(0xf412, 0xf418, 16, 3, std::bind(dump_dictop, _2, "SET"),
std::bind(exec_dict_set, _1, _2, Dictionary::SetMode::Set, "SET", false)))
.insert(OpcodeInstr::mkfixedrange(0xf41a, 0xf420, 16, 3, std::bind(dump_dictop, _2, "SETGET"),
std::bind(exec_dict_setget, _1, _2, Dictionary::SetMode::Set, "SETGET", false)))
.insert(
OpcodeInstr::mkfixedrange(0xf422, 0xf428, 16, 3, std::bind(dump_dictop, _2, "REPLACE"),
std::bind(exec_dict_set, _1, _2, Dictionary::SetMode::Replace, "REPLACE", false)))
.insert(OpcodeInstr::mkfixedrange(
0xf42a, 0xf430, 16, 3, std::bind(dump_dictop, _2, "REPLACEGET"),
std::bind(exec_dict_setget, _1, _2, Dictionary::SetMode::Replace, "REPLACEGET", false)))
.insert(OpcodeInstr::mkfixedrange(0xf432, 0xf438, 16, 3, std::bind(dump_dictop, _2, "ADD"),
std::bind(exec_dict_set, _1, _2, Dictionary::SetMode::Add, "ADD", false)))
.insert(OpcodeInstr::mkfixedrange(0xf43a, 0xf440, 16, 3, std::bind(dump_dictop, _2, "ADDGET"),
std::bind(exec_dict_setget, _1, _2, Dictionary::SetMode::Add, "ADDGET", false)))
.insert(OpcodeInstr::mkfixedrange(0xf441, 0xf444, 16, 2, std::bind(dump_dictop2, _2, "SETB"),
std::bind(exec_dict_set, _1, _2, Dictionary::SetMode::Set, "SET", true)))
.insert(OpcodeInstr::mkfixedrange(0xf445, 0xf448, 16, 2, std::bind(dump_dictop2, _2, "SETGETB"),
std::bind(exec_dict_setget, _1, _2, Dictionary::SetMode::Set, "SETGET", true)))
.insert(
OpcodeInstr::mkfixedrange(0xf449, 0xf44c, 16, 2, std::bind(dump_dictop2, _2, "REPLACEB"),
std::bind(exec_dict_set, _1, _2, Dictionary::SetMode::Replace, "REPLACE", true)))
.insert(OpcodeInstr::mkfixedrange(
0xf44d, 0xf450, 16, 2, std::bind(dump_dictop2, _2, "REPLACEGETB"),
std::bind(exec_dict_setget, _1, _2, Dictionary::SetMode::Replace, "REPLACEGET", true)))
.insert(OpcodeInstr::mkfixedrange(0xf451, 0xf454, 16, 2, std::bind(dump_dictop2, _2, "ADDB"),
std::bind(exec_dict_set, _1, _2, Dictionary::SetMode::Add, "ADD", true)))
.insert(OpcodeInstr::mkfixedrange(0xf455, 0xf458, 16, 2, std::bind(dump_dictop2, _2, "ADDGETB"),
std::bind(exec_dict_setget, _1, _2, Dictionary::SetMode::Add, "ADDGET", true)))
.insert(OpcodeInstr::mkfixedrange(0xf459, 0xf45c, 16, 2, std::bind(dump_dictop2, _2, "DEL"), exec_dict_delete))
.insert(
OpcodeInstr::mkfixedrange(0xf462, 0xf468, 16, 3, std::bind(dump_dictop, _2, "DELGET"), exec_dict_deleteget))
.insert(OpcodeInstr::mkfixedrange(0xf469, 0xf46c, 16, 2, std::bind(dump_dictop2, _2, "GETOPTREF"),
exec_dict_get_optref))
.insert(OpcodeInstr::mkfixedrange(0xf46d, 0xf470, 16, 2, std::bind(dump_dictop2, _2, "SETGETOPTREF"),
exec_dict_setget_optref))
.insert(OpcodeInstr::mksimple(0xf470, 16, "PFXDICTSET",
std::bind(exec_pfx_dict_set, _1, PrefixDictionary::SetMode::Set, "SET")))
.insert(OpcodeInstr::mksimple(0xf471, 16, "PFXDICTREPLACE",
std::bind(exec_pfx_dict_set, _1, PrefixDictionary::SetMode::Replace, "REPLACE")))
.insert(OpcodeInstr::mksimple(0xf472, 16, "PFXDICTADD",
std::bind(exec_pfx_dict_set, _1, PrefixDictionary::SetMode::Add, "ADD")))
.insert(OpcodeInstr::mksimple(0xf473, 16, "PFXDICTDEL", exec_pfx_dict_delete))
.insert(OpcodeInstr::mkfixedrange(0xf474, 0xf480, 16, 4, dump_dictop_getnear, exec_dict_getnear))
.insert(OpcodeInstr::mkfixedrange(0xf482, 0xf488, 16, 5, std::bind(dump_dictop, _2, "MIN"), exec_dict_getmin))
.insert(OpcodeInstr::mkfixedrange(0xf48a, 0xf490, 16, 5, std::bind(dump_dictop, _2, "MAX"), exec_dict_getmin))
.insert(OpcodeInstr::mkfixedrange(0xf492, 0xf498, 16, 5, std::bind(dump_dictop, _2, "REMMIN"), exec_dict_getmin))
.insert(OpcodeInstr::mkfixedrange(0xf49a, 0xf4a0, 16, 5, std::bind(dump_dictop, _2, "REMMAX"), exec_dict_getmin))
.insert(OpcodeInstr::mkfixed(0xf4a0 >> 2, 14, 2, dump_dict_get_exec, exec_dict_get_exec))
.insert(OpcodeInstr::mkextrange(0xf4a400, 0xf4a800, 24, 11,
std::bind(dump_push_const_dict, _1, _3, "DICTPUSHCONST"), exec_push_const_dict,
compute_len_push_const_dict))
.insert(OpcodeInstr::mksimple(0xf4a8, 16, "PFXDICTGETQ", std::bind(exec_pfx_dict_get, _1, 0, "Q")))
.insert(OpcodeInstr::mksimple(0xf4a9, 16, "PFXDICTGET", std::bind(exec_pfx_dict_get, _1, 1, "")))
.insert(OpcodeInstr::mksimple(0xf4aa, 16, "PFXDICTGETJMP", std::bind(exec_pfx_dict_get, _1, 2, "JMP")))
.insert(OpcodeInstr::mksimple(0xf4ab, 16, "PFXDICTGETEXEC", std::bind(exec_pfx_dict_get, _1, 3, "EXEC")))
.insert(OpcodeInstr::mkextrange(0xf4ac00, 0xf4b000, 24, 11,
std::bind(dump_push_const_dict, _1, _3, "PFXDICTSWITCH"),
exec_const_pfx_dict_switch, compute_len_push_const_dict))
.insert(OpcodeInstr::mkfixedrange(0xf4b1, 0xf4b4, 16, 3, std::bind(dump_subdictop2, _2, "GET"), exec_subdict_get))
.insert(
OpcodeInstr::mkfixedrange(0xf4b5, 0xf4b8, 16, 3, std::bind(dump_subdictop2, _2, "RPGET"), exec_subdict_get));
}
} // namespace vm