@@ -11,7 +11,6 @@ import {SignedMath} from "./math/SignedMath.sol";
1111 */
1212library Strings {
1313 bytes16 private constant HEX_DIGITS = "0123456789abcdef " ;
14- bytes16 private constant HEX_DIGITS_UPPERCASE = "0123456789ABCDEF " ;
1514 uint8 private constant ADDRESS_LENGTH = 20 ;
1615
1716 /**
@@ -64,15 +63,17 @@ library Strings {
6463 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
6564 */
6665 function toHexString (uint256 value , uint256 length ) internal pure returns (string memory ) {
67- if (length < Math.log256 (value) + 1 ) {
68- revert StringsInsufficientHexLength (value, length);
69- }
70-
66+ uint256 localValue = value;
7167 bytes memory buffer = new bytes (2 * length + 2 );
7268 buffer[0 ] = "0 " ;
7369 buffer[1 ] = "x " ;
74- _unsafeSetHexString (buffer, 2 , value);
75-
70+ for (uint256 i = 2 * length + 1 ; i > 1 ; -- i) {
71+ buffer[i] = HEX_DIGITS[localValue & 0xf ];
72+ localValue >>= 4 ;
73+ }
74+ if (localValue != 0 ) {
75+ revert StringsInsufficientHexLength (value, length);
76+ }
7677 return string (buffer);
7778 }
7879
@@ -89,22 +90,23 @@ library Strings {
8990 * representation, according to EIP-55.
9091 */
9192 function toChecksumHexString (address addr ) internal pure returns (string memory ) {
92- bytes memory lowercase = new bytes (40 );
93- uint160 addrValue = uint160 (addr);
94- _unsafeSetHexString (lowercase, 0 , addrValue);
95- bytes32 hashedAddr = keccak256 (abi.encodePacked (lowercase));
93+ bytes memory buffer = bytes (toHexString (addr));
94+
95+ // hash the hex part of buffer (skip length + 2 bytes, length 40)
96+ uint256 hashValue;
97+ assembly ("memory-safe" ) {
98+ hashValue := shr (96 , keccak256 (add (buffer, 0x22 ), 40 ))
99+ }
96100
97- bytes memory buffer = new bytes (42 );
98- buffer[0 ] = "0 " ;
99- buffer[1 ] = "x " ;
100- uint160 hashValue = uint160 (bytes20 (hashedAddr));
101101 for (uint256 i = 41 ; i > 1 ; -- i) {
102- uint8 digit = uint8 (addrValue & 0xf );
103- buffer[i] = hashValue & 0xf > 7 ? HEX_DIGITS_UPPERCASE[digit] : HEX_DIGITS[digit];
104- addrValue >>= 4 ;
102+ // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
103+ if (hashValue & 0xf > 7 && uint8 (buffer[i]) > 96 ) {
104+ // case shift by xoring with 0x20
105+ buffer[i] ^= 0x20 ;
106+ }
105107 hashValue >>= 4 ;
106108 }
107- return string (abi.encodePacked ( buffer) );
109+ return string (buffer);
108110 }
109111
110112 /**
@@ -113,17 +115,4 @@ library Strings {
113115 function equal (string memory a , string memory b ) internal pure returns (bool ) {
114116 return bytes (a).length == bytes (b).length && keccak256 (bytes (a)) == keccak256 (bytes (b));
115117 }
116-
117- /**
118- * @dev Sets the hexadecimal representation of a value in the specified buffer starting from the given offset.
119- *
120- * NOTE: This function does not check that the `buffer` can allocate `value` without overflowing. Make sure
121- * to check whether `Math.log256(value) + 1` is larger than the specified `length`.
122- */
123- function _unsafeSetHexString (bytes memory buffer , uint256 offset , uint256 value ) private pure {
124- for (uint256 i = buffer.length ; i > offset; -- i) {
125- buffer[i - 1 ] = HEX_DIGITS[value & 0xf ];
126- value >>= 4 ;
127- }
128- }
129118}
0 commit comments