@@ -37,34 +37,112 @@ 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
+ 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 ))
53
120
}
54
- end = ptr;
121
+ end : = ptr
55
122
}
56
123
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 " )
60
134
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))))))
65
141
}
66
142
67
- return slot;
143
+ // Store length and allocate memory
144
+ mstore (encoded, encodedLength)
145
+ mstore (0x40 , add (encoded, add (0x20 , encodedLength)))
68
146
}
69
147
}
70
148
0 commit comments