Skip to content

Commit dbee56c

Browse files
committed
rewrite _encode in assembly
1 parent d09ebfa commit dbee56c

File tree

1 file changed

+106
-22
lines changed

1 file changed

+106
-22
lines changed

contracts/utils/Base58.sol

Lines changed: 106 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,34 +37,118 @@ 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+
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))
53114
}
54-
end = ptr;
115+
end := ptr
55116
}
56117

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")
60141

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))))))
65148
}
66149

67-
return slot;
150+
mstore(encoded, encodedLength)
151+
mstore(0x40, add(encoded, add(0x20, encodedLength)))
68152
}
69153
}
70154

0 commit comments

Comments
 (0)