Solidity

Bytes-Utils 切片功能如何工作?

  • February 19, 2022

我對 bytes-utils 庫的 slice 功能非常困惑。我有幾個關於它是如何工作的問題,尤其是關於下面的程式碼。完整的程式碼可以在這裡找到。

  1. 為什麼聲明 tempBytes 然後分配空閒記憶體指針?這樣就不能return(0x40, length)省油嗎?
  2. 為什麼需要 lengthmod 變數?
  3. 什麼是 mc 變數,為什麼它專門乘以 0x20 而不是另一個數字?
  4. for 循環在這裡究竟做了什麼?
  5. 為什麼需要更新空閒記憶體指針?如果不是,那怎麼會發生?

謝謝!


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.) 這裡應用了前面提到的特殊處理。如前所述,我們的字節儲存類似於arraybytes32。為此,我們還儲存bytes. 在記憶體中,我們將其儲存為<32 bytes for length><data (e.g. bytes32 array)>. 如 2) 中所述,程式碼避免使用比所需更多的記憶體,我們從頭開始偏移它(所以從頭開始複製更多)。記憶體中的確切開始儲存在mc和中ccmc是用於切片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增加cc32(例如查看我們的bytes“數組”中的下一個條目)。然後在循環體內複製數據。為此,切片的一部分從源記憶體 ( mload(cc)) 載入,然後儲存在新記憶體 ( mstore(mc, ...)) 中。

編輯:我嘗試在程式碼中添加一些註釋:https ://gist.github.com/rmeissner/76d6345796909ee41fb9f36fdaa4d15f

引用自:https://ethereum.stackexchange.com/questions/122029