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 }