Skip to content

Instantly share code, notes, and snippets.

@mingderwang
Created April 8, 2024 04:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mingderwang/4a1023b7454d33bb4b75e35c95234867 to your computer and use it in GitHub Desktop.
Save mingderwang/4a1023b7454d33bb4b75e35c95234867 to your computer and use it in GitHub Desktop.
/* solhint-disable */
// SPDX-License-Identifier: MIT
// from OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)
pragma solidity ^0.8.19;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*
* _Available since v4.5._
*/
library Base64URL {
/**
* @dev Base64 Encoding/Decoding Table
*/
string internal constant _TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode32(bytes memory data) internal pure returns (string memory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/
if (data.length == 0) return "";
// Loads the table into memory
string memory table = _TABLE;
// Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
// and split into 4 numbers of 6 bits.
// The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
// - `data.length + 2` -> Round up
// - `/ 3` -> Number of 3-bytes chunks
// - `4 *` -> 4 characters for each chunk
//string memory result = new string(4 * ((data.length + 2) / 3));
string memory result = new string(4 * ((data.length + 2) / 3) - 1);
/// @solidity memory-safe-assembly
// @solhint-disable-next-line no-inline-assembly
assembly {
// Prepare the lookup table (skip the first "length" byte)
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 32)
// Run over the input, 3 bytes at a time
for {
let dataPtr := data
let endPtr := add(data, mload(data))
} lt(dataPtr, endPtr) { } {
// Advance 3 bytes
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
// To write each character, shift the 3 bytes (18 bits) chunk
// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
// and apply logical AND with 0x3F which is the number of
// the previous character in the ASCII table prior to the Base64 Table
// The result is then added to the table to get the character to write,
// and finally write it in the result pointer but with a left shift
// of 256 (1 byte) - 8 (1 ASCII char) = 248 bits
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
}
/*
// When data `bytes` is not exactly 3 bytes long
// it is padded with `=` characters at the end
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
*/
}
return result;
}
}
/* solhint-enable */
@mingderwang
Copy link
Author

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./Base64URL.sol";

contract MyContract {
function encode(bytes memory _data) public pure returns (string memory) {
return Base64URL.encode32(_data);
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment