@@ -37,34 +37,112 @@ library Base58 {
3737 return _decode (bytes (data));
3838 }
3939
40- function _encode (bytes memory data ) private pure returns (bytes memory ) {
41- unchecked {
42- uint256 dataCLZ = data.countLeading (0x00 );
43- uint256 length = dataCLZ + ((data.length - dataCLZ) * 8351 ) / 6115 + 1 ;
44- bytes memory slot = new bytes (length);
45-
46- uint256 end = length;
47- for (uint256 i = 0 ; i < data.length ; i++ ) {
48- uint256 ptr = length;
49- for (uint256 carry = _mload8i (data, i); ptr > end || carry != 0 ; -- ptr) {
50- carry += 256 * _mload8i (slot, ptr - 1 );
51- _mstore8i (slot, ptr - 1 , uint8 (carry % 58 ));
52- carry /= 58 ;
40+ function _encode (bytes memory data ) private pure returns (bytes memory encoded ) {
41+ // For reference, solidity implementation
42+ // unchecked {
43+ // uint256 dataCLZ = data.countLeading(0x00);
44+ // uint256 length = dataCLZ + ((data.length - dataCLZ) * 8351) / 6115 + 1;
45+ // encoded = new bytes(length);
46+ // uint256 end = length;
47+ // for (uint256 i = 0; i < data.length; ++i) {
48+ // uint256 ptr = length;
49+ // for (uint256 carry = uint8(data[i]); ptr > end || carry != 0; --ptr) {
50+ // carry += 256 * uint8(encoded[ptr - 1]);
51+ // encoded[ptr - 1] = bytes1(uint8(carry % 58));
52+ // carry /= 58;
53+ // }
54+ // end = ptr;
55+ // }
56+ // uint256 encodedCLZ = encoded.countLeading(0x00);
57+ // length -= encodedCLZ - dataCLZ;
58+ // encoded.splice(encodedCLZ - dataCLZ);
59+ // for (uint256 i = 0; i < length; ++i) {
60+ // encoded[i] = _TABLE[uint8(encoded[i])];
61+ // }
62+ // }
63+
64+ // Assembly is ~50% cheaper for buffers of size 32.
65+ assembly ("memory-safe" ) {
66+ function clzBytes (ptr, length) -> i {
67+ let chunk
68+ for {
69+ i := 0
70+ } lt (i, length) {
71+ i := add (i, 1 )
72+ } {
73+ // Every 32 bytes, load a new chunk
74+ if iszero (mod (i, 0x20 )) {
75+ chunk := mload (add (ptr, i))
76+ }
77+ // If the first byte of the chunk is not zero, break
78+ if shr (248 , chunk) {
79+ break
80+ }
81+ // Shift chunk
82+ chunk := shl (8 , chunk)
83+ }
84+ }
85+
86+ encoded := mload (0x40 )
87+ let dataLength := mload (data)
88+
89+ // Count number of zero bytes at the beginning of `data`
90+ let dataCLZ := clzBytes (add (data, 0x20 ), dataLength)
91+
92+ // Initial encoding
93+ let slotLength := add (add (dataCLZ, div (mul (sub (dataLength, dataCLZ), 8351 ), 6115 )), 1 )
94+
95+ // Zero the encoded buffer
96+ for {
97+ let i := 0
98+ } lt (i, slotLength) {
99+ i := add (i, 0x20 )
100+ } {
101+ mstore (add (add (encoded, 0x20 ), i), 0 )
102+ }
103+
104+ // Build the "slots"
105+ for {
106+ let i := 0
107+ let end := slotLength
108+ } lt (i, dataLength) {
109+ i := add (i, 1 )
110+ } {
111+ let ptr := slotLength
112+ for {
113+ let carry := shr (248 , mload (add (add (data, 0x20 ), i)))
114+ } or (carry, lt (end, ptr)) {
115+ ptr := sub (ptr, 1 )
116+ carry := div (carry, 58 )
117+ } {
118+ carry := add (carry, mul (256 , shr (248 , mload (add (add (encoded, 0x1f ), ptr)))))
119+ mstore8 (add (add (encoded, 0x1f ), ptr), mod (carry, 58 ))
53120 }
54- end = ptr;
121+ end : = ptr
55122 }
56123
57- uint256 slotCLZ = slot.countLeading (0x00 );
58- length -= slotCLZ - dataCLZ;
59- slot.splice (slotCLZ - dataCLZ);
124+ // Count number of zero bytes at the beginning of slots
125+ let slotCLZ := clzBytes (add (encoded, 0x20 ), slotLength)
126+
127+ // Update length
128+ let offset := sub (slotCLZ, dataCLZ)
129+ let encodedLength := sub (slotLength, offset)
130+
131+ // Store the encoding table. This overlaps with the FMP that we are going to reset later anyway.
132+ mstore (0x1f , "123456789ABCDEFGHJKLMNPQRSTUVWXY " )
133+ mstore (0x3f , "Zabcdefghijkmnopqrstuvwxyz " )
60134
61- bytes memory cache = _TABLE;
62- for (uint256 i = 0 ; i < length; ++ i) {
63- // equivalent to `slot[i] = TABLE[slot[i]];`
64- _mstore8 (slot, i, _mload8 (cache, _mload8i (slot, i)));
135+ for {
136+ let i := 0
137+ } lt (i, encodedLength) {
138+ i := add (i, 1 )
139+ } {
140+ mstore8 (add (add (encoded, 0x20 ), i), mload (shr (248 , mload (add (add (encoded, 0x20 ), add (offset, i))))))
65141 }
66142
67- return slot;
143+ // Store length and allocate memory
144+ mstore (encoded, encodedLength)
145+ mstore (0x40 , add (add (encoded, 0x20 ), encodedLength))
68146 }
69147 }
70148
0 commit comments