Conversation
|
Also not sure what's wrong with the diff ci check, showing error:
|
|
What made you think about this trick? |
|
I was benchmarking and trying to optimize address checksum computation for a project of mine. I was looking at a table of hex nibble to binary to ascii-binary table and noticed that you could relatively cheaply compute:
I noticed that a-f all have bit 4 set, but to exclude 8 & 9 you need to also check bit 2 & 3, giving you: For the ascii representation I noticed it's basically: So yeah once you have the nibbles spaced out, padded and you have a bitmap of which byte will be a letter or not it's relatively easy to get the ascii representation. |
| shl(64, and(value, 0xffffffffffffffff0000000000000000)), | ||
| and(value, 0xffffffffffffffff) |
There was a problem hiding this comment.
(1 << 64) - 1 = 0xffffffffffffffff
(1 << 64) - 1 << 64 = 0xffffffffffffffff0000000000000000
| ) | ||
| w := | ||
| and( | ||
| 0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff, or(shl(8, w), w) |
There was a problem hiding this comment.
0xff * (type(uint256).max / type(uint16).max) = 0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff
| ) | ||
| w := | ||
| and( | ||
| 0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff, |
There was a problem hiding this comment.
0xffff * (type(uint256).max / type(uint32).max) = 0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff
|
|
||
| w := | ||
| and( | ||
| 0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff, |
There was a problem hiding this comment.
0xffffffff * (type(uint256).max / type(uint64).max) = 0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff
| w := | ||
| sub( | ||
| xor( | ||
| xor(w, 0x3030303030303030303030303030303030303030303030303030303030303030), |
There was a problem hiding this comment.
0x30 * (type(uint).max / 0xff) = 0x3030303030303030303030303030303030303030303030303030303030303030
|
Can cheaply create these masks JIT using the below formula: function repeat(uint256 x, uint strideBits) internal pure returns (uint256) {
return x * (type(uint256).max / ((1 << strideBits) - 1));
} |
Using this method to generate masks saved about 50 bytes of codesize, at the expense of additional gas likely not worth it but interesting way to generate repeat constants nonetheless. |
Description
Optimized address to hex string with some fancy bit operations. Has a fairly large gas trade-off ~360 bytes more by the looks of it, open questions:
Checklist
forge fmt?forge test?