Перейти к основному содержимому

Стандартная библиотека FunC

warning

Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @alexgton.

к сведению

В этом разделе обсуждается библиотека 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";

Создает кортеж из 4 элементов.

untuple4

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

Распаковывает кортеж из 4 элементов.

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";

Возвращает внутренний адрес текущего смарт-контракта как срез с MsgAddressInt. При необходимости его можно проанализировать далее с помощью примитивов, таких как parse_std_addr.

get_balance

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

Возвращает оставшийся баланс смарт-контракта в виде tuple, состоящего из int (оставшийся баланс в nanotoncoin) и cell (словарь с 32-битными ключами, представляющими баланс "дополнительных валют"). Поскольку это произойдет в фазе вычислений, баланс контракта будет включать входящее сообщение value, вычитать storage_fee и import_fee.

warning

Необработанные примитивы, такие как 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";

Возвращает значение глобального параметра конфигурации с целочисленным индексом i как cell или null значение.

Хэши

cell_hash

int cell_hash(cell c) asm "HASHCU";

Вычисляет хэш представления cell c и возвращает его как 256-битное беззнаковое целое число x. Полезно для подписи и проверки подписей произвольных сущностей, представленных деревом ячеек.

slice_hash

int slice_hash(slice s) asm "HASHSU";

Вычисляет хэш slice s и возвращает его как 256-битное целое число без знака x. Результат такой же, как если бы была создана обычная ячейка, содержащая только данные и ссылки из s, и ее хэш был вычислен cell_hash.

string_hash

int string_hash(slice s) asm "SHA256U";

Вычисляет sha256 бит данных slice s. Если длина бит s не делится на восемь, выдается исключение потери значимости ячейки. Значение хэша возвращается как 256-битное целое число без знака x.

Проверка подписи

check_signature

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

Проверяет signature Ed25519 для hash (256-битное беззнаковое целое число, обычно вычисляемое как хэш некоторых данных) с помощью public_key (также представленного 256-битным беззнаковым целым числом). Подпись должна содержать не менее 512 бит данных; используются только первые 512 бит. Если подпись действительна, результат равен -1; в противном случае это 0. Обратите внимание, что CHKSIGNU создает 256-битный срез с хешем и вызывает CHKSIGNS. То есть, если hash вычисляется как хеш некоторых данных, эти данные хешируются дважды, второе хеширование происходит внутри CHKSIGNS.

check_data_signature

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

Проверяет, является ли signature допустимой подписью Ed25519 части данных slice data, используя public_key, аналогично check_signature. Если длина бит data не делится на восемь, выдается исключение переполнения ячейки. Проверка подписей 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). Рекурсивно вычисляет количество отдельных ячеек x, битов данных y и ссылок на ячейки z в DAG с корнем cell c, фактически возвращая общее хранилище, используемое этим DAG, с учетом идентификации равных ячеек. Значения x, y и z вычисляются путем обхода в глубину этого DAG с хэш-таблицей хэшей посещенных ячеек, используемых для предотвращения посещений уже посещенных ячеек. Общее количество посещенных ячеек x не может превышать неотрицательного max_cells; в противном случае вычисление прерывается до посещения (max_cells + 1)-й ячейки и возвращается нулевой флаг, указывающий на ошибку. Если c равно null, возвращается 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 не учитывает ячейку, содержащую сам срез s; однако биты данных и ссылки на ячейки s учитываются в y и z.

compute_data_size

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

Не тихая версия compute_data_size?, которая выдает исключение переполнения ячейки (8) при сбое.

slice_compute_data_size

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

Не тихая версия slice_compute_data_size?, которая выдает исключение переполнения ячейки (8) при сбое.

Сохранение и загрузка постоянного хранилища

get_data

cell get_data() asm "c4 PUSH";

Возвращает ячейку постоянного хранилища контракта. Позже ее можно проанализировать или изменить с помощью примитивов среза и конструктора.

set_data

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

Устанавливает ячейку c как постоянные данные контракта. Вы можете обновить постоянное хранилище контракта с помощью этого примитива.

Примитивы продолжения

get_c3

cont get_c3() impure asm "c3 PUSH";

Обычно c3 имеет продолжение, инициализированное всем кодом контракта. Оно используется для вызовов функций. Примитив возвращает текущее значение c3.

set_c3

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

Обновляет текущее значение c3. Обычно используется для обновления кода смарт-контракта во время выполнения. Обратите внимание, что после выполнения этого примитива текущий код (и стек рекурсивных вызовов функций) не изменится, но любой другой вызов функции будет использовать функцию из нового кода.

bless

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

Преобразует slice s в простое обычное продолжение c с c.code = s, пустым стеком и списком сохранения.

Примитивы, связанные с газом

accept_message

() accept_message() impure asm "ACCEPT";

Устанавливает текущий лимит газа gl на максимально допустимое значение gm и сбрасывает газовый кредит gc на ноль, уменьшая значение gr на gc в процессе. Другими словами, текущий смарт-контракт соглашается купить немного газа, чтобы завершить текущую транзакцию. Это действие требуется для обработки внешних сообщений, которые не несут никакой ценности (следовательно, нет газа).

Для получения более подробной информации проверьте accept_message effects

set_gas_limit

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

Устанавливает текущий лимит газа gl на минимальное значение limit и gm и сбрасывает газовый кредит gc на ноль. В этот момент, если количество потребленного газа (включая текущую инструкцию) превышает результирующее значение gl, перед установкой новых лимитов газа выдается (необработанное) исключение отсутствия газа. Обратите внимание, что set_gas_limit с аргументом limit ≥ 2^63 − 1 эквивалентно accept_message.

Для получения более подробной информации см. accept_message effects

commit

() commit() impure asm "COMMIT";

Фиксирует текущее состояние регистров c4 ("постоянные данные") и c5 ("действия"), так что текущее выполнение считается "успешным" с сохраненными значениями, даже если позже выдается исключение.

buy_gas

() buy_gas(int gram) impure asm "BUYGAS";
предупреждение

Код операции BUYGAS в настоящее время не реализован

Вычисляет количество газа, которое можно купить за gram nanotoncoin, и устанавливает gl соответственно так же, как set_gas_limit.

Примитивы действий

raw_reserve

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

Создает выходное действие, которое зарезервирует ровно amount nanotoncoin (если mode = 0), максимум amount nanotoncoin (если mode = 2) или все, кроме amount nanotoncoin (если mode = 1 или mode = 3) из оставшегося баланса на аккаунте. Это примерно эквивалентно созданию исходящего сообщения, переносящего amount nanotoncoin (или b − amount nanotoncoin, где b — оставшийся баланс) самому себе, так что последующие выходные действия не смогут потратить больше денег, чем остаток. Бит +2 в mode означает, что внешнее действие не терпит неудачу, если указанная сумма не может быть зарезервирована; вместо этого весь оставшийся баланс резервируется. Бит +8 в mode означает amount <- -amount перед выполнением любых дальнейших действий. Бит +4 в mode означает, что amount увеличивается на исходный баланс текущего аккаунта (до фазы вычислений), включая все дополнительные валюты перед выполнением любых других проверок и действий. В настоящее время amount должно быть неотрицательным целым числом, а mode должно быть в диапазоне 0..15.

raw_reserve_extra

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

Похоже на raw_reserve, но также принимает словарь extra_amount (представленный cell или null) с дополнительными валютами. Таким образом, можно зарезервировать валюты, отличные от Toncoin.

send_raw_message

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

Отправляет необработанное сообщение, содержащееся в msg, которое должно содержать правильно сериализованный объект Message X, с единственным исключением, что исходный адрес может иметь фиктивное значение addr_none (которое будет автоматически заменено текущим адресом смарт-контракта), а поля ihr_fee, fwd_fee, created_lt и created_at могут иметь произвольные значения (которые будут перезаписаны правильными значениями во время фазы действия текущей транзакции). Целочисленный параметр mode содержит флаги.

В настоящее время существует 3 режима и 4 флага для сообщений. Вы можете объединить один режим с несколькими (возможно, ни одним) флагами, чтобы получить требуемый mode. Комбинация просто означает получение суммы их значений. Таблица с описаниями режимов и флагов приведена ниже.

РежимОписание
0Обычное сообщение
64Перенести всю оставшуюся стоимость входящего сообщения в дополнение к стоимости, изначально указанной в новом сообщении
128Перенести весь оставшийся баланс текущего смарт-контракта вместо стоимости, изначально указанной в сообщении
ФлагОписание
+1Оплатить комиссию за перевод отдельно от стоимости сообщения
+2Игнорировать некоторые ошибки, возникающие при обработке этого сообщения на этапе действия (см. примечание ниже)
+16В случае сбоя действия — возврат транзакции. Никакого эффекта, если используется +2.
+32Текущий аккаунт должен быть уничтожен, если его результирующий баланс равен нулю (часто используется с режимом 128)
+2 флага
  1. Недостаточно Toncoin:
    • Недостаточно стоимости для перевода с сообщением (вся стоимость входящего сообщения была израсходована).
    • Недостаточно средств для обработки сообщения.
    • Недостаточно стоимости, прикрепленной к сообщению, для оплаты комиссий за пересылку.
    • Недостаточно дополнительной валюты для отправки с сообщением.
    • Недостаточно средств для оплаты исходящего внешнего сообщения.
  2. Сообщение слишком большое (проверьте размер сообщения для получения дополнительной информации).
  3. Сообщение имеет слишком большую глубину Меркла.

Однако он не игнорирует ошибки в следующих сценариях:

  1. Сообщение имеет недопустимый формат.
  2. Режим сообщения включает как 64, так и 128 модификаций.
  3. Исходящее сообщение имеет недопустимые библиотеки в StateInit.
  4. Внешнее сообщение не является обычным или включает флаг +16 или +32 или оба
warning
  1. Флаг +16 - не использовать во внешних сообщениях (например, на кошельках), так как нет отправителя, который мог бы получить отклоненное сообщение.
  2. Флаг +2 - важно во внешних сообщениях (например, на кошельках).

Вы можете увидеть подробный пример здесь.

set_code

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

Создает выходное действие, которое изменит этот код смарт-контракта на тот, который задан ячейкой new_code. Обратите внимание, что это изменение вступит в силу только после успешного завершения текущего выполнения смарт-контракта. (Ср. set_c3)

Примитивы генератора случайных чисел

Генератор псевдослучайных чисел использует случайное начальное число, беззнаковое 256-битное целое число и (иногда) другие данные, хранящиеся в c7. Начальное значение случайного начального числа перед выполнением смарт-контракта в блокчейне TON представляет собой хэш адреса смарт-контракта и глобального случайного начального числа блока. Если внутри блока есть несколько запусков одного и того же смарт-контракта, то все эти запуски будут иметь одинаковое случайное начальное число. Это можно исправить, например, запустив randomize_lt перед первым использованием генератора псевдослучайных чисел.

предупреждение

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

random

int random() impure asm "RANDU256";

Генерирует новое псевдослучайное целое число без знака длиной 256 бит x. Алгоритм следующий: если r — это старое значение случайного начального числа, рассматриваемого как 32-байтовый массив (путем построения представления с обратным порядком байтов без знака длиной 256 бит), то вычисляется его sha512(r); первые 32 байта этого хэша сохраняются как новое значение r' случайного начального числа, а оставшиеся 32 байта возвращаются как следующее случайное значение x.

rand

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

Генерирует новое псевдослучайное целое число z в диапазоне 0..range−1 (или range..−1, если range < 0). Точнее, генерируется случайное значение x без знака, как в random; затем z := x * range / 2^256 вычисляется.

get_seed

int get_seed() impure asm "RANDSEED";

Возвращает текущее случайное начальное число как беззнаковое 256-битное целое число.

set_seed

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

Устанавливает случайное начальное число как беззнаковое 256-битное seed.

randomize

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

Смешивает беззнаковое 256-битное целое число x со случайным начальным числом r, устанавливая для случайного начального значения значение sha256 для объединения двух 32-байтовых строк: первая с представлением старого начального значения числа r, а вторая с представлением значения в порядке возрастания x.

randomize_lt

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

Эквивалентно randomize(cur_lt());.

Примитивы манипуляции адресами

Примитивы манипуляции адресами, перечисленные ниже, сериализуют и десериализуют значения в соответствии со следующей схемой TL-B.

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_none представлен как t = (0), т. е. кортеж, содержащий ровно одно целое число, равное нулю
  • addr_extern представлен как t = (1, s), где срез s содержит поле external_address. Другими словами, t - это пара (кортеж, состоящий из двух записей), содержащая целое число, равное единице, и срез s
  • addr_std представлен как t = (2, u, x, s), где u - это либо null (если anycast отсутствует), либо срез s', содержащий rewrite_pfx (если anycast присутствует). Далее, целое число x является workchain_id, а срез s содержит адрес
  • addr_var представлено как t = (3, u, x, s), где u, x и s имеют то же значение, что и для addr_std

load_msg_addr

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

Загружает из slice s единственный префикс, который является допустимым MsgAddress, и возвращает как этот префикс s', так и остаток s'' от s в виде срезов.

parse_addr

tuple parse_addr(slice s) asm "PARSEMSGADDR";

Раскладывает slice s, содержащий допустимый MsgAddress, на tuple t с отдельными полями этого MsgAddress. Если s не является допустимым MsgAddress, выдается исключение десериализации ячейки.

parse_std_addr

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

Анализирует срез s, содержащий допустимый MsgAddressInt (обычно msg_addr_std), применяет перезапись из anycast (если присутствует) к префиксу адреса той же длины и возвращает как воркчейн, так и 256-битный адрес в виде целых чисел. Если адрес не 256-битный или если s не является допустимой сериализацией MsgAddressInt, выдает исключение deserialization ячейки.

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";

Выводит строку. Длина бит параметра среза должна делиться на 8.

dump_stack

() dump_stack() impure asm "DUMPSTK";

Выводит стек (максимум 255 верхних значений) и показывает общую глубину стека.

Примитивы среза

Говорят, что примитив загружает некоторые данные, если он возвращает данные и остаток среза (поэтому его также можно использовать как модифицирующий метод).

Говорят, что примитив загружает некоторые данные, если он возвращает только данные (его можно использовать как немодифицирующий метод).

Если не указано иное, примитивы загрузки и предзагрузки считывают данные из префикса фрагмента.

begin_parse

slice begin_parse(cell c) asm "CTOS";

Преобразует cell в slice. Обратите внимание, что c должна быть либо обычной ячейкой, либо экзотической ячейкой (см. TVM.pdf, 3.1.2), которая автоматически загружается для получения обычной ячейки c', преобразованной впоследствии в slice.

end_parse

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

Проверяет, является ли s пустым. Если нет, выдает исключение.

load_ref

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

Загружает первую ссылку из среза.

preload_ref

cell preload_ref(slice s) asm "PLDREF";

Предварительно загружает первую ссылку из среза.

load_int

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

Загружает знаковое len-битное целое число из среза.

load_uint

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

Загружает беззнаковое len-битное целое число из среза.

preload_int

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

Предварительно загружает знаковое len-битное целое число из среза.

preload_uint

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

Предварительно загружает беззнаковое len-битное целое число из среза.

load_bits

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

Загружает первые 0 ≤ len ≤ 1023 бит из среза s в отдельный срез s''.

preload_bits

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

Предварительно загружает первые 0 ≤ len ≤ 1023 бит из slice s в отдельный slice s''.

load_coins

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

Загружает сериализованное количество Toncoin (любое беззнаковое целое число до 2^120 - 1).

skip_bits

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

Возвращает все, кроме первых 0 ≤ len ≤ 1023 бит s.

first_bits

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

Возвращает первые 0 ≤ len ≤ 1023 бит s.

skip_last_bits

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

Возвращает все, кроме последних 0 ≤ len ≤ 1023 бит s.

slice_last

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

Возвращает последние 0 ≤ len ≤ 1023 бит s.

load_dict

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

Загружает словарь D из среза s. Может применяться к словарям или к значениям произвольных типов Maybe ^Y (возвращает null, если используется конструктор nothing).

preload_dict

cell preload_dict(slice s) asm "PLDDICT";

Предварительно загружает словарь D из среза s.

skip_dict

slice skip_dict(slice s) asm "SKIPDICT";

Загружает словарь как load_dict, но возвращает только остаток среза.

Примитивы размера среза

slice_refs

int slice_refs(slice s) asm "SREFS";

Возвращает количество ссылок в срезе s.

slice_bits

int slice_bits(slice s) asm "SBITS";

Возвращает количество бит данных в срезе s.

slice_bits_refs

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

Возвращает как количество бит данных, так и количество ссылок в s.

slice_empty?

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

Проверяет, является ли срез s пустым (т. е. не содержит бит данных и ссылок на ячейки).

slice_data_empty?

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

Проверяет, нет ли в срезе s битов данных.

slice_refs_empty?

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

Проверяет, нет ли в срезе s ссылок.

slice_depth

int slice_depth(slice s) asm "SDEPTH";

Возвращает глубину среза s. Если в s нет ссылок, то возвращается 0; в противном случае возвращаемое значение равно единице плюс максимальная глубина ячеек, на которые ссылается s.

Примитивы конструктора

Говорят, что примитив сохраняет значение x в конструкторе b, если он возвращает измененную версию конструктора b' со значением x, сохраненным в конце. Его можно использовать как немодифицирующий метод.

Все перечисленные ниже примитивы сначала проверяют, достаточно ли места в builder, а затем диапазон сериализуемого значения.

begin_cell

builder begin_cell() asm "NEWC";

Создает новый пустой builder.

end_cell

cell end_cell(builder b) asm "ENDC";

Преобразует builder в обычную cell.

store_ref

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

Сохраняет ссылку на ячейку c в builder b.

store_uint

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

Сохраняет беззнаковое len-битное целое x в b для 0 ≤ len ≤ 256.

store_int

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

Сохраняет знаковое len-битное целое x в b для 0 ≤ len ≤ 257.

store_slice

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

Сохраняет срез s в builder b.

store_grams

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

store_coins

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

Сохраняет (сериализует) целое x в диапазоне 0..2^120 − 1 в builder b. Сериализация x состоит из 4-битного беззнакового целого числа с обратным порядком байтов l, которое является наименьшим целым числом l ≥ 0, таким образом, что x < 2^8l, за которым следует 8l-битное беззнаковое представление x с обратным порядком байтов. Если x не принадлежит поддерживаемому диапазону, выдается исключение проверки диапазона.

Это наиболее распространенный способ хранения Toncoin.

store_dict

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

Сохраняет словарь D, представленный ячейкой c или null, в builder b. Другими словами, сохраняет 1-бит и ссылку на c, если c не является null, и 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";

Возвращает количество ссылок на ячейки, уже сохраненных в Builder b.

builder_bits

int builder_bits(builder b) asm "BBITS";

Возвращает количество битов данных, уже сохраненных в Builder b.

builder_depth

int builder_depth(builder b) asm "BDEPTH";

Возвращает глубину builder b. Если в b не сохранено ссылок на ячейки, то возвращает 0; в противном случае возвращаемое значение равно единице плюс максимальная глубина ячеек, на которые ссылается b.

Примитивы ячеек

cell_depth

int cell_depth(cell c) asm "CDEPTH";

Возвращает глубину ячейки c. Если у c нет ссылок, то возвращает 0; в противном случае возвращаемое значение равно единице плюс максимальная глубина ячеек, на которые ссылается c. Если c является null вместо ячейки, возвращается ноль.

cell_null?

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

Проверяет, является ли c null. Обычно null-ячейка представляет собой пустой словарь. FunC также имеет встроенный полиморфный null?. (См. built-ins.)

Примитивы словарей

предупреждение

Приведенные ниже примитивы словарей являются низкоуровневыми и не проверяют, что структура ячейки, к которой они применяются, соответствует сигнатуре операции. Применение операции словаря к "не словарю" или применение операции, соответствующей одной длине/знаку ключа, к словарю с другим типом ключей, например, одновременная запись в один словарь значений ключей с 8-битным подписанным ключом и 7-битным неподписанным ключом, является Неопределенным поведением. Часто в таких случаях выдается исключение, но в редких случаях может быть записано/считано неправильное значение. Разработчикам настоятельно рекомендуется избегать такого кода.

Как сказано в TVM.pdf:

Словари допускают два различных представления в качестве значений стека TVM:

  • Срез s с сериализацией значения TL-B типа HashmapE(n, X). Другими словами, s состоит либо из одного бита, равного нулю (если словарь пуст), либо из одного бита, равного единице, и ссылки на ячейку, содержащую корень двоичного дерева, т. е. сериализованное значение типа Hashmap(n, X).
  • "Ячейка Maybe" c^?, т. е. значение, которое является либо ячейкой (содержащей сериализованное значение типа Hashmap(n, X), как и раньше), либо null (соответствующее пустому словарю, см. нулевые значения). Когда ячейка Maybe c^? используется для представления словаря, мы обычно обозначаем ее D.

Большинство примитивов словаря, перечисленных ниже, принимают и возвращают словари во второй форме, что более удобно для работы со стеком. Однако сериализованные словари внутри более крупных объектов TL-B используют первое представление.

В FunC словари также представлены типом cell с неявным предположением, что это может быть значение null. Для словарей с разной длиной ключа или типом значения нет отдельных типов (в конце концов, это FunC, а не FunC++).

Примечание по таксономии

Примитив словаря может интерпретировать ключи словаря либо как беззнаковые l-битные целые числа, либо как знаковые l-битные целые числа, либо как l-битные срезы. Примитивы, перечисленные ниже, отличаются префиксом перед словом dict в их именах. i обозначает знаковые целые ключи, u обозначает беззнаковые целые ключи, а пустой префикс обозначает ключи среза.

Например, udict_set — это функция установки по ключу для словарей с беззнаковыми целочисленными ключами; idict_set — это соответствующая функция для словарей с целочисленными ключами со знаком; dict_set — это функция для словарей с ключами срезов.

В заголовках используется пустой префикс.

Кроме того, некоторые примитивы имеют свои аналоги с префиксом ~. Это позволяет использовать их как модифицирующие методы.

Значения в словарях

Значения в словаре могут храниться либо как подсрез во внутренней ячейке словаря, либо через ссылку на отдельную ячейку. В первом случае нет гарантии, что значение, достаточно малое для размещения в ячейке, также поместится в словаре, поскольку часть пространства внутренней ячейки может быть уже занята частью соответствующего ключа. Напротив, последний метод хранения менее эффективен с точки зрения использования газа. Сохранение значения с использованием второго метода равносильно вставке среза без битов данных и единственной ссылкой на значение в первом методе.

dict_set

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";

Устанавливает значение, связанное с key_len-bit key index в словаре dict, в value (ячейки) и возвращает результирующий словарь.

dict_set_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";

Аналогично dict_set, но со значением, заданным как ссылка на value ячейки.

dict_get?

(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";

Ищет ключ index в словаре dict, который использует ключи бит key_len. В случае успеха он извлекает связанное значение как slice и возвращает значение флага -1, чтобы указать на успех. Если поиск не удался, он возвращает (null, 0)​​.

dict_get_ref?

(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_get?, но возвращает первую ссылку найденного значения.

dict_get_ref

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

Вариант dict_get_ref?, который возвращает null вместо значения, если ключ index отсутствует в словаре dict.

dict_set_get_ref

(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";

Устанавливает значение, связанное с index, в value (если value равно null, то вместо этого удаляется ключ) и возвращает старое значение (или null, если значение отсутствовало).

dict_delete?

(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";

Удаляет key_len-битный ключ index из словаря dict. Если ключ присутствует, возвращает измененный словарь dict' и флаг успеха −1. В противном случае возвращает исходный словарь dict и 0.

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";

Удаляет key_len-битный ключ index из словаря dict. Если ключ присутствует, возвращает измененный словарь dict', исходное значение x, связанное с ключом k (представленное срезом), и флаг успеха −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 устанавливает значение, связанное с ключом index в словаре dict, в 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";

Операция replace, похожая на dict_set, но которая устанавливает значение ключа index в словаре dict в value, только если ключ уже присутствовал в dict. Возвращает либо измененную версию словаря и флаг -1, либо (dict, 0).

Аналоги конструктора

Следующие примитивы принимают новое значение в качестве компоновщика вместо среза, что часто удобнее, если значение необходимо сериализовать из нескольких компонентов, вычисленных в стеке. Чистый эффект примерно эквивалентен преобразованию b в срез и выполнению соответствующего примитива, перечисленного выше.

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";

Вычисляет минимальный ключ k в словаре dict, удаляет его и возвращает (dict', k, x, -1), где dict' — это измененная версия dict, а x — это значение, связанное с k. Если словарь пуст, возвращает (dict, null, null, 0).

Обратите внимание, что ключ, возвращаемый idict_delete_get_min, может отличаться от ключа, возвращаемого dict_delete_get_min и udict_delete_get_min.

dict_delete_get_max

(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";

Вычисляет максимальный ключ k в словаре dict, удаляет его и возвращает (dict', k, x, -1), где dict' — это измененная версия dict, а x — это значение, связанное с k. Если словарь пуст, возвращает (dict, null, null, 0).

dict_get_min?

(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";

Вычисляет минимальный ключ k в словаре dict, связанное значение x и возвращает (k, x, -1). Если словарь пуст, возвращает (null, null, 0).

dict_get_max?

(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";

Вычисляет максимальный ключ k в словаре dict, связанное значение x и возвращает (k, x, -1). Если словарь пуст, возвращает (null, null, 0).

dict_get_min_ref?

(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_min? но возвращает единственную ссылку в значении в качестве ссылки.

dict_get_max_ref?

(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_max?, но возвращает единственную ссылку в значении в качестве ссылки.

dict_get_next?

(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";

Вычисляет минимальное значение ключа k в словаре dict, которое больше, чем pivot; возвращает k, соответствующее значение и флаг, указывающий на успех. Если словарь пуст, возвращает (null, null, 0).

dict_get_nexteq?

(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";

Аналогично dict_get_next?, но вычисляет минимальный ключ k, который больше или равен pivot.

dict_get_prev?

(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?, но вычисляет максимальный ключ k, меньший, чем pivot.

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?, но вычисляет максимальный ключ k, меньший или равный pivot.

new_dict

cell new_dict() asm "NEWDICT";

Создает пустой словарь, который на самом деле является значением null. Особый случай null().

dict_empty?

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

Проверяет, является ли словарь пустым. Эквивалентно cell_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). Ищет уникальный префикс среза key, присутствующий в словаре префиксного кода dict. Если он найден, префикс s возвращается как s', а соответствующее значение (также срез) как x. Остаток s возвращается как срез s''. Если ни один префикс s не является ключом в словаре префиксного кода dict, он возвращает неизмененный s и нулевой флаг.

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";

Помечает переменную как используемую, чтобы код, который ее создал, не был удален, даже если он не является нечистым. (например, нечистый спецификатор)

Другие примитивы

мин

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

Вычисляет минимум из двух целых чисел x и y.

max

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

Вычисляет максимум из двух целых чисел x и y.

minmax

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

Сортирует два целых числа.

abs

int abs(int x) asm "ABS";

Вычисляет абсолютное значение целого числа x.