Solidity memory layout introduction 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 (scratch space) for hashing methods The scratch space of the Hash method.

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

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

Solidity reserves four 32-byte slots, and the specific byte ranges (including endpoints) are as follows:

0x00-0x3f (0-63) (64 bytes): Temporary storage space for the hash method

0x5f (64-95) (32 bytes): currently allocated memory size (also known as free memory pointer) 0x40-

0x60-0x7f (96-127) (32 bytes): Zero slot, the value used for memory array initialization, 0 slot should not be written.

0x80 (>128) The storage start position of the data pointed by the free pointer. 0x80 (the position where the 5th slot starts and the 4th slot ends.)

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).

Scratch space can be used between statements (e.g. inside inline assembly). Zero slots are used for initial values ​​of dynamic memory arrays and should never be written. (The free pointer originally points to the location of 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:

Contract state variables are stored in the storeage in a compact way, for example, multiple values ​​are stored in one slot at the same time. In addition to dynamic size arrays and mapping, state variable data data starts from slot 0 and continues to be stored in slots one by one. For each variable, the length of data bytes is determined by the variable type. Variable groups with multiple continuous variable data items and less than 32 bytes in length will be packed into a single storage slot. Follow the rules below:

  • The first item in a storage slot is stored lower-order aligned. 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. Value types only occupy enough bytes of data space 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.

  • Items following struct or array data always start a new storage slot. Items following struct or array data always start a new storage slot.

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.

For contracts using inheritance, the order of state variables is determined by the C3 linearized contract order, which starts with the most basic contract. State variables from different contracts will share the same storage slot if allowed by the above rules.

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.

In short, in order to optimize the EVM to the greatest extent, make sure that the order of the storage variables and struct members you try to declare can make these variables compactly packaged and stored. For example: state variable declaration order, uint128, uint128, uint256; uint128, uint256, uint128. The former occupies 2 slots and the latter occupies 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.

Because of unpredictable data sizes, mapping and dynamically sized arrays cannot be stored between, before or after state variable data. They only occupy individual 32-byte slots, depending on the rules above. Their elements are stored in a different slot, depending on the result computed with the Keccak-256 algorithm.

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. Assuming the storage locations of mapping and array, after applying the storage layout rules, There is a p slot. For dynamic arrays, this slot stores the number of elements in the array. (Dynamic-sized byte arrays and strings are an exception). For mappings, this slot remains empty, but mappings need to ensure that even with nested mappings, their contents end up in a different storage location. That is, mapping and    dynamic Arrays, strings, and bytes need at least two slots to store. The first slot, called the main slot, stores the data of the data structure itself, such as the length and the address of the nested structure. This slot is obtained according to the memory allocation of the EVM system. The other slot, according to the hash algorithm, the main slot gets the data address.

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:

The value corresponding to the key k of mapping is stored inkeccak256(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. If the key is a value type, h will pad to 32 bytes. 

  • for strings and byte arrays,  h computes the  keccak256 hash of the unpadded data. If the key is string or byte array, h will calculate the unpadded hash value, 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.

If the value of the mapping is a non-value type, the calculated slot marks the starting position of the data. If the value of the mapping is a struct type, you have to add an offset to correspond to the element position in the structure.

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 and string are the same encoding rules. Generally speaking, the encoding rules are the same as bytes1[], there is a slot p dedicated to the array itself, and the storage position of the data is obtained by calculating the position of p through hash, which is obtained by keccak256(p). However, for a short value ( less than 32 bytes) the array elements and the length of the array are stored together in the same slot.

Cleaning Up Variables

In the EVM, eventually all values ​​are stored in 256-bit slots. In some cases (in some cases), some value types are less than 256 bits, which requires cleaning the remaining bits. If not cleaned, the remaining bits may not be 0, which will affect the final stored value or confuse the value storage. It means that the value you store may lose its fidelity.

Guess you like

Origin blog.csdn.net/gridlayout/article/details/131433165