C++代码操作指令的定义

ops/2024/10/24 0:50:50/

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、最简单的函数
  • 二、栈设计
      • StackEntry 类
      • std::vector 容器
      • std::vector<StackEntry>的特点
      • std::vector<StackEntry>的操作
      • 成员枚举 Type:
      • 成员变量:
      • 构造函数和析构函数:
      • 成员函数:


前言

TVM操作代码的分析

一、最简单的函数

麻雀虽小五脏俱全

int exec_swap(VmState* st) {Stack& stack = st->get_stack();VM_LOG(st) << "execute SWAP\n";stack.check_underflow(2);swap(stack[0], stack[1]);return 0;
}int exec_xchg0(VmState* st, unsigned args) {int x = args & 15;Stack& stack = st->get_stack();VM_LOG(st) << "execute XCHG s" << x;stack.check_underflow_p(x);swap(stack[0], stack[x]);return 0;
}

这两个函数是TON虚拟机中用于操作栈的指令,具体功能如下:

  1. exec_swap 函数

    • 这个函数实现了 SWAP 操作码,用于交换虚拟机栈顶的两个元素。
    • 函数首先通过 st->get_stack() 获取当前虚拟机状态的栈引用。
    • 使用 VM_LOG(st) 记录执行的操作,便于调试。
    • stack.check_underflow(2) 检查栈中是否至少有两个元素,如果没有,将抛出异常。
    • 使用 swap(stack[0], stack[1]) 交换栈顶两个元素的值。
    • 函数返回 0,表示操作成功执行。
  2. exec_xchg0 函数

    • 这个函数实现了 XCHG 操作码的一个变种,用于交换栈顶元素与栈中指定位置元素的值。
    • unsigned args 参数表示操作码的参数,用于确定要交换的栈中的位置。
    • int x = args & 15; 通过位运算提取参数的低四位,因为 XCHG 操作码的参数范围是0到15,对应栈中的索引0到15。
    • 函数同样首先获取栈引用,并记录执行的操作。
    • stack.check_underflow_p(x) 检查栈中是否至少有 x+1 个元素(因为要交换栈顶元素和位置 x 的元素,所以需要至少 x+1 个元素),如果没有,将抛出异常。
    • 使用 swap(stack[0], stack[x]) 交换栈顶元素和位置 x 的元素的值。
    • 函数返回 0,表示操作成功执行。

在计算机编程中,位运算是一种处理数据的方法,它允许我们直接操作数字的二进制表示。在这段代码中,int x = args & 15;这行代码使用了位运算来提取参数args的低四位。

  1. 位掩码

    • 15这个数字在二进制中表示为1111。这是一个4位的掩码,用于从另一个数字中提取低四位。
  2. 位与运算(AND)

    • &是位与运算符。当它用于两个数字时,它会对这两个数字的二进制表示进行按位与操作。结果中的每一位都是1,仅当两个操作数中相应的位都是1时。
  3. 提取低四位

    • 当你用args变量与掩码15(二进制1111)进行位与运算时,args的高位数(即除了低四位之外的所有位)都会与掩码中的0相与,结果为0。而args的低四位会与掩码中的1相与,保持不变。
    • 因此,这个操作实际上“掩蔽”了args的所有高位数,只留下了低四位。

例如,如果args的值是0b10111001(二进制表示,十进制中的179),那么:

  • args的二进制表示:10111001
  • 掩码15的二进制表示:00001111

进行位与运算:

  10111001 (args)
& 00001111 (掩码15)
----------00001001 (结果,即9)

只有args的低四位被保留在结果中,这就是为什么x的值是9

这种技术常用于解码操作码或参数,其中只有某些位是有意义的。在这个特定的虚拟机实现中,它用于确定XCHG操作码要交换栈中哪个元素与栈顶元素。

二、栈设计

std::vector<StackEntry> 是一个使用 C++ 标准模板库(STL)中的 std::vector 容器来存储 StackEntry 对象的动态数组。让我们逐步分解这个类型,以更好地理解其组成和行为。

StackEntry 类

首先,StackEntry 类是 Stack 类中定义的一个结构,它表示虚拟机栈中的一个元素。这个类非常灵活,可以存储多种类型的数据,这由其 Type 枚举和 RefAny 类型的 ref 成员变量决定。StackEntry 支持的数据类型包括整数、单元格(cell)、构建器(builder)、切片(slice)、虚拟机继续(vmcont)、元组(tuple)、栈(stack)、字符串、字节串、位字符串、盒子(box)、原子(atom)和对象。

std::vector 容器

std::vector 是 C++ STL 中的一个序列容器,它封装了一个可以动态改变大小的数组。与静态数组相比,std::vector 提供了更多的功能,例如动态内存管理、随机访问、以及在容器末尾快速添加或删除元素的能力。

std::vector的特点

  • 动态数组std::vector<StackEntry> 允许在运行时动态地增加或减少 StackEntry 对象的数量。这意味着你可以根据需要向栈中添加或移除元素,而不需要事先知道栈的最终大小。

  • 随机访问:作为序列容器,std::vector 提供了对元素的快速随机访问能力,这意味着你可以通过下标快速访问 std::vector<StackEntry> 中的任何 StackEntry 对象。

  • 内存连续std::vector 存储的元素在内存中是连续的,这有助于提高缓存的效率,并允许使用指针和迭代器来访问元素。

  • 可调整大小std::vector 在需要时会自动调整其容量。当添加的元素超过当前容量时,std::vector 会分配一块新的更大的内存区域,并将所有元素移动到新的位置。

  • 异常安全std::vector 提供了一定的异常安全性保证。例如,在分配新内存失败时,它不会泄漏资源。

std::vector的操作

  • 添加元素:可以使用 push_back() 方法在 std::vector<StackEntry> 的末尾添加新的 StackEntry 对象。

  • 移除元素:可以使用 pop_back() 方法移除 std::vector<StackEntry> 末尾的 StackEntry 对象。

  • 访问元素:可以通过下标操作符 []at() 方法访问 std::vector<StackEntry> 中的 StackEntry 对象。

  • 检查大小:可以使用 size() 方法获取 std::vector<StackEntry> 中元素的数量。

  • 清空容器:可以使用 clear() 方法移除 std::vector<StackEntry> 中的所有元素。

  • 内存管理std::vector 自动管理其存储的内存,但你也可以通过调用 reserve() 方法预分配内存,以提高大量插入操作的效率。

std::vector<StackEntry> 结合了 StackEntry 类的灵活性和 std::vector 容器的动态特性,为虚拟机提供了一个强大且灵活的栈实现。这种设计使得虚拟机可以高效地管理操作数,同时能够适应各种不同的数据类型和操作需求。

StackEntry 类是 TON 虚拟机中用于表示虚拟机栈中的一个元素的类。它是一个多功能的数据结构,可以存储不同类型的数据,这使得它非常适合作为虚拟机操作数栈的元素。下面是对 StackEntry 类的详细解释:

成员枚举 Type:

StackEntry 类定义了一个名为 Type 的枚举,列出了 StackEntry 可以存储的所有可能的数据类型:

  • t_null:空值,表示 StackEntry 不包含任何数据。
  • t_int:整数类型。
  • t_cell:单元格引用类型,用于引用虚拟机中的单元格数据结构
  • t_builder:构建器引用类型,用于构建或修改单元格。
  • t_slice:切片引用类型,用于表示单元格的一部分。
  • t_vmcont:虚拟机继续类型,可能用于表示虚拟机的执行状态。
  • t_tuple:元组类型,用于存储一组 StackEntry 对象。
  • t_stack:栈类型,用于存储另一个栈。
  • t_string:字符串类型。
  • t_bytes:字节串类型,用于存储二进制数据。
  • t_bitstring:位字符串类型,用于存储位序列。
  • t_box:盒子类型,可能用于存储某种封装的数据。
  • t_atom:原子类型,可能用于表示不可分割的数据单元。
  • t_object:对象类型,用于存储自定义对象。

成员变量:

  • RefAny ref:一个引用任何类型的能力,它实际存储 StackEntry 的数据。
  • Type tp:表示 StackEntry 当前存储的数据类型。

构造函数和析构函数:

  • 默认构造函数初始化一个空的 StackEntry
  • 析构函数清理 StackEntry 使用的资源。
  • 多个构造函数允许从不同类型的数据创建 StackEntry 对象。
  • 拷贝构造函数和移动构造函数支持复制和移动 StackEntry 对象。
  • 赋值运算符和移动赋值运算符支持赋值 StackEntry 对象。

成员函数:

  • clear:清除 StackEntry 中的数据,将其设置为空值。
  • set_int:设置 StackEntry 存储的整数数据。
  • emptyis_tupleis_atomis_intis_cellis_nullis:这些函数检查 StackEntry 当前存储的数据类型。
  • is_list:检查 StackEntry 是否表示一个列表(在虚拟机中可能表示为特定的数据结构)。
  • swap:交换当前 StackEntry 对象与另一个对象的内容。
  • operator==operator!=:比较两个 StackEntry 对象是否相等或不相等。
  • type:返回 StackEntry 当前存储的数据类型。

StackEntry 类的设计使其成为一个非常灵活和强大的构建块,用于构建虚拟机的栈。通过支持多种数据类型,它可以处理各种操作和计算,这是执行虚拟机指令集所必需的。此外,它的设计允许高效地存储和操作数据,这对于性能至关重要。

StackEntry 类是虚拟机中用于表示操作数栈中的一个元素的类。

  1. 默认构造函数

    StackEntry() : ref(), tp(t_null) {
    }
    

    这个构造函数创建了一个默认的 StackEntry 对象,将其类型设置为 t_null,表示该栈条目目前不包含任何数据。ref 成员被默认初始化,此时不引用任何对象。

  2. 析构函数

    ~StackEntry() {
    }
    

    StackEntry 类的析构函数。由于 ref 成员变量是智能指针,它会在 StackEntry 对象销毁时自动释放其管理的资源,因此析构函数中不需要执行额外的清理工作。

  3. 从单元格引用构造

    StackEntry(Ref<Cell> cell_ref) : ref(std::move(cell_ref)), tp(t_cell) {
    }
    

    这个构造函数接收一个 Cell 的引用,并将其移动到 ref 成员变量中,同时将类型设置为 t_cell。这表示该栈条目现在持有一个单元格引用。

  4. 从单元格构建器引用构造

    StackEntry(Ref<CellBuilder> cb_ref) : ref(std::move(cb_ref)), tp(t_builder) {
    }
    

    类似于单元格引用,这个构造函数接收一个 CellBuilder 的引用,将其移动到 ref 成员变量中,并将类型设置为 t_builder,表示该栈条目现在持有一个单元格构建器引用。

  5. 从单元格切片引用构造

    StackEntry(Ref<CellSlice> cs_ref) : ref(std::move(cs_ref)), tp(t_slice) {
    }
    

    这个构造函数接收一个 CellSlice 的引用,将其移动到 ref 成员变量中,并将类型设置为 t_slice,表示该栈条目现在持有一个单元格切片引用。

  6. 从整数引用构造

    StackEntry(td::RefInt256 int_ref) : ref(std::move(int_ref)), tp(t_int) {
    }
    

    这个构造函数接收一个整数的引用,将其移动到 ref 成员变量中,并将类型设置为 t_int,表示该栈条目现在持有一个整数。

  7. 从字符串引用构造

    StackEntry(Ref<Cnt<std::string>> str_ref, bool bytes = false): ref(std::move(str_ref)), tp(bytes ? t_bytes : t_string) {
    }
    

    这个构造函数接收一个字符串的引用,并根据 bytes 参数的值将类型设置为 t_stringt_bytes,表示该栈条目现在持有一个字符串引用。如果 bytestrue,则类型设置为 t_bytes,表示字符串应该被当作字节序列处理。

  8. 直接从字符串构造

    StackEntry(std::string str, bool bytes = false) : ref(), tp(bytes ? t_bytes : t_string) {ref = Ref<Cnt<std::string>>{true, std::move(str)};
    }
    

    这个构造函数直接接收一个 std::string 对象,并根据 bytes 参数的值将类型设置为 t_stringt_bytes。与前一个构造函数不同的是,它直接接收字符串数据而不是引用,并将这个字符串数据移动到一个新的 Cnt<std::string> 对象中,然后由 ref 成员变量管理。

这些构造函数提供了多种方式来创建 StackEntry 对象,并初始化其 reftp 成员变量,以确保栈条目能够安全地存储和管理不同类型的数据。
在您提供的 StackEntry 类的构造函数中,参数 bool bytes = false 是一个默认参数。这个参数在两个构造函数中出现:

  1. StackEntry(Ref<Cnt<std::string>> str_ref, bool bytes = false)
  2. StackEntry(std::string str, bool bytes = false)

这个参数的作用是指示传入的字符串 (std::string) 是否应该被当作字节序列(bytes)处理。默认值 false 表示字符串不被当作字节序列处理,而是作为普通的文本字符串。

  • bool bytes = false:这是一个构造函数的参数,它允许调用者指定字符串是否应该被视为原始字节数据。这个参数的默认值是 false,意味着如果不明确指定,字符串将被当作普通字符串处理。

  • bytesfalse

    • 字符串被视为普通的文本数据,可能会进行编码转换、字符处理等操作。
    • 例如,如果字符串是 UTF-8 编码的文本,它将被当作文本处理。
  • bytestrue

    • 字符串被视为原始的字节序列,不会进行任何字符编码或解码处理。
    • 这通常用于处理二进制数据,例如文件内容、网络传输的数据等。

假设我们有一个字符串 “Hello, World!”,我们想将它存储在 StackEntry 中:

  • 如果我们不关心它是文本还是字节序列,可以简单地使用默认构造函数:

    StackEntry entry(std::string("Hello, World!"));
    

    这里 bytes 默认为 false,所以 entry 将包含一个普通的字符串。

  • 如果我们知道 “Hello, World!” 实际上是一个二进制数据的一部分(例如,它可能是一个编码后的协议消息),我们可以这样创建 StackEntry

    StackEntry entry(std::string("Hello, World!"), true);
    

    这里 bytes 被设置为 true,所以 entry 将这个字符串当作字节序列处理。

通过这种方式,StackEntry 类提供了灵活性,允许开发者根据需要处理字符串数据,无论是作为普通文本还是作为字节序列。


http://www.ppmy.cn/ops/127961.html

相关文章

RabbitMQ service is already present - only updating service parameters

Windows下卸载RabbitMQ之后,然后重新注册RabbitMQ服务的时候,报错以下信息: D:\software\rabbitmq-server-4.0.2\rabbitmq_server-4.0.2\sbin>D:\software\rabbitmq-server-4.0.2\rabbitmq_server-4.0.2\sbin\rabbitmq-service.bat install RabbitMQ service is already …

【Jmeter】jmeter运行时自动获取线程号几种方式

背景&#xff1a; 在模拟多线程场景时&#xff0c;使用序号可以标记并追踪当前执行请求的虚拟用户。 方法1&#xff1a;通过添加BeanShell前置处理器获取或BeanShell预处理程序获取线程号 获取函数&#xff1a;${__BeanShell(ctx.getThread().getThreadName())} Thread.slee…

②PROFINET转ModbusTCP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 PROFINET 转 Modbus TCP &#xff08;接上一章&#xff09; 配置使用 与 PROFINET 主站进行组态说明 这里介绍与西门子 PLC 的…

Godot中类和静态类型

目录 类 关键字class_name 除了为类定义方法&#xff0c;我们也可以为类定义属性字段 实例释放前后的打印 Refcounted RefCounted维护了一个引用计数器 get_reference_count 类是引用类型数据 class关键字 静态类型 静态方法 静态方法只能访问静态变量 类 是面向…

“摄像机”跟随及攻击抖动实现

学习Unity的摄像机功能&#xff0c;可以帮助我们实现摄像机对人物的跟随移动&#xff0c;还可以使用这个工具自带的插件&#xff0c;摄像机震动&#xff0c;颤动&#xff0c;增强打击感&#xff1b; 首先来安装一下这个插件&#xff0c;window菜单--packageManage--左上角Unit…

数据结构 - 树,初探

树是一种非线性数据结构&#xff0c;是以分支关系定义的层次结构&#xff0c;因此形态上和自然界中的倒挂的树很像&#xff0c;而数据结构中树根向上树叶向下。 什么是树&#xff1f; 01定义 树是由n&#xff08;n>0&#xff09;个元素节点组成的有限集合&#xff0c;当n0时…

Redis学习笔记:整数集合

概述 整数集合&#xff08;intset&#xff09;是集合键的底层实现之一&#xff0c;当一个集合只包含整数值元素&#xff0c;并且这个集合的元素数量不多时&#xff0c;Redis就会使用整数集合作为集合键的底层实现。它可以保存类型为int16_t、int32_t或者int64_t的整数值&#…

MeshXL: Neural Coordinate Field forGenerative 3D Foundation Models 论文解读

目录 一、概述 二、相关工作 1、3D表示 2、3D生成 3、三维基础模型 三、NeurCF 四、MeshXL 1、流程 2、损失 五、数据集 六、实验 1、模型参数量 2、 不同模型对比 3、Mesh生成的多样性 4、文本或图像到网格的生成 5、纹理生成 一、概述 该论文介绍了一个生成式…