// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Test {
uint256 zero // slot 0
uint256 one; // slot 1
uint256 two; // slot 2
uint256 there;// slot 3
struct AAA {
uint256 a;
uint256 b;
bytes c;
}
mapping(uint256 => AAA[]) public ddd; // slot 4
constructor() {
//ddd[2].push(AAA({a: 9, b: 10,c:"string input4"})); //小于32字节的
ddd[2].push(AAA({a: 9, b: 10,c:"string input4 more than 64 hahaha"}));
}
}
c的位置计算是通过一下:
uint256(keccak256(abi.encodePacked(keccak256(abi.encodePacked(2, 4)))) + 2
后边加的2 是通过index 算来的,a的index=0 c就是2了
c 的位置是:115422248451717562185388182079897522298857624646553443070743177396828943826236 值为:bytes32: ret 0x737472696e6720696e707574340000000000000000000000000000000000001a--1a--16转10进制为26--代表的识length(13)*2 =26 1a之前是数据转化完后就是string input4
但是如果大于32字节,c 的位置是:115422248451717562185388182079897522298857624646553443070743177396828943826236 此时存储的就是length(值为33)*2 +1 =67 返回是 bytes32: ret 0x0000000000000000000000000000000000000000000000000000000000000043 低位43 16转10进制为67,这是具体的字符串value存储的位置为uint256(keccak256(abi.encodePacked(slot))); slot 为c的位置115422248451717562185388182079897522298857624646553443070743177396828943826236
然后计算数据存储的位置48107489538665329287718715171855900440650012581208935513126464494439610548754 取对应位置的值:0x737472696e6720696e70757434206d6f7265207468616e203634206861686168 转string为:string input4 more than 64 hahah
知识补给:
bytes 和 string
bytes 和 string 的编码是相同的。 一般来说,编码与 bytes1[] 类似,即有一个槽用于存放数组本身和一个数据区, 这个数据区是用该槽的位置的 keccak256 哈希值计算的。 然而,对于较短的值(短于32字节),数组元素与长度一起存储在同一个槽中。
特别是:如果数据最多只有 31 字节长, 元素被存储在高阶字节中(左对齐),最低阶字节存储值 length * 2。 对于存储数据长度为 32 或更多字节的字节数,主槽 p 存储 length * 2 + 1, 数据照常存储在 keccak256(p)。这意味着您可以通过检查最低位是否被设置来区分短数组和长数组: 短数组(未设置)和长数组(设置)
//0x737472696e6720696e70757434206d6f7265207468616e203634206861686168
function bytes32ToString(bytes32 _bytes32) external pure returns (string memory) {
bytes memory bytesArray = new bytes(32);
for (uint256 i ; i < 32; i++) {
bytesArray[i] = _bytes32[i];
}
return string(bytesArray);
}