github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/libraries/Bytes.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity ^0.8.0;
     3  
     4  /// @title Bytes
     5  /// @notice Bytes is a library for manipulating byte arrays.
     6  library Bytes {
     7      /// @custom:attribution https://github.com/GNSPS/solidity-bytes-utils
     8      /// @notice Slices a byte array with a given starting index and length. Returns a new byte array
     9      ///         as opposed to a pointer to the original array. Will throw if trying to slice more
    10      ///         bytes than exist in the array.
    11      /// @param _bytes Byte array to slice.
    12      /// @param _start Starting index of the slice.
    13      /// @param _length Length of the slice.
    14      /// @return Slice of the input byte array.
    15      function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
    16          unchecked {
    17              require(_length + 31 >= _length, "slice_overflow");
    18              require(_start + _length >= _start, "slice_overflow");
    19              require(_bytes.length >= _start + _length, "slice_outOfBounds");
    20          }
    21  
    22          bytes memory tempBytes;
    23  
    24          assembly {
    25              switch iszero(_length)
    26              case 0 {
    27                  // Get a location of some free memory and store it in tempBytes as
    28                  // Solidity does for memory variables.
    29                  tempBytes := mload(0x40)
    30  
    31                  // The first word of the slice result is potentially a partial
    32                  // word read from the original array. To read it, we calculate
    33                  // the length of that partial word and start copying that many
    34                  // bytes into the array. The first word we copy will start with
    35                  // data we don't care about, but the last `lengthmod` bytes will
    36                  // land at the beginning of the contents of the new array. When
    37                  // we're done copying, we overwrite the full first word with
    38                  // the actual length of the slice.
    39                  let lengthmod := and(_length, 31)
    40  
    41                  // The multiplication in the next line is necessary
    42                  // because when slicing multiples of 32 bytes (lengthmod == 0)
    43                  // the following copy loop was copying the origin's length
    44                  // and then ending prematurely not copying everything it should.
    45                  let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
    46                  let end := add(mc, _length)
    47  
    48                  for {
    49                      // The multiplication in the next line has the same exact purpose
    50                      // as the one above.
    51                      let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
    52                  } lt(mc, end) {
    53                      mc := add(mc, 0x20)
    54                      cc := add(cc, 0x20)
    55                  } { mstore(mc, mload(cc)) }
    56  
    57                  mstore(tempBytes, _length)
    58  
    59                  //update free-memory pointer
    60                  //allocating the array padded to 32 bytes like the compiler does now
    61                  mstore(0x40, and(add(mc, 31), not(31)))
    62              }
    63              //if we want a zero-length slice let's just return a zero-length array
    64              default {
    65                  tempBytes := mload(0x40)
    66  
    67                  //zero out the 32 bytes slice we are about to return
    68                  //we need to do it because Solidity does not garbage collect
    69                  mstore(tempBytes, 0)
    70  
    71                  mstore(0x40, add(tempBytes, 0x20))
    72              }
    73          }
    74  
    75          return tempBytes;
    76      }
    77  
    78      /// @notice Slices a byte array with a given starting index up to the end of the original byte
    79      ///         array. Returns a new array rathern than a pointer to the original.
    80      /// @param _bytes Byte array to slice.
    81      /// @param _start Starting index of the slice.
    82      /// @return Slice of the input byte array.
    83      function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) {
    84          if (_start >= _bytes.length) {
    85              return bytes("");
    86          }
    87          return slice(_bytes, _start, _bytes.length - _start);
    88      }
    89  
    90      /// @notice Converts a byte array into a nibble array by splitting each byte into two nibbles.
    91      ///         Resulting nibble array will be exactly twice as long as the input byte array.
    92      /// @param _bytes Input byte array to convert.
    93      /// @return Resulting nibble array.
    94      function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) {
    95          bytes memory _nibbles;
    96          assembly {
    97              // Grab a free memory offset for the new array
    98              _nibbles := mload(0x40)
    99  
   100              // Load the length of the passed bytes array from memory
   101              let bytesLength := mload(_bytes)
   102  
   103              // Calculate the length of the new nibble array
   104              // This is the length of the input array times 2
   105              let nibblesLength := shl(0x01, bytesLength)
   106  
   107              // Update the free memory pointer to allocate memory for the new array.
   108              // To do this, we add the length of the new array + 32 bytes for the array length
   109              // rounded up to the nearest 32 byte boundary to the current free memory pointer.
   110              mstore(0x40, add(_nibbles, and(not(0x1F), add(nibblesLength, 0x3F))))
   111  
   112              // Store the length of the new array in memory
   113              mstore(_nibbles, nibblesLength)
   114  
   115              // Store the memory offset of the _bytes array's contents on the stack
   116              let bytesStart := add(_bytes, 0x20)
   117  
   118              // Store the memory offset of the nibbles array's contents on the stack
   119              let nibblesStart := add(_nibbles, 0x20)
   120  
   121              // Loop through each byte in the input array
   122              for { let i := 0x00 } lt(i, bytesLength) { i := add(i, 0x01) } {
   123                  // Get the starting offset of the next 2 bytes in the nibbles array
   124                  let offset := add(nibblesStart, shl(0x01, i))
   125                  // Load the byte at the current index within the `_bytes` array
   126                  let b := byte(0x00, mload(add(bytesStart, i)))
   127  
   128                  // Pull out the first nibble and store it in the new array
   129                  mstore8(offset, shr(0x04, b))
   130                  // Pull out the second nibble and store it in the new array
   131                  mstore8(add(offset, 0x01), and(b, 0x0F))
   132              }
   133          }
   134          return _nibbles;
   135      }
   136  
   137      /// @notice Compares two byte arrays by comparing their keccak256 hashes.
   138      /// @param _bytes First byte array to compare.
   139      /// @param _other Second byte array to compare.
   140      /// @return True if the two byte arrays are equal, false otherwise.
   141      function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) {
   142          return keccak256(_bytes) == keccak256(_other);
   143      }
   144  }