Solidity内存布局介绍 Layout in Memory and Storage

Solidity reserves four 32-byte slots, with specific byte ranges (inclusive of endpoints) being used as follows:

0x00 - 0x3f (64 bytes): scratch space(暂存空间) for hashing methods  Hash方法的暂存空间.

0x40 - 0x5f (32 bytes): currently allocated memory size (aka. free memory pointer)

0x60 - 0x7f (32 bytes): zero slot

Solidity保留了四个32字节的插槽,具体的字节范围(包括端点)如下所示:

0x00-0x3f  (0-63)(64字节):哈希方法的暂存空间

0x5f (64-95)(32字节):当前分配的内存大小(也称为空闲内存指针)0x40-

0x60-0x7f (96-127)(32字节):零插槽,用于内存数组初始化的值,  0插槽不应该被写入东西.

0x80 (>128) 空闲指针所指向数据的存储开始位置. 0x80 (第5个槽位开始的位置,第4个槽位结束的位置.)

Scratch space can be used between statements (i.e. within inline assembly). The zero slot is used as initial value for dynamic memory arrays and should never be written to (the free memory pointer points to 0x80 initially).

暂存空间可以在statement之间被使用(比如内联汇编内部). zero 卡槽 被用于动态内存数组的初始值,永远不应该被写。  (空闲指针最原始指向0x80, 128的位置.)

Layout of State Variables in Storage

State variables of contracts are stored in storage in a compact way such that multiple values sometimes use the same storage slot. Except for dynamically-sized arrays and mappings (see below), data is stored contiguously item after item starting with the first state variable, which is stored in slot 0. For each variable, a size in bytes is determined according to its type. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:

合约状态变量以一种紧凑性方式存储在storeage中, 比如多个values同时存储在一个slot中。 除了动态的尺寸的数组和mapping, 状态变量数据data从0插槽开始,一个接一个持续的存储到插槽. 对于每一个变量,数据字节的长度由变量类型来确定。 多个连续的变量数据项并且小于32字节长度的变量群,会被打包到一个单独的存储插槽中. 遵守以下规则:

  • The first item in a storage slot is stored lower-order aligned. 第一个数据,在存储插槽中, 低位对齐.

  • Value types use only as many bytes as are necessary to store them. 值类型仅仅占用足够存储他们的数据空间字节数就行了.

  • If a value type does not fit the remaining part of a storage slot, it is stored in the next storage slot.  如果插槽剩余的字节空间不足以存储值类型数据,则它会被存储在下一个插槽中.

  • Structs and array data always start a new slot and their items are packed tightly according to these rules.   struct 和 array的数据总是单独存储一个插槽,它们的内部项会依照这个规则紧凑的打包存储

  • Items following struct or array data always start a new storage slot. 在struct和array之后的存储的数据项,总是会开启一个新的存储插槽.

For contracts that use inheritance, the ordering of state variables is determined by the C3-linearized order of contracts starting with the most base-ward contract. If allowed by the above rules, state variables from different contracts do share the same storage slot.

对于使用继承的契约,状态变量的顺序由C3线性化的契约顺序决定,该顺序从最基本的合约开始。如果上述规则允许,来自不同合同的状态变量将共享同一存储槽。

Finally, in order to allow the EVM to optimize for this, ensure that you try to order your storage variables and struct members such that they can be packed tightly. For example, declaring your storage variables in the order of uint128, uint128, uint256 instead of uint128, uint256, uint128, as the former will only take up two slots of storage whereas the latter will take up three.

总之,为了最大的使EVM优化, 确保你尝试声明的存储变量和struct 成员的顺序,可以使得这些变量可以紧致的打包存放.  比如:状态变量声明顺序, uint128,uint128,uint256;  uint128,uint256,uint128.   前者占用2个插槽, 后者占用了3个.

Mappings and Dynamic Arrays

Due to their unpredictable size, mappings and dynamically-sized array types cannot be stored “in between” the state variables preceding and following them. Instead, they are considered to occupy only 32 bytes with regards to the rules above and the elements they contain are stored starting at a different storage slot that is computed using a Keccak-256 hash.

因为不可预测的数据大小, mapping和动态尺寸的数组,不能够存储在状态变量数据之间,之前或之后. 它们仅仅占据独立的32字节插槽,依赖上面的规则。 它们的元素存储在一个不同的插槽中,取决于用Keccak-256算法计算结果.

Assume the storage location of the mapping or array ends up being a slot p after applying the storage layout rules. For dynamic arrays, this slot stores the number of elements in the array (byte arrays and strings are an exception, see below). For mappings, the slot stays empty, but it is still needed to ensure that even if there are two mappings next to each other, their content ends up at different storage locations. 假设mapping和array的存储位置,在应用了存储布局规则后,存在一个p插槽. 对于动态数组,这个插槽存储数组的元素个数。(动态尺寸的字节数组和string是一个exception). 对于mapping,这个插槽保持为空, 但是mapping扔需要确保即使嵌套mapping,他们的内容最终会在一个不同的存储位置.    就是mapping和动态数组,string,bytes 需要至少两个slot去存储,第一个slot,称为主slot,存储数据结构本身的数据,比如长度,嵌套的结构地址,这个slot根据EVM系统内存分配获取.  另一个slot,根据hash算法,主slot得到数据地址.

The value corresponding to a mapping key k is located at keccak256(h(k) . p) where . is concatenation and h is a function that is applied to the key depending on its type:

mapping 的key   k对应的值,存储位置在keccak256(h(k) . p),  Hashmap的key对应的value的存储位置,是通过计算key和hash本身slot位置的Hash获得.

  • for value types, h pads the value to 32 bytes in the same way as when storing the value in memory.  如果key是值类型,h则会补位到32字节. 

  • for strings and byte arrays, h computes the keccak256 hash of the unpadded data.如果key是string或byte array,  h 会计算未补位的hash值, keccak256.

If the mapping value is a non-value type, the computed slot marks the start of the data. If the value is of struct type, for example, you have to add an offset corresponding to the struct member to reach the member.

如果mapping的值是非值类型,则计算的插槽标记数据的开始位置. 如果mapping的值是struct type,  你不得不添加一个offset去对应到结构里面的元素位置.

As an example, consider the following contract:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;


contract C {
    struct S { uint16 a; uint16 b; uint256 c; }
    uint x;   // slot 0
    mapping(uint => mapping(uint => S)) data; // slot 1
}

Let us compute the storage location of data[4][9].c. The position of the mapping itself is 1 (the variable x with 32 bytes precedes it). This means data[4] is stored at keccak256(uint256(4) . uint256(1)). The type of data[4] is again a mapping and the data for data[4][9] starts at slot keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))). The slot offset of the member c inside the struct S is 1 because a and b are packed in a single slot. This means the slot for data[4][9].c is keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1. The c type of the value  is uint256, so it uses a single slot.

bytes and string (已经理解)

bytes and string are encoded identically. In general, the encoding is similar to bytes1[], in the sense that there is a slot for the array itself and a data area that is computed using a keccak256 hash of that slot’s position. However, for short values (shorter than 32 bytes) the array elements are stored together with the length in the same slot.

bytes 和 string 是相同的编码规则。一般来说, 编码规则和bytes1[]是一样的,有一个专门为数组本身存储的插槽p, 数据的存储位置通过hash 计算p的位置,keccak256(p)得到.  然而, 对于一个短值(小于32字节)数组元素和数组的长度一起存储在同一个插槽里.

Cleaning Up Variables

在EVM中,最终所有的值都会存储在256位的插槽里面。在某些情况下(in some case), 有的值类型小于256位,  这就需要把剩余位给清理了.  如果不清理,剩余位可能不是0, 会影响最终存储的值或混淆值存储. 意思你存储的值,可能失去保真了.

猜你喜欢

转载自blog.csdn.net/gridlayout/article/details/131433165