Bytes-Utils 切片功能如何工作?
我對 bytes-utils 庫的 slice 功能非常困惑。我有幾個關於它是如何工作的問題,尤其是關於下面的程式碼。完整的程式碼可以在這裡找到。
- 為什麼聲明 tempBytes 然後分配空閒記憶體指針?這樣就不能
return(0x40, length)
省油嗎?- 為什麼需要 lengthmod 變數?
- 什麼是 mc 變數,為什麼它專門乘以 0x20 而不是另一個數字?
- for 循環在這裡究竟做了什麼?
- 為什麼需要更新空閒記憶體指針?如果不是,那怎麼會發生?
謝謝!
bytes memory tempBytes; assembly { tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } return tempbytes;
相當多的問題,我不得不說原始程式碼並沒有很好的評論。
1.) 彙編程式碼遵循 Solidity 的記憶體佈局/行為(請參閱記憶體中的 Solidity )。空閒記憶體指針 (aka
0x40
) 包含一個指向空閒記憶體區域開始的指針。通過將其載入到 中tempBytes
,表明此變數的內容儲存在此位置。這類似於其他基於指針的語言中的記憶體指針。5.) 最後,空閒記憶體指針會增加到指向 後面
tempBytes
,這樣新的記憶體將由其他 Solidity 程式碼分配到正確的區域。如果您不更新空閒記憶體指針,新的記憶體分配將覆蓋您的切片。2.)
lengthmod
是長度為 32 的模組。這是必需的,因為記憶體是按字讀取的。1 個字等於 32 個字節(十六進制的 0x20)。因此,很多操作都是基於 32 個步驟。如果您考慮 Solidity 中的 a ,您可以將其想像為串聯bytes
的數組。bytes32
因此,如果您處理bytes
長度不是 32 的倍數的情況,則必須應用一些特殊邏輯。在彙編中計算 32 的模數可以用 來完成,and(_length, 31)
然後將其儲存在lengthmod
.該程式碼希望避免使用比所需更多的記憶體。由於在讀取和寫入記憶體時一切都發生在 32 字節操作中,因此程式碼會偏移開頭以避免超出結尾,以防切出的數量不是 32 的倍數(這對 3 很重要)。
3.) 這裡應用了前面提到的特殊處理。如前所述,我們的字節儲存類似於
array
bytes32。為此,我們還儲存bytes
. 在記憶體中,我們將其儲存為<32 bytes for length><data (e.g. bytes32 array)>
. 如 2) 中所述,程式碼避免使用比所需更多的記憶體,我們從頭開始偏移它(所以從頭開始複製更多)。記憶體中的確切開始儲存在mc
和中cc
。mc
是用於切片cc
的記憶體和用於原始數據的記憶體。如果
lengthmod
為 0,則不需要偏移記憶體區域的開始。在這種情況下,添加了 32(十六進制中的 0x20),以便將數據寫入長度為bytes
.4.) for 循環的語法是
for {<init_block>} <condition_block> {<post_iteration_block>} {<body_block>}
. 如前所述,bytes
它們就像一個數組bytes32
。然後在切片時bytes
迭代這個“數組”並將切片數據複製到一個新的“數組”(tempBytes
)。mc
是新數組cc
的索引,是源數組的索引。Firstcc
被初始化為指向應該被切片的數據的開始。在每次迭代中,檢查是否已經複製了足夠的數據。每次迭代後mc
增加cc
32(例如查看我們的bytes
“數組”中的下一個條目)。然後在循環體內複製數據。為此,切片的一部分從源記憶體 (mload(cc)
) 載入,然後儲存在新記憶體 (mstore(mc, ...)
) 中。編輯:我嘗試在程式碼中添加一些註釋:https ://gist.github.com/rmeissner/76d6345796909ee41fb9f36fdaa4d15f