跳到主要内容

FunC 标准库

信息

本节讨论了 stdlib.fc 库,它包含了在 FunC 中使用的标准函数。

目前,该库只是最常用的 TVM 命令的汇编器的包装,这些命令不是内置的。库中使用的每个 TVM 命令的描述都可以在 TVM 文档部分找到。本文档也借用了一些描述。

文件中的一些函数被注释掉了。这意味着它们已经成为了优化目的的内置函数。然而,类型签名和语义保持不变。

请注意,stdlib 中没有呈现一些不太常见的命令。总有一天它们也会被添加。

元组操作原语

名称和类型大多是自解释的。有关多态函数的更多信息,请参见 多态性与 forall

请注意,目前原子类型 tuple 的值不能转换为复合元组类型(例如 [int, cell]),反之亦然。

Lisp 类型列表

列表可以表示为嵌套的 2 元组。空列表通常表示为 TVM null 值(可以通过调用 null() 获得)。例如,元组 (1, (2, (3, null))) 表示列表 [1, 2, 3]。列表的元素可以是不同类型。

cons

forall X -> tuple cons(X head, tuple tail) asm "CONS";

在 Lisp 类型列表的开头添加一个元素。

uncons

forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";

提取 Lisp 类型列表的头和尾。

list_next

forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";

提取 Lisp 类型列表的头和尾。可用作 (非)修改方法

() foo(tuple xs) {
(_, int x) = xs.list_next(); ;; get the first element, `_` means do not use tail list
int y = xs~list_next(); ;; pop the first element
int z = xs~list_next(); ;; pop the second element
}

car

forall X -> X car(tuple list) asm "CAR";

返回 Lisp 类型列表的头部。

cdr

tuple cdr(tuple list) asm "CDR";

返回 Lisp 类型列表的尾部。

其他元组原语

empty_tuple

tuple empty_tuple() asm "NIL";

创建 0 元素元组。

tpush

forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";

将值 x 追加到 Tuple t = (x1, ..., xn),但只有在结果 Tuple t' = (x1, ..., xn, x) 不超过 255 个字符时才有效。否则,会抛出类型检查异常。

single

forall X -> [X] single(X x) asm "SINGLE";

创建单例,即长度为一的元组。

unsingle

forall X -> X unsingle([X] t) asm "UNSINGLE";

解包单例。

pair

forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";

创建一对。

unpair

forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";

解包一对。

triple

forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";

创建三元组。

untriple

forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";

解包三元组。

tuple4

forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";

创建四元组。

untuple4

forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";

解包四元组。

first

forall X -> X first(tuple t) asm "FIRST";

返回元组的第一个元素。

second

forall X -> X second(tuple t) asm "SECOND";

返回元组的第二个元素。

third

forall X -> X third(tuple t) asm "THIRD";

返回元组的第三个元素。

fourth

forall X -> X fourth(tuple t) asm "3 INDEX";

返回元组的第四个元素。

pair_first

forall X, Y -> X pair_first([X, Y] p) asm "FIRST";

返回一对的第一个元素。

pair_second

forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";

返回一对的第二个元素。

triple_first

forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";

返回三元组的第一个元素。

triple_second

forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";

返回三元组的第二个元素。

triple_third

forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";

返回三元组的第三个元素。

特定领域原语

从 c7 提取信息

关于智能合约调用的一些有用信息可以在 c7 特殊寄存器中找到。这些原语用于方便地提取数据。

now

int now() asm "NOW";

返回当前 Unix 时间作为整数。

my_address

slice my_address() asm "MYADDR";

以 Slice 形式返回当前智能合约的内部地址,其中包含 MsgAddressInt。如果需要,可以进一步使用诸如 parse_std_addr 之类的原语进行解析。

get_balance

[int, cell] get_balance() asm "BALANCE";

tuple 形式返回智能合约的剩余余额,其中包括 int(剩余余额,以nanoton计)和 cell(一个包含 32 位键的字典,代表“额外代币”的余额)。注意,RAW 原语(如 send_raw_message)不会更新此字段。

cur_lt

int cur_lt() asm "LTIME";

返回当前交易的逻辑时间。

block_lt

int block_lt() asm "BLOCKLT";

返回当前区块的起始逻辑时间。

config_param

cell config_param(int x) asm "CONFIGOPTPARAM";

cellnull 值的形式返回全局配置参数的值,其中整数索引为 i

哈希

cell_hash

int cell_hash(cell c) asm "HASHCU";

计算cell c的 representation hash ,并将其作为一个256位无符号整数x返回。用于签名和检查由cell树表示的任意实体的签名。

slice_hash

int slice_hash(slice s) asm "HASHSU";

计算slice s的哈希,并将其作为一个256位无符号整数x返回。结果与创建一个只包含s的数据和引用的普通cell,并通过cell_hash计算其哈希的情况相同。

string_hash

int string_hash(slice s) asm "SHA256U";

计算slice s数据位的sha256。如果s的位长度不能被八整除,则抛出一个cell下溢异常。哈希值作为一个256位无符号整数x返回。

签名检查

check_signature

int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU";

使用public_key(也表示为一个256位无符号整数)检查hash(通常作为某些数据的哈希计算得出的256位无符号整数)的Ed25519 signature。签名必须包含至少512个数据位;只使用前512位。如果签名有效,结果为-1;否则,为0。请注意,CHKSIGNU创建一个包含哈希的256位 slice ,并调用CHKSIGNS。也就是说,如果hash是作为某些数据的哈希计算的,这些数据会被_两次_哈希,第二次哈希发生在CHKSIGNS内部。

check_data_signature

int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS";

检查signature是否是使用public_keyslice data数据部分的有效Ed25519签名,类似于check_signature。如果data的位长度不能被八整除,则抛出一个cell下溢异常。Ed25519签名的验证是标准的,使用sha256将data简化为实际签名的256位数字。

计算boc大小

下面的原语可能对于计算用户提供数据的存储费用有用。

compute_data_size?

(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";

返回(x, y, z, -1)(null, null, null, 0)。递归地计算以cell c为根的DAG中不同cell的数量x、数据位y和cell引用z,有效地返回此DAG使用的总存储量,同时考虑到相等cell的识别。xyz的值通过对此DAG进行深度优先遍历来计算,并使用访问过的cell哈希的哈希表来防止已访问cell的重复访问。访问的cell总数x不能超过非负的max_cells;否则,在访问第(max_cells + 1)个cell之前,计算将被中止,并返回零标志以指示失败。如果cnull,则返回x = y = z = 0

slice_compute_data_size?

(int, int, int, int) slice_compute_data_size?(slice s, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";

类似于compute_data_size?,但接受的是slice s而不是cell。返回的x值不考 虑包含 slice s本身的cell;然而,s的数据位和cell引用在yz中要被考虑。

compute_data_size

(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE";

compute_data_size?的非静默版本,失败时抛出cell溢出异常(8)。

slice_compute_data_size

(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE";

slice_compute_data_size?的非静默版本,失败时抛出cell溢出异常(8)。

持久存储保存和加载

get_data

cell get_data() asm "c4 PUSH";

返回持久化合约存储cell。稍后可以使用 slice 和构建器原语对其进行解析或修改。

set_data

() set_data(cell c) impure asm "c4 POP";

将cellc设置为持久化合约数据。您可以使用这个原语更新持久化合约存储。

Continuation 原语

get_c3

cont get_c3() impure asm "c3 PUSH";

通常c3有一个由合约的整个代码初始化的continuation。它用于函数调用。原语返回c3的当前值。

set_c3

() set_c3(cont c) impure asm "c3 POP";

更新c3的当前值。通常,它用于实时更新智能合约代码。请注意,在执行此原语之后,当前代码(以及递归函数调用堆栈)不会改变,但任何其他函数调用将使用新代码中的函数。

bless

cont bless(slice s) impure asm "BLESS";

slice s转换为一个简单的普通 continuation c,其中c.code = s,堆栈和保存列表为空。

与 gas 相关的原语

accept_message

() accept_message() impure asm "ACCEPT";

将当前 gas 限制gl设置为其允许的最大值gm,并将 gas 信用gc重置为零,同时减少gr的值gc。换句话说,当前智能合约同意购买一些 gas 以完成当前交易。这个动作是处理不携带价值(因此不含 gas )的外部消息所必需的。

有关更多详细信息,请查看accept_message effects

set_gas_limit

() set_gas_limit(int limit) impure asm "SETGASLIMIT";

将当前 gas 限制gl设置为limitgm的最小值,并将 gas 信用gc重置为零。此时,如果消耗的 gas 量(包括当前指令)超过gl的结果值,则在设置新 gas 限制之前会抛出(未处理的) gas 不足异常。请注意,带有limit ≥ 2^63 − 1参数的set_gas_limit等同于accept_message

有关更多详细信息,请查看accept_message effects

commit

() commit() impure asm "COMMIT";

提交寄存器c4(“持久数据”)和c5(“动作”)的当前状态,以便即使稍后抛出异常,当前执行也被视为“成功”,并保存这些值。

buy_gas

() buy_gas(int gram) impure asm "BUYGAS";
警告

BUYGAS操作码目前尚未实现

计算可以用gramnanoton币购买的gas 量,并以与set_gas_limit相同的方式相应地设置gl

动作原语

raw_reserve

() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";

创建一个输出动作,该动作将准确地预留amount nanoton 币(如果mode = 0),最多amount nanoton 币(如果mode = 2),或除amount nanoton 币以外的所有 nanoton 币(如果mode = 1mode = 3)从账户的剩余余额中。它大致等同于创建一个携带amount nanoton 币(或b − amount nanoton 币,其中b是剩余余额)的出站消息发送给自己,这样随后的输出动作就不会花费超过剩余部分的金额。mode中的+2位意味着外部动作在无法预留指定金额时不会失败;相反,将预留所有剩余余额。mode中的+8位意味着amount <- -amount在进行任何进一步的动作之前。mode中的+4位意味着在进行任何其他检查和动作之前,amount会增加当前账户的原始余额(在 Compute Phase 之前),包括所有额外代币。目前,amount必须是非负整数,mode必须在0..15范围内。

raw_reserve_extra

() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";

类似于raw_reserve,但还接受一个由cellnull表示的额外代币字典extra_amount。这样,除了Toncoin以外的其他代币也可以被预留。

send_raw_message

() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";

发送包含在msg中的原始消息,它应该包含一个正确序列化的消息对象X,唯一的例外是源地址可以有一个虚拟值addr_none(自动替换为当前智能合约地址),以及ihr_feefwd_feecreated_ltcreated_at字段可以有任意值(在当前交易的 Action Phase 期间用正确的值重写)。整数参数mode包含标志。

目前有3种消息Modes和3种消息Flags。您可以将单一Mode与多个(也许没有)标志组合以获得所需的mode。组合只是意味着获取它们值的总和。下面给出了Modes和Flags的描述表格。

Mode描述
0普通消息
64除了最初在新消息中指示的值之外,还携带入站消息的所有剩余价值
128携带当前智能合约的所有剩余余额,而不是最初在消息中指示的值
Flag描述
+1单独支付消息价值之外的转移费用
+2忽略在 Action Phase 处理此消息时出现的任何错误
+16在动作失败的情况下 - 弹回交易。如果使用+2,则无效。
+32如果当前账户的最终余额为零,则必须销毁该账户(通常与模式128一起使用)
+2 flag
  1. Toncoins 不足:
    • 没有足够的值与报文一起传输(所有输入的报文值都已消耗)。
    • 处理信息的资金不足。
    • 信息的价值不足以支付转发费用。
    • 没有足够的额外货币来发送信息。
    • 没有足够的资金支付对外信息。
  2. 信息过大(请查看 Message size,了解更多信息)。
  3. 信息的 Merkle 深度太大。

但在以下情况下,它不会忽略错误:

  1. 报文格式无效。
  2. 信息模式包括 64 和 128 两种模式。
  3. 出站报文的 StateInit 库无效。
  4. 外部报文不是普通报文,或包含 +16 或 +32 标志,或两者兼有。
warning
  1. +16 标志 - 不要在外部报文(如发给钱包的报文)中使用,因为没有发件人接收被退回的报文。
  2. +2 标志 - 这在外部消息(例如,发送到钱包)中非常重要。

伪随机数生成器使用随机种子(一个无符号的256位整数)和(有时)c7中保存的其他数据。在TON区块链中执行智能合约之前,随机种子的初始值是智能合约地址和全局区块随机种子的哈希。如果在一个区块内有多次运行相同的智能合约,那么所有这些运行都将具有相同的随机种子。例如,可以通过在第一次使用伪随机数生成器之前运行randomize_lt来解决这个问题。

set_code

() set_code(cell new_code) impure asm "SETCODE";

创建一个输出操作,将此智能合约代码更改为 cell new_code 给出的代码。请注意,只有在成功终止智能合约的当前运行后,该更改才会生效。(参见 set_c3)

随机数发生器基元

生成一个新的伪随机无符号256位整数x。算法如下:如果r是旧的随机种子值,被视为一个32字节的数组(通过构造一个无符号256位整数的大端表示),那么计算其sha512(r);这个哈希的前32字节被存储为随机种子的新值r',剩余的32字节作为下一个随机值x返回。

警告

Keep in mind that random numbers generated by the functions below can be predicted if you do not use additional tricks.

get_seed

int random() impure asm "RANDU256";

以一个无符号的256位整数返回当前随机种子。

set_seed

int rand(int range) impure asm "RAND";

将随机种子设置为一个无符号的256位seed

randomize

int get_seed() impure asm "RANDSEED";

通过将随机种子设置为两个32字节字符串的串联的sha256来将一个无符号的256位整数x混合到随机种子r中,这两个32字节字符串:第一个包含旧种子r的大端表示,第二个包含x的大端表示。

randomize_lt

int set_seed(int seed) impure asm "SETRAND";

相当于randomize(cur_lt());

地址操作原语

() randomize(int x) impure asm "ADDRAND";

将无符号 256 位整数 x 混合到随机种子 r 中,方法是将随机种子设置为两个 32 字节字符串连接的 sha256:第一个字符串是旧种子 r 的大二进制表示,第二个字符串是 x 的大二进制表示。

randomize_lt

() randomize_lt() impure asm "LTIME" "ADDRAND";

等价于 randomize(cur_lt());

地址操作基元

slice s 加载唯一有效的 MsgAddress 前缀,并返回此前缀 s's 的其余部分 s'' 作为 slice 。

addr_none$00 = MsgAddressExt;

addr_extern$01 len:(## 8) external_address:(bits len)
= MsgAddressExt;

anycast_info$_ depth:(#<= 30) { depth >= 1 }
rewrite_pfx:(bits depth) = Anycast;

addr_std$10 anycast:(Maybe Anycast)
workchain_id:int8 address:bits256 = MsgAddressInt;

addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
_ _:MsgAddressInt = MsgAddress;
_ _:MsgAddressExt = MsgAddress;

int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddress dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;

ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;

反序列化后的 MsgAddress 由元组 t 表示,如下所示:

  • addr_nonet = (0) 表示,即一个元组恰好包含 一个等于零的整数
  • addr_extern 表示为 t = (1, s),其中片段 s 包含 字段 external_address。换句话说,t 是一对(由两个条目组成的元组),包含一个等于 1 的整数和片段 s
  • addr_std 表示为 t = (2, u, x, s),其中 unull (如果不存在 anycast )或包含 rewrite_pfx 的片段 s' (如果存在 anycast )。接下来,整数 xworkchain_id ,片段 s 包含地址
  • addr_vart = (3,u,x,s) 表示,其中 uxs 的含义与 addr_std 相同。

parse_std_addr

(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR";

解析包含有效 MsgAddressInt(通常为 msg_addr_std)的 slice s,将重写 anycast(如果存在)应用到地址相同长度前缀,并返回工作链和256位地址作为整数。如果地址不是256位,或者 s 不是 MsgAddressInt 的有效序列化,抛出cell deserialization 异常。

parse_var_addr

tuple parse_addr(slice s) asm "PARSEMSGADDR";

parse_std_addr 的变体,即使地址不是正好256位长(由 msg_addr_var 表示),也以 slice s 返回(重写后的)地址。

调试原语

(int, int) parse_std_addr(slice s) asm "REWRITESTDADDR";

解析包含有效的 MsgAddressInt(通常为 msg_addr_std)的片段 s,将 anycast (如果存在)重写应用于地址的同长度前缀,然后以整数形式返回工作链和 256 位地址。如果地址不是 256 位或 s 不是 MsgAddressInt 的有效序列化,则会抛出 cell 反序列化异常。

parse_var_addr

(int, slice) parse_var_addr(slice s) asm "REWRITEVARADDR";

parse_std_addr 的一个变体,以片段 s 的形式返回(重写的)地址,即使它不是精确的 256 位长(由 msg_addr_var 表示)。

调试原语

据说,如果原语仅返回数据,则称其为_预加载_数据(可用作非修改方法)。

~dump

forall X -> () ~dump(X value) impure asm "s0 DUMP";

输出一个值。多个值可以作为一个元组转储,例如~dump([v1, v2, v3])

~strdump

() ~strdump(slice str) impure asm "STRDUMP";

转存字符串。 slice 参数位长必须能被 8 整除。

dump_stack

() dump_stack() impure asm "DUMPSTK";

转出堆栈(最多转出顶部 255 个值)并显示堆栈总深度。

slice 基元

如果一个基元返回数据和片段的剩余部分(因此也可用作修改方法),那么就可以说它 加载 了某些数据。

如果一个基元只返回某些数据,那么就可以说它 预加载 了这些数据(它可以用作非修改方法)。

从 slice 中预加载第一个引用。

load_int

slice begin_parse(cell c) asm "CTOS";

从 slice 中加载一个有符号的 len 位整数。

load_uint

() end_parse(slice s) impure asm "ENDS";

从 slice 中加载一个无符号的 len 位整数。

preload_int

(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";

从 slice 中预加载一个有符号的 len 位整数。

preload_uint

cell preload_ref(slice s) asm "PLDREF";

从 slice 中预加载一个无符号的 len 位整数。

load_bits

;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX";

从 slice s 中加载前 0 ≤ len ≤ 1023 位到一个单独的 slice s''

preload_bits

;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX";

从 slice s 中预加载前 0 ≤ len ≤ 1023 位到一个单独的 slice s''

load_coins

;; int preload_int(slice s, int len) asm "PLDIX";

加载序列化的 Toncoins 数量(任何最高为 2^120 - 1 的无符号整数)。

skip_bits

;; int preload_uint(slice s, int len) asm "PLDUX";

返回 s 的前 0 ≤ len ≤ 1023 位以外的所有值。

first_bits

;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX";

返回 s 的前 0 ≤ len ≤ 1023 位。

skip_last_bits

;; slice preload_bits(slice s, int len) asm "PLDSLICEX";

返回 s 中除最后 0 ≤ len ≤ 1023 位之外的所有值。

slice_last

(slice, int) load_coins(slice s) asm( -> 1 0) "LDGRAMS";

返回 s 的最后 0 ≤ len ≤ 1023 位。

load_dict

slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";

从 slice s 中加载字典 D。可应用于字典或任意 Maybe ^Y 类型的值(如果使用 nothing 构造器,则返回 null)。

preload_dict

slice first_bits(slice s, int len) asm "SDCUTFIRST";

从 slice s 中预加载字典 D

skip_dict

slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";

load_dict 一样加载字典,但只返回 slice 的其余部分。

slice 大小原语

slice slice_last(slice s, int len) asm "SDCUTLAST";

返回 s 的最后 0 ≤ len ≤ 1023 位。

load_dict

(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";

从片段 s 中加载字典 D。可用于字典或任意 Maybe ^Y 类型的值(如果使用了 nothing 构造函数,则返回 null)。

preload_dict

cell preload_dict(slice s) asm "PLDDICT";

从片段 s 中预载字典 D

skip_dict

slice skip_dict(slice s) asm "SKIPDICT";

load_dict 一样加载字典,但只返回片段的剩余部分。

slice 尺寸基元

slice_data_empty?

int slice_refs(slice s) asm "SREFS";

检查 slice s 是否没有数据位。

slice_refs_empty?

int slice_bits(slice s) asm "SBITS";

检查 slice s 是否没有引用。

slice_depth

(int, int) slice_bits_refs(slice s) asm "SBITREFS";

返回 slice s 的深度。如果 s 没有引用,则返回 0;否则,返回值是 s 中引用的 cell 的深度最大值加一。

构建器原语

int slice_empty?(slice s) asm "SEMPTY";

下面列出的所有原语首先检查构建器中是否有足够的空间,然后是被序列化值的范围。

begin_cell

int slice_data_empty?(slice s) asm "SDEMPTY";

创建一个新的空 builder

end_cell

int slice_refs_empty?(slice s) asm "SREMPTY";

builder 转换为普通的 cell

store_ref

int slice_depth(slice s) asm "SDEPTH";

将对 cell c 的引用存储到构建器 b 中。

store_uint

如果一个基元返回的构建器 b 的修改版本中,b 的末尾存储了值 x,那么这个基元就可以说是在构建器 b 中存储了值 x。它可以作为非修改方法使用。

将无符号的 len 位整数 x 存储到 b 中,0 ≤ len ≤ 256

store_int

builder begin_cell() asm "NEWC";

将有符号的 len 位整数 x 存储到 b 中,0 ≤ len ≤ 257

store_slice

cell end_cell(builder b) asm "ENDC";

将 slice s 存储到构建器 b 中。

store_grams

builder store_ref(builder b, cell c) asm(c b) "STREF";

将 cell c 的引用存储到构造函数 b

store_uint

builder store_uint(builder b, int x, int len) asm(x b len) "STUX";

这是存储 Toncoins 的最常见方法。

store_dict

builder store_int(builder b, int x, int len) asm(x b len) "STIX";

将由 cell cnull 表示的字典 D 存储到构建器 b 中。换句话说,如果 c 不是 null,则存储1位和对 c 的引用;否则存储0位。

store_maybe_ref

builder store_slice(builder b, slice s) asm "STSLICER";

等同于 store_dict

构建器大小原语

builder store_grams(builder b, int x) asm "STGRAMS";

store_coins

builder store_coins(builder b, int x) asm "STGRAMS";

将范围为 0..2^120 - 1 的整数 x 存储(序列化)到生成器 b 中。x 的序列化由一个 4 位无符号大端整数 l(即 x < 2^8l 的最小整数 l ≥ 0)和一个 x8l 位无符号大端表示组成。如果 x 不属于支持的范围,将产生范围检查异常。

这是储存Toncoins的最常见方式。

store_dict

builder store_dict(builder b, cell c) asm(c b) "STDICT";

将 cell cnull 所代表的字典 D 存储到构造函数 b。换句话说,如果 c 不是 null 则存储 1 位和对 c 的引用,否则存储 0 位。

store_maybe_ref

builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";

等同于 store_dict

生成器尺寸基元

builder_refs

int builder_refs(builder b) asm "BREFS";

返回已存储在构建器 b 中的 cell 引用的数量。

builder_bits

int builder_bits(builder b) asm "BBITS";

返回已存储在构建器 b 中的数据位的数量。

builder_depth

int builder_depth(builder b) asm "BDEPTH";

在 FunC 中,字典也由 cell 类型表示,隐含假设它可能是 null 值。字典没有不同键长或值类型的单独类型(毕竟,这是 FunC,不是 FunC++)。

分类说明

cell_depth

int cell_depth(cell c) asm "CDEPTH";

标题中使用了空前缀。

cell_null?

int cell_null?(cell c) asm "ISNULL";

字典中的值可以直接存储为内部字典 cell 的子 slice ,也可以作为对单独 cell 的引用存储。在第一种情况下,不能保证一个足够小以适应 cell 格的值也将适应字典 ,则适合字典(因为内部 cell 的一部分可能已经被对应键的一部分占用)。另一方面,第二种存储方式的 gas 效率较低。使用第二种方法存储一个值等同于在第一种方法中插入一个没有数据位的 slice 和一个对该值的单一引用。

dict_set

警告

下面的字典原语是低级的,不会检查所应用的 cell 结构是否与操作签名相匹配。将字典操作应用于 "非字典",或将与一种键长/符号相对应的操作应用于具有不同种类键的字典,例如同时向一个字典写入具有 8 位有符号键和 7 位无符号键的键值,都是未定义行为。在这种情况下,通常会出现异常,但在极少数情况下,可能会写入/读取错误的值。强烈建议开发人员避免使用此类代码。

在字典 dict 中设置与 key_len 位键 index 关联的值 value(一个 slice ),并返回结果字典。

字典作为 TVM 堆栈值有两种不同的表示方法:

  • slice s 的序列化类型为 HashmapE(n, X) 的 TL-B 值。换句话说,s 包含一个等于零的比特(如果字典为空),或者一个等于一的比特和对包含二叉树根的 cell 的引用,即Hashmap(n, X)类型的序列化值。
  • 一个 "Maybe cell" c^?,即一个 cell 值(包含一个类型为Hashmap(n, X)的序列化值)或一个null(对应于一个空字典,参见 null values)。当使用 "Maybe cell " c^? 来表示字典时,我们通常用D来表示。

下面列出的大多数字典原语都接受并返回第二种形式的字典,因为第二种形式更便于堆栈操作。不过,较大的 TL-B 对象中的序列化字典使用第一种表示法。

在 FunC 中,字典也由 cell 类型表示,并隐含假设它可能是一个 null 值。对于具有不同键长度或值类型的字典,没有单独的类型(毕竟,这是 FunC,而不是 FunC++)。

分类说明

字典基元可以将字典的键解释为无符号 l 位整数、有符号 l 位整数或 l 位 slice 。下面列出的基元因其名称中 dict 前的前缀而不同。i "代表有符号整数键,"u "代表无符号整数键,空前缀代表 slice 键。

字典原语可能将字典的键解释为无符号 l 位整数、有符号 l 位整数或 l 位 slice 。下面列出的原语名称中的前缀不同。i 表示有符号整数键,u 表示无符号整数键,空前缀表示 slice 键。

在字典 dict 中查找 key_len 位键 index。成功时,返回找到的值作为 slice 以及表示成功的 -1 标志位。如果失败,则返回 (null, 0)

此外,某些基元的对应前缀为~。这样就可以将它们用作修改方法

字典的值

类似于 dict_get?,但返回找到的值的第一个引用。

dict_get_ref

cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";

dict_get_ref? 的变体,如果键 index 不在字典 dict 中,则返回 null 而不是值。

dict_set_get_ref

cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";

将与 index 关联的值设置为 value(如果 valuenull,则删除键),并返回旧值(如果值不存在,则为 null)。

dict_delete?

(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";

从字典 dict 中删除 key_len 位键 index。如果键存在,则返回修改后的字典 dict' 和成功标志位 −1。否则,返回原始字典 dict0

dict_delete_get?

(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";

从字典 dict 中删除 key_len 位键 index。如果键存在,则返回修改后的字典 dict'、与键 k 关联的原始值 x(由一个 slice 表示),以及成功标志位 −1。否则,返回 (dict, null, 0)

dict_add?

cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";

dict_setadd 对应项,将字典 dict 中与键 index 关联的值设置为 value,但仅当它尚未出现在 D 中时。返回修改后的字典和 -1 标志位或 (dict, 0)

dict_replace?

(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";

类似于 dict_setreplace 操作,但只有在键 index 已经出现在 dict 中时才将字典 dict 中键 index 的值设置为 value。返回修改后的字典和 -1 标志位或 (dict, 0)

构建器对应项

(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";

从字典dict中删除key_len位键index。如果键存在,则返回修改后的字典 dict' 和成功标志 -1。否则,返回原始字典 dict0

dict_delete_get?

(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";

从字典dict中删除key_len位键index。如果键存在,则返回修改后的 dictionary dict'、与键 k 相关的原始值 x(用 Slice 表示)和成功标志 -1。否则,返回 (dict,null,0)

dict_add?

(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";

dict_set 相对应的 add 将字典 dict 中与键 index 相关的值设置为 value,但前提是 D 中还没有这个值。返回字典的修改版本和-1标志或(dict, 0)

dict_replace?

(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";

dict_set类似的replace操作,但只有在dict中已经存在键index的情况下,才会将字典dict中键index的值设置为value。返回字典的修改版本和-1标志或(dict, 0)

构建器对应项

计算字典 dict 中的最小键 k,将其移除,并返回 (dict', k, x, -1),其中 dict' 是修改后的 dictx 是与 k 关联的值。如果字典为空,则返回 (dict, null, null, 0)

dict_set_builder

cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, ()) ~idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";

dict_set 类似,但接受一个构造函数。

dict_add_builder?

(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";

dict_add? 类似,但接受一个生成器。

dict_replace_builder?

(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";

dict_replace? 类似,但接受一个生成器。

dict_delete_get_min

(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";

计算字典 "dict "中最小键 "k",删除它并返回"(dict', k, x, -1)",其中 "dict'"是 "dict "的修改版本,"x "是与 "k "相关的值。如果 dict 为空,则返回 (dict,null,null,0)

类似于 dict_get_min?,但返回值中唯一的引用作为引用。

dict_get_max_ref?

(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";

类似于 dict_get_max?,但返回值中唯一的引用作为引用。

dict_get_next?

(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";

计算字典 dict 中大于 pivot 的最小键 k;返回 k、关联值和表示成功的标志位。如果字典为空,则返回 (null, null, 0)

dict_get_nexteq?

(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";

类似于 dict_get_next?,但计算大于或等于 pivot 的最小键 k

dict_get_prev?

(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";

类似于 dict_get_next?,但计算小于 pivot 的最大键 k

dict_get_preveq?

(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";

类似于 dict_get_prev?,但计算小于或等于 pivot 的最大键 k

new_dict

(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";

创建一个空字典,实际上是一个 null 值。null() 的特例。

dict_empty?

(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";

检查字典是否为空。等同于 cell_null?

前缀字典原语

(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";

dict_get_next? 类似,但计算比 pivot 小的最大 key k

dict_get_preveq?

(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";

类似于 dict_get_prev?,但计算小于或等于 pivot 的最大 key k

new_dict

cell new_dict() asm "NEWDICT";

创建一个空字典,实际上是一个 null 值。null()` 的特例。

dict_empty?

int dict_empty?(cell c) asm "DICTEMPTY";

检查字典是否为空。等同于 cell_null?

null

TVM 还支持具有非固定长度键的字典,这些键构成前缀码(即没有任何键是另一个键的前缀)。有关它们的更多信息,请参阅 TVM 说明 部分。

pfxdict_get?

(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";

返回 (s',x,s'',-1)(null,null,s,0)。 查找前缀代码字典 "dict "中片段 "key "的唯一前缀。如果找到,则以 s' 的形式返回 s 的前缀,以 x 的形式返回相应的值(也是片段)。s "的剩余部分将作为片段 "s''"返回。如果前缀代码字典 dict 中没有 s 的前缀,则返回不变的 s 和表示失败的 0 标志。

pfxdict_set?

(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";

dict_set 类似,但如果键是字典中另一个键的前缀,则可能失败。返回一个标志表示成功。

pfxdict_delete?

(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";

类似于 dict_delete?

特殊基元

null

forall X -> X null() asm "PUSHNULL";

通过 TVM 类型 Null, FunC 表示不存在某个原子类型的值。因此,null 实际上可以有任何原子类型。

~impure_touch

forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";

将变量标记为使用过的变量,这样产生该变量的代码即使不是不纯的也不会被删除。(参见 impure specifier)。

其他基元

min

int min(int x, int y) asm "MIN";

计算两个整数 xy 的最小值。

max

int max(int x, int y) asm "MAX";

计算两个整数 xy 的最大值。

minmax

(int, int) minmax(int x, int y) asm "MINMAX";

对两个整数进行排序。

abs

int abs(int x) asm "ABS";

计算整数 x 的绝对值。