Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ auto_detect_solc = false
optimizer = true
optimizer_runs = 1_000
gas_limit = 100_000_000 # ETH is 30M, but we use a higher value.
skip = ["*/*7702*", "*/*BlockHashLib*", "*/*Transient*", "*/ext/ithaca/*", "*/ext/zksync/*"]
skip = ["*/*7702*", "*/*BlockHashLib*", "*/*Transient*", "*/ext/ithaca/*", "*/clz/*" ,"*/ext/zksync/*"]
fs_permissions = [{ access = "read", path = "./test/data"}]
remappings = [
"forge-std=test/utils/forge-std/"
]

[profile.pre_global_structs]
skip = ["*/g/*", "*/*7702*", "*/*BlockHashLib*", "*/*Transient*", "*/ext/ithaca/*", "*/ext/zksync/*"]
skip = ["*/g/*", "*/*7702*", "*/*BlockHashLib*", "*/*Transient*", "*/ext/ithaca/*", "*/clz/*", "*/ext/zksync/*"]


[profile.post_osaka]
Expand All @@ -27,13 +27,13 @@ skip = ["*/ext/ithaca/*"]
[profile.zksync]
match_path = "*/ext/zksync/*"
evm_version = "cancun"
skip = []
skip = ["*/clz/*"]

[profile.ithaca]
gas_limit = 16_777_216 # To prevent EVM error.
evm_version = "prague"
test = "test/ext/ithaca"
skip = []
skip = ["*/clz/*"]

[profile.solx]
solc_version = "/usr/local/bin/solx"
Expand Down
1,254 changes: 1,254 additions & 0 deletions src/utils/clz/FixedPointMathLib.sol

Large diffs are not rendered by default.

287 changes: 287 additions & 0 deletions src/utils/clz/LibBit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BIT TWIDDLING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @dev Find last set.
/// Returns the index of the most significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
function fls(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// r := add(xor(255, clz(or(x,1))), shl(8, iszero(x))) // 153092
// r := add(sub(255, clz(x)), mul(257, iszero(x))) // 149252
r := xor(xor(255, clz(x)), mul(255, iszero(x)))
}
}

/// @dev Find first set.
/// Returns the index of the least significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
/// Equivalent to `ctz` (count trailing zeros), which gives
/// the number of zeros following the least significant one bit.
function ffs(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Isolate the least significant bit.
x := and(x, add(not(x), 1))
r := xor(xor(255, clz(x)), mul(255, iszero(x)))
}
}

/// @dev Returns the number of set bits in `x`.
function popCount(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let isMax := eq(x, max)
x := sub(x, and(shr(1, x), div(max, 3)))
x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
x := and(add(x, shr(4, x)), div(max, 17))
c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
}
}

/// @dev Returns the number of zero bytes in `x`.
/// To get the number of non-zero bytes, simply do `32 - countZeroBytes(x)`.
function countZeroBytes(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
c := byte(0, mul(shr(7, not(m)), shr(7, not(or(or(add(and(x, m), m), x), m)))))
}
}

/// @dev Returns the number of zero bytes in `s`.
/// To get the number of non-zero bytes, simply do `s.length - countZeroBytes(s)`.
function countZeroBytes(bytes memory s) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
function czb(x_) -> _c {
let _m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
_c := shr(7, not(or(or(add(and(x_, _m), _m), x_), _m)))
_c := byte(0, mul(shr(7, not(_m)), _c))
}
let n := mload(s)
let l := shl(5, shr(5, n))
s := add(s, 0x20)
for { let i } xor(i, l) { i := add(i, 0x20) } { c := add(czb(mload(add(s, i))), c) }
if lt(l, n) {
c := add(czb(or(shr(shl(3, sub(n, l)), not(0)), mload(add(s, l)))), c)
}
}
}

/// @dev Returns the number of zero bytes in `s`.
/// To get the number of non-zero bytes, simply do `s.length - countZeroBytes(s)`.
function countZeroBytesCalldata(bytes calldata s) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
function czb(x_) -> _c {
let _m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
_c := shr(7, not(or(or(add(and(x_, _m), _m), x_), _m)))
_c := byte(0, mul(shr(7, not(_m)), _c))
}
let l := shl(5, shr(5, s.length))
for { let i } xor(i, l) { i := add(i, 0x20) } {
c := add(czb(calldataload(add(s.offset, i))), c)
}
if lt(l, s.length) {
let m := shr(shl(3, sub(s.length, l)), not(0))
c := add(czb(or(m, calldataload(add(s.offset, l)))), c)
}
}
}

/// @dev Returns whether `x` is a power of 2.
function isPo2(uint256 x) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `x && !(x & (x - 1))`.
result := iszero(add(and(x, sub(x, 1)), iszero(x)))
}
}

/// @dev Returns `x` reversed at the bit level.
function reverseBits(uint256 x) internal pure returns (uint256 r) {
uint256 m0 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 m1 = m0 ^ (m0 << 2);
uint256 m2 = m1 ^ (m1 << 1);
r = reverseBytes(x);
r = (m2 & (r >> 1)) | ((m2 & r) << 1);
r = (m1 & (r >> 2)) | ((m1 & r) << 2);
r = (m0 & (r >> 4)) | ((m0 & r) << 4);
}

/// @dev Returns `x` reversed at the byte level.
function reverseBytes(uint256 x) internal pure returns (uint256 r) {
unchecked {
// Computing masks on-the-fly reduces bytecode size by about 200 bytes.
uint256 m0 = 0x100000000000000000000000000000001 * (~toUint(x == uint256(0)) >> 192);
uint256 m1 = m0 ^ (m0 << 32);
uint256 m2 = m1 ^ (m1 << 16);
uint256 m3 = m2 ^ (m2 << 8);
r = (m3 & (x >> 8)) | ((m3 & x) << 8);
r = (m2 & (r >> 16)) | ((m2 & r) << 16);
r = (m1 & (r >> 32)) | ((m1 & r) << 32);
r = (m0 & (r >> 64)) | ((m0 & r) << 64);
r = (r >> 128) | (r << 128);
}
}

/// @dev Return `x` leading zeroes bits.
function clz_(uint256 x) internal pure returns (uint256 r) {
assembly {
r := clz(x)
}
}

/// @dev Returns the common prefix of `x` and `y` at the bit level.
function commonBitPrefix(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
uint256 s = 256 - clz_(x ^ y);
return (x >> s) << s;
}
}

/// @dev Returns the common prefix of `x` and `y` at the nibble level.
function commonNibblePrefix(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
uint256 s = (64 - (clz_(x ^ y) >> 2)) << 2;
return (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) {
unchecked {
uint256 s = (32 - (clz_(x ^ y) >> 3)) << 3;
return (x >> s) << s;
}
}

/// @dev hex"ABCD" -> hex"0A0B0C0D".
function toNibbles(bytes memory s) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := mload(s)
mstore(result, add(n, n)) // Store the new length.
s := add(s, 0x20)
let o := add(result, 0x20)
// forgefmt: disable-next-item
for { let i := 0 } lt(i, n) { i := add(i, 0x10) } {
let x := shr(128, mload(add(s, i)))
x := and(0x0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff, or(shl(64, x), x))
x := and(0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff, or(shl(32, x), x))
x := and(0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff, or(shl(16, x), x))
x := and(0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff, or(shl(8, x), x))
mstore(add(o, add(i, i)),
and(0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f, or(shl(4, x), x)))
}
mstore(add(o, mload(result)), 0) // Zeroize slot after result.
mstore(0x40, add(0x40, add(o, mload(result)))) // Allocate memory.
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BOOLEAN OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

// A Solidity bool on the stack or memory is represented as a 256-bit word.
// Non-zero values are true, zero is false.
// A clean bool is either 0 (false) or 1 (true) under the hood.
// Usually, if not always, the bool result of a regular Solidity expression,
// or the argument of a public/external function will be a clean bool.
// You can usually use the raw variants for more performance.
// If uncertain, test (best with exact compiler settings).
// Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).

/// @dev Returns `x & y`. Inputs must be clean.
function rawAnd(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(x, y)
}
}

/// @dev Returns `x & y`.
function and(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(iszero(iszero(x)), iszero(iszero(y)))
}
}

/// @dev Returns `w & x & y`.
function and(bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(or(iszero(w), or(iszero(x), iszero(y))))
}
}

/// @dev Returns `v & w & x & y`.
function and(bool v, bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(or(or(iszero(v), iszero(w)), or(iszero(x), iszero(y))))
}
}

/// @dev Returns `x | y`. Inputs must be clean.
function rawOr(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, y)
}
}

/// @dev Returns `x | y`.
function or(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(or(x, y)))
}
}

/// @dev Returns `w | x | y`.
function or(bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(or(w, or(x, y))))
}
}

/// @dev Returns `v | w | x | y`.
function or(bool v, bool w, bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(or(v, or(w, or(x, y)))))
}
}

/// @dev Returns 1 if `b` is true, else 0. Input must be clean.
function rawToUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := b
}
}

/// @dev Returns 1 if `b` is true, else 0.
function toUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(b))
}
}
}
Loading