Base64.sol 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.1.0) (utils/Base64.sol)
  3. pragma solidity ^0.8.20;
  4. /**
  5. * @dev Provides a set of functions to operate with Base64 strings.
  6. */
  7. library Base64 {
  8. /**
  9. * @dev Base64 Encoding/Decoding Table
  10. * See sections 4 and 5 of https://datatracker.ietf.org/doc/html/rfc4648
  11. */
  12. string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  13. string internal constant _TABLE_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
  14. /**
  15. * @dev Converts a `bytes` to its Bytes64 `string` representation.
  16. */
  17. function encode(bytes memory data) internal pure returns (string memory) {
  18. return _encode(data, _TABLE, true);
  19. }
  20. /**
  21. * @dev Converts a `bytes` to its Bytes64Url `string` representation.
  22. * Output is not padded with `=` as specified in https://www.rfc-editor.org/rfc/rfc4648[rfc4648].
  23. */
  24. function encodeURL(bytes memory data) internal pure returns (string memory) {
  25. return _encode(data, _TABLE_URL, false);
  26. }
  27. /**
  28. * @dev Internal table-agnostic conversion
  29. */
  30. function _encode(bytes memory data, string memory table, bool withPadding) private pure returns (string memory) {
  31. /**
  32. * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
  33. * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
  34. */
  35. if (data.length == 0) return "";
  36. // If padding is enabled, the final length should be `bytes` data length divided by 3 rounded up and then
  37. // multiplied by 4 so that it leaves room for padding the last chunk
  38. // - `data.length + 2` -> Prepare for division rounding up
  39. // - `/ 3` -> Number of 3-bytes chunks (rounded up)
  40. // - `4 *` -> 4 characters for each chunk
  41. // This is equivalent to: 4 * Math.ceil(data.length / 3)
  42. //
  43. // If padding is disabled, the final length should be `bytes` data length multiplied by 4/3 rounded up as
  44. // opposed to when padding is required to fill the last chunk.
  45. // - `4 * data.length` -> 4 characters for each chunk
  46. // - ` + 2` -> Prepare for division rounding up
  47. // - `/ 3` -> Number of 3-bytes chunks (rounded up)
  48. // This is equivalent to: Math.ceil((4 * data.length) / 3)
  49. uint256 resultLength = withPadding ? 4 * ((data.length + 2) / 3) : (4 * data.length + 2) / 3;
  50. string memory result = new string(resultLength);
  51. assembly ("memory-safe") {
  52. // Prepare the lookup table (skip the first "length" byte)
  53. let tablePtr := add(table, 1)
  54. // Prepare result pointer, jump over length
  55. let resultPtr := add(result, 0x20)
  56. let dataPtr := data
  57. let endPtr := add(data, mload(data))
  58. // In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
  59. // set it to zero to make sure no dirty bytes are read in that section.
  60. let afterPtr := add(endPtr, 0x20)
  61. let afterCache := mload(afterPtr)
  62. mstore(afterPtr, 0x00)
  63. // Run over the input, 3 bytes at a time
  64. for {} lt(dataPtr, endPtr) {} {
  65. // Advance 3 bytes
  66. dataPtr := add(dataPtr, 3)
  67. let input := mload(dataPtr)
  68. // To write each character, shift the 3 byte (24 bits) chunk
  69. // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
  70. // and apply logical AND with 0x3F to bitmask the least significant 6 bits.
  71. // Use this as an index into the lookup table, mload an entire word
  72. // so the desired character is in the least significant byte, and
  73. // mstore8 this least significant byte into the result and continue.
  74. mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
  75. resultPtr := add(resultPtr, 1) // Advance
  76. mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
  77. resultPtr := add(resultPtr, 1) // Advance
  78. mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
  79. resultPtr := add(resultPtr, 1) // Advance
  80. mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
  81. resultPtr := add(resultPtr, 1) // Advance
  82. }
  83. // Reset the value that was cached
  84. mstore(afterPtr, afterCache)
  85. if withPadding {
  86. // When data `bytes` is not exactly 3 bytes long
  87. // it is padded with `=` characters at the end
  88. switch mod(mload(data), 3)
  89. case 1 {
  90. mstore8(sub(resultPtr, 1), 0x3d)
  91. mstore8(sub(resultPtr, 2), 0x3d)
  92. }
  93. case 2 {
  94. mstore8(sub(resultPtr, 1), 0x3d)
  95. }
  96. }
  97. }
  98. return result;
  99. }
  100. }