⚡️ Optimize address to string
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:
- worth the codesize trade off?
- document how it works?
Checklist
- [x] Ran
forge fmt? - [x] Ran
forge test?
Also not sure what's wrong with the diff ci check, showing error:
fatal: a branch named 'main' already exists
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:
- bitmap of whether each nibble will be a letter or not
- and eventually how to make a padded nibble into it's ascii representation
'0' 0000 -> 00110000
'1' 0001 -> 00110001
'2' 0010 -> 00110010
'3' 0011 -> 00110011
'4' 0100 -> 00110100
'5' 0101 -> 00110101
'6' 0110 -> 00110110
'7' 0111 -> 00110111
'8' 1000 -> 00111000
'9' 1001 -> 00111001
'a' 1010 -> 01100001
'b' 1011 -> 01100010
'c' 1100 -> 01100011
'd' 1101 -> 01100100
'e' 1110 -> 01100101
'f' 1111 -> 01100110
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: b4 && (b3 || b2), which you can do by shifting and masking the word a couple times.
For the ascii representation I noticed it's basically:
b8 b7 b6 b5 b4 b3 b2 b1
digit: 0 0 1 1 [ x ]
letter: 0 1 1 0 [ 8 ^ x - 1 ]
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.
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));
}
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.