@@ -37,34 +37,118 @@ library Base58 {
37
37
return _decode (bytes (data));
38
38
}
39
39
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
+ encoded := mload (0x40 )
67
+ let dataLength := mload (data)
68
+
69
+ // count number of zero bytes at the beginning of `data`
70
+ let dataCLZ := 0
71
+ let dataChunk
72
+ for {} lt (dataCLZ, dataLength) {
73
+ dataCLZ := add (dataCLZ, 1 )
74
+ } {
75
+ // every 32 bytes, load a new chunk
76
+ if iszero (mod (dataCLZ, 0x20 )) {
77
+ dataChunk := mload (add (data, add (0x20 , dataCLZ)))
78
+ }
79
+ // if the first byte of the chunk does not match the search element, exit
80
+ if shr (248 , dataChunk) {
81
+ break
82
+ }
83
+ // shift chunk
84
+ dataChunk := shl (8 , dataChunk)
85
+ }
86
+
87
+ // intial encoding
88
+ let slotLength := add (add (dataCLZ, div (mul (sub (dataLength, dataCLZ), 8351 ), 6115 )), 1 )
89
+
90
+ // zero the encoded buffer
91
+ for {
92
+ let i := 0
93
+ } lt (i, slotLength) {
94
+ i := add (i, 0x20 )
95
+ } {
96
+ mstore (add (add (encoded, 0x20 ), i), 0 )
97
+ }
98
+
99
+ for {
100
+ let i := 0
101
+ let end := slotLength
102
+ } lt (i, dataLength) {
103
+ i := add (i, 1 )
104
+ } {
105
+ let ptr := slotLength
106
+ for {
107
+ let carry := shr (248 , mload (add (add (data, 0x20 ), i)))
108
+ } or (carry, lt (end, ptr)) {
109
+ ptr := sub (ptr, 1 )
110
+ carry := div (carry, 58 )
111
+ } {
112
+ carry := add (carry, mul (256 , shr (248 , mload (add (add (encoded, 0x1f ), ptr)))))
113
+ mstore8 (add (add (encoded, 0x1f ), ptr), mod (carry, 58 ))
53
114
}
54
- end = ptr;
115
+ end : = ptr
55
116
}
56
117
57
- uint256 slotCLZ = slot.countLeading (0x00 );
58
- length -= slotCLZ - dataCLZ;
59
- slot.splice (slotCLZ - dataCLZ);
118
+ // count number of zero bytes at the beginning of slots (stored in encoded)
119
+ let slotCLZ := 0
120
+ let slotChunk
121
+ for {} lt (slotCLZ, slotLength) {
122
+ slotCLZ := add (slotCLZ, 1 )
123
+ } {
124
+ // every 32 bytes, load a new chunk
125
+ if iszero (mod (slotCLZ, 0x20 )) {
126
+ slotChunk := mload (add (encoded, add (0x20 , slotCLZ)))
127
+ }
128
+ // if the first byte of the chunk does not match the search element, exit
129
+ if shr (248 , slotChunk) {
130
+ break
131
+ }
132
+ // shift chunk
133
+ slotChunk := shl (8 , slotChunk)
134
+ }
135
+
136
+ let offset := sub (slotCLZ, dataCLZ)
137
+ let encodedLength := sub (slotLength, offset)
138
+
139
+ mstore (0x1f , "123456789ABCDEFGHJKLMNPQRSTUVWXY " )
140
+ mstore (0x3f , "Zabcdefghijkmnopqrstuvwxyz " )
60
141
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)));
142
+ for {
143
+ let i := 0
144
+ } lt (i, encodedLength) {
145
+ i := add (i, 1 )
146
+ } {
147
+ mstore8 (add (add (encoded, 0x20 ), i), mload (shr (248 , mload (add (add (encoded, 0x20 ), add (offset, i))))))
65
148
}
66
149
67
- return slot;
150
+ mstore (encoded, encodedLength)
151
+ mstore (0x40 , add (encoded, add (0x20 , encodedLength)))
68
152
}
69
153
}
70
154
0 commit comments