diff --git a/src/utils/LibBit.sol b/src/utils/LibBit.sol index b6a8826cd..d31551bca 100644 --- a/src/utils/LibBit.sol +++ b/src/utils/LibBit.sol @@ -117,14 +117,25 @@ library LibBit { } } - /// @dev Returns the common prefix of `x` and `y` in hex format. - function commonPrefix(uint256 x, uint256 y) internal pure returns (uint256 r) { - uint256 lz = clz(x ^ y); - assembly { - let nibbles := div(lz, 4) - // Since nibbles is always <= 64, there's no risk of underflow. - let bits := mul(sub(64, nibbles), 4) - r := shl(bits, shr(bits, x)) + /// @dev Returns the common prefix of `x` and `y` at the bit level. + function commonBitPrefix(uint256 x, uint256 y) internal pure returns (uint256 r) { + r = clz(x ^ y); + r = (x >> r) << r; + } + + /// @dev Returns the common prefix of `x` and `y` at the nibble level. + function commonNibblePrefix(uint256 x, uint256 y) internal pure returns (uint256 r) { + unchecked { + uint256 s = (64 - (clz(x ^ y) >> 2)) << 2; + r = (x >> s) << s; + } + } + + /// @dev Returns the common prefix of `x` and `y` at the byte level. + function commonBytePrefix(uint256 x, uint256 y) internal pure returns (uint256 r) { + unchecked { + uint256 s = (32 - (clz(x ^ y) >> 3)) << 3; + r = (x >> s) << s; } } diff --git a/test/LibBit.t.sol b/test/LibBit.t.sol index ba641b1b5..459fa9e35 100644 --- a/test/LibBit.t.sol +++ b/test/LibBit.t.sol @@ -226,15 +226,20 @@ contract LibBitTest is SoladyTest { } function testCommonPrefix() public { - assertEq(LibBit.commonPrefix(0x1, 0x2), 0); - assertEq(LibBit.commonPrefix(0x1234abc, 0x1234bbb), 0x1234000); - assertEq(LibBit.commonPrefix(0x1234abc, 0x1234abc), 0x1234abc); + assertEq(LibBit.commonNibblePrefix(0x1, 0x2), 0); + assertEq(LibBit.commonNibblePrefix(0x1234abc, 0x1234bbb), 0x1234000); + assertEq(LibBit.commonNibblePrefix(0x1234abc, 0x1234abc), 0x1234abc); + + assertEq(LibBit.commonBytePrefix(0xaabbcc, 0xaabbcc), 0xaabbcc); + assertEq(LibBit.commonBytePrefix(0xaabbcc, 0xaabbc0), 0xaabb00); + assertEq(LibBit.commonBytePrefix(0xaabbcc, 0xaab0c0), 0xaa0000); + assertEq(LibBit.commonBytePrefix(0xaabbcc, 0xa0b0c0), 0x000000); } function testCommonPrefix(uint256 x, uint8 p) public { uint256 y = x ^ (1 << p); uint256 l = 63 - p / 4; uint256 r = l == 0 ? 0 : x & ~((1 << ((64 - l) * 4)) - 1); - assertEq(LibBit.commonPrefix(x, y), r); + assertEq(LibBit.commonNibblePrefix(x, y), r); } }