Bytes.sol 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.24;
  3. import {Math} from "./math/Math.sol";
  4. /**
  5. * @dev Bytes operations.
  6. */
  7. library Bytes {
  8. /**
  9. * @dev Forward search for `s` in `buffer`
  10. * * If `s` is present in the buffer, returns the index of the first instance
  11. * * If `s` is not present in the buffer, returns type(uint256).max
  12. *
  13. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf[Javascript's `Array.indexOf`]
  14. */
  15. function indexOf(bytes memory buffer, bytes1 s) internal pure returns (uint256) {
  16. return indexOf(buffer, s, 0);
  17. }
  18. /**
  19. * @dev Forward search for `s` in `buffer` starting at position `pos`
  20. * * If `s` is present in the buffer (at or after `pos`), returns the index of the next instance
  21. * * If `s` is not present in the buffer (at or after `pos`), returns type(uint256).max
  22. *
  23. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf[Javascript's `Array.indexOf`]
  24. */
  25. function indexOf(bytes memory buffer, bytes1 s, uint256 pos) internal pure returns (uint256) {
  26. uint256 length = buffer.length;
  27. for (uint256 i = pos; i < length; ++i) {
  28. if (bytes1(_unsafeReadBytesOffset(buffer, i)) == s) {
  29. return i;
  30. }
  31. }
  32. return type(uint256).max;
  33. }
  34. /**
  35. * @dev Backward search for `s` in `buffer`
  36. * * If `s` is present in the buffer, returns the index of the last instance
  37. * * If `s` is not present in the buffer, returns type(uint256).max
  38. *
  39. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf[Javascript's `Array.lastIndexOf`]
  40. */
  41. function lastIndexOf(bytes memory buffer, bytes1 s) internal pure returns (uint256) {
  42. return lastIndexOf(buffer, s, type(uint256).max);
  43. }
  44. /**
  45. * @dev Backward search for `s` in `buffer` starting at position `pos`
  46. * * If `s` is present in the buffer (at or before `pos`), returns the index of the previous instance
  47. * * If `s` is not present in the buffer (at or before `pos`), returns type(uint256).max
  48. *
  49. * NOTE: replicates the behavior of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf[Javascript's `Array.lastIndexOf`]
  50. */
  51. function lastIndexOf(bytes memory buffer, bytes1 s, uint256 pos) internal pure returns (uint256) {
  52. unchecked {
  53. uint256 length = buffer.length;
  54. // NOTE here we cannot do `i = Math.min(pos + 1, length)` because `pos + 1` could overflow
  55. for (uint256 i = Math.min(pos, length - 1) + 1; 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.
  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(buffer, add(start, 0x20)), sub(end, start))
  87. }
  88. return result;
  89. }
  90. /**
  91. * @dev Reads a bytes32 from a bytes array without bounds checking.
  92. *
  93. * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
  94. * assembly block as such would prevent some optimizations.
  95. */
  96. function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
  97. // This is not memory safe in the general case, but all calls to this private function are within bounds.
  98. assembly ("memory-safe") {
  99. value := mload(add(buffer, add(0x20, offset)))
  100. }
  101. }
  102. }