Bytes.sol 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.2.0) (utils/Bytes.sol)
  3. pragma solidity ^0.8.24;
  4. import {Math} from "./math/Math.sol";
  5. /**
  6. * @dev Bytes operations.
  7. */
  8. library Bytes {
  9. /**
  10. * @dev Forward search for `s` in `buffer`
  11. * * If `s` is present in the buffer, returns the index of the first instance
  12. * * If `s` is not present in the buffer, returns type(uint256).max
  13. *
  14. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf[Javascript's `Array.indexOf`]
  15. */
  16. function indexOf(bytes memory buffer, bytes1 s) internal pure returns (uint256) {
  17. return indexOf(buffer, s, 0);
  18. }
  19. /**
  20. * @dev Forward search for `s` in `buffer` starting at position `pos`
  21. * * If `s` is present in the buffer (at or after `pos`), returns the index of the next instance
  22. * * If `s` is not present in the buffer (at or after `pos`), returns type(uint256).max
  23. *
  24. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf[Javascript's `Array.indexOf`]
  25. */
  26. function indexOf(bytes memory buffer, bytes1 s, uint256 pos) internal pure returns (uint256) {
  27. uint256 length = buffer.length;
  28. for (uint256 i = pos; i < length; ++i) {
  29. if (bytes1(_unsafeReadBytesOffset(buffer, i)) == s) {
  30. return i;
  31. }
  32. }
  33. return type(uint256).max;
  34. }
  35. /**
  36. * @dev Backward search for `s` in `buffer`
  37. * * If `s` is present in the buffer, returns the index of the last instance
  38. * * If `s` is not present in the buffer, returns type(uint256).max
  39. *
  40. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf[Javascript's `Array.lastIndexOf`]
  41. */
  42. function lastIndexOf(bytes memory buffer, bytes1 s) internal pure returns (uint256) {
  43. return lastIndexOf(buffer, s, type(uint256).max);
  44. }
  45. /**
  46. * @dev Backward search for `s` in `buffer` starting at position `pos`
  47. * * If `s` is present in the buffer (at or before `pos`), returns the index of the previous instance
  48. * * If `s` is not present in the buffer (at or before `pos`), returns type(uint256).max
  49. *
  50. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf[Javascript's `Array.lastIndexOf`]
  51. */
  52. function lastIndexOf(bytes memory buffer, bytes1 s, uint256 pos) internal pure returns (uint256) {
  53. unchecked {
  54. uint256 length = buffer.length;
  55. for (uint256 i = Math.min(Math.saturatingAdd(pos, 1), length); i > 0; --i) {
  56. if (bytes1(_unsafeReadBytesOffset(buffer, i - 1)) == s) {
  57. return i - 1;
  58. }
  59. }
  60. return type(uint256).max;
  61. }
  62. }
  63. /**
  64. * @dev Copies the content of `buffer`, from `start` (included) to the end of `buffer` into a new bytes object in
  65. * memory.
  66. *
  67. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice[Javascript's `Array.slice`]
  68. */
  69. function slice(bytes memory buffer, uint256 start) internal pure returns (bytes memory) {
  70. return slice(buffer, start, buffer.length);
  71. }
  72. /**
  73. * @dev Copies the content of `buffer`, from `start` (included) to `end` (excluded) into a new bytes object in
  74. * memory. The `end` argument is truncated to the length of the `buffer`.
  75. *
  76. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice[Javascript's `Array.slice`]
  77. */
  78. function slice(bytes memory buffer, uint256 start, uint256 end) internal pure returns (bytes memory) {
  79. // sanitize
  80. uint256 length = buffer.length;
  81. end = Math.min(end, length);
  82. start = Math.min(start, end);
  83. // allocate and copy
  84. bytes memory result = new bytes(end - start);
  85. assembly ("memory-safe") {
  86. mcopy(add(result, 0x20), add(add(buffer, 0x20), start), sub(end, start))
  87. }
  88. return result;
  89. }
  90. /**
  91. * @dev Moves the content of `buffer`, from `start` (included) to the end of `buffer` to the start of that buffer.
  92. *
  93. * NOTE: This function modifies the provided buffer in place. If you need to preserve the original buffer, use {slice} instead
  94. */
  95. function splice(bytes memory buffer, uint256 start) internal pure returns (bytes memory) {
  96. return splice(buffer, start, buffer.length);
  97. }
  98. /**
  99. * @dev Moves the content of `buffer`, from `start` (included) to end (excluded) to the start of that buffer. The
  100. * `end` argument is truncated to the length of the `buffer`.
  101. *
  102. * NOTE: This function modifies the provided buffer in place. If you need to preserve the original buffer, use {slice} instead
  103. */
  104. function splice(bytes memory buffer, uint256 start, uint256 end) internal pure returns (bytes memory) {
  105. // sanitize
  106. uint256 length = buffer.length;
  107. end = Math.min(end, length);
  108. start = Math.min(start, end);
  109. // allocate and copy
  110. assembly ("memory-safe") {
  111. mcopy(add(buffer, 0x20), add(add(buffer, 0x20), start), sub(end, start))
  112. mstore(buffer, sub(end, start))
  113. }
  114. return buffer;
  115. }
  116. /*
  117. * @dev Reverses the byte order of a bytes32 value, converting between little-endian and big-endian.
  118. * Inspired in https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel[Reverse Parallel]
  119. */
  120. function reverseBytes32(bytes32 value) internal pure returns (bytes32) {
  121. value = // swap bytes
  122. ((value >> 8) & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) |
  123. ((value & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
  124. value = // swap 2-byte long pairs
  125. ((value >> 16) & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) |
  126. ((value & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
  127. value = // swap 4-byte long pairs
  128. ((value >> 32) & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) |
  129. ((value & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);
  130. value = // swap 8-byte long pairs
  131. ((value >> 64) & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) |
  132. ((value & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);
  133. return (value >> 128) | (value << 128); // swap 16-byte long pairs
  134. }
  135. /// @dev Same as {reverseBytes32} but optimized for 128-bit values.
  136. function reverseBytes16(bytes16 value) internal pure returns (bytes16) {
  137. value = // swap bytes
  138. ((value & 0xFF00FF00FF00FF00FF00FF00FF00FF00) >> 8) |
  139. ((value & 0x00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
  140. value = // swap 2-byte long pairs
  141. ((value & 0xFFFF0000FFFF0000FFFF0000FFFF0000) >> 16) |
  142. ((value & 0x0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
  143. value = // swap 4-byte long pairs
  144. ((value & 0xFFFFFFFF00000000FFFFFFFF00000000) >> 32) |
  145. ((value & 0x00000000FFFFFFFF00000000FFFFFFFF) << 32);
  146. return (value >> 64) | (value << 64); // swap 8-byte long pairs
  147. }
  148. /// @dev Same as {reverseBytes32} but optimized for 64-bit values.
  149. function reverseBytes8(bytes8 value) internal pure returns (bytes8) {
  150. value = ((value & 0xFF00FF00FF00FF00) >> 8) | ((value & 0x00FF00FF00FF00FF) << 8); // swap bytes
  151. value = ((value & 0xFFFF0000FFFF0000) >> 16) | ((value & 0x0000FFFF0000FFFF) << 16); // swap 2-byte long pairs
  152. return (value >> 32) | (value << 32); // swap 4-byte long pairs
  153. }
  154. /// @dev Same as {reverseBytes32} but optimized for 32-bit values.
  155. function reverseBytes4(bytes4 value) internal pure returns (bytes4) {
  156. value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); // swap bytes
  157. return (value >> 16) | (value << 16); // swap 2-byte long pairs
  158. }
  159. /// @dev Same as {reverseBytes32} but optimized for 16-bit values.
  160. function reverseBytes2(bytes2 value) internal pure returns (bytes2) {
  161. return (value >> 8) | (value << 8);
  162. }
  163. /**
  164. * @dev Reads a bytes32 from a bytes array without bounds checking.
  165. *
  166. * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
  167. * assembly block as such would prevent some optimizations.
  168. */
  169. function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
  170. // This is not memory safe in the general case, but all calls to this private function are within bounds.
  171. assembly ("memory-safe") {
  172. value := mload(add(add(buffer, 0x20), offset))
  173. }
  174. }
  175. }