diff --git a/foundry.toml b/foundry.toml index 0e9c6acc5..e74f43e46 100644 --- a/foundry.toml +++ b/foundry.toml @@ -10,7 +10,7 @@ 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/" diff --git a/src/utils/clz/FixedPointMathLib.sol b/src/utils/clz/FixedPointMathLib.sol new file mode 100644 index 000000000..ba7692115 --- /dev/null +++ b/src/utils/clz/FixedPointMathLib.sol @@ -0,0 +1,1254 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +/// @notice Arithmetic library with operations for fixed-point numbers. +/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol) +/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) +library FixedPointMathLib { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CUSTOM ERRORS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev The operation failed, as the output exceeds the maximum value of uint256. + error ExpOverflow(); + + /// @dev The operation failed, as the output exceeds the maximum value of uint256. + error FactorialOverflow(); + + /// @dev The operation failed, due to an overflow. + error RPowOverflow(); + + /// @dev The mantissa is too big to fit. + error MantissaOverflow(); + + /// @dev The operation failed, due to an multiplication overflow. + error MulWadFailed(); + + /// @dev The operation failed, due to an multiplication overflow. + error SMulWadFailed(); + + /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero. + error DivWadFailed(); + + /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero. + error SDivWadFailed(); + + /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero. + error MulDivFailed(); + + /// @dev The division failed, as the denominator is zero. + error DivFailed(); + + /// @dev The full precision multiply-divide operation failed, either due + /// to the result being larger than 256 bits, or a division by a zero. + error FullMulDivFailed(); + + /// @dev The output is undefined, as the input is less-than-or-equal to zero. + error LnWadUndefined(); + + /// @dev The input outside the acceptable domain. + error OutOfDomain(); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CONSTANTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev The scalar of ETH and most ERC20s. + uint256 internal constant WAD = 1e18; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* SIMPLIFIED FIXED POINT OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Equivalent to `(x * y) / WAD` rounded down. + function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. + if gt(x, div(not(0), y)) { + if y { + mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. + revert(0x1c, 0x04) + } + } + z := div(mul(x, y), WAD) + } + } + + /// @dev Equivalent to `(x * y) / WAD` rounded down. + function sMulWad(int256 x, int256 y) internal pure returns (int256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mul(x, y) + // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`. + if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) { + mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`. + revert(0x1c, 0x04) + } + z := sdiv(z, WAD) + } + } + + /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks. + function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := div(mul(x, y), WAD) + } + } + + /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks. + function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) { + /// @solidity memory-safe-assembly + assembly { + z := sdiv(mul(x, y), WAD) + } + } + + /// @dev Equivalent to `(x * y) / WAD` rounded up. + function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mul(x, y) + // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. + if iszero(eq(div(z, y), x)) { + if y { + mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. + revert(0x1c, 0x04) + } + } + z := add(iszero(iszero(mod(z, WAD))), div(z, WAD)) + } + } + + /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks. + function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD)) + } + } + + /// @dev Equivalent to `(x * WAD) / y` rounded down. + function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`. + if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) { + mstore(0x00, 0x7c5f487d) // `DivWadFailed()`. + revert(0x1c, 0x04) + } + z := div(mul(x, WAD), y) + } + } + + /// @dev Equivalent to `(x * WAD) / y` rounded down. + function sDivWad(int256 x, int256 y) internal pure returns (int256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mul(x, WAD) + // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`. + if iszero(mul(y, eq(sdiv(z, WAD), x))) { + mstore(0x00, 0x5c43740d) // `SDivWadFailed()`. + revert(0x1c, 0x04) + } + z := sdiv(z, y) + } + } + + /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks. + function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := div(mul(x, WAD), y) + } + } + + /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks. + function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) { + /// @solidity memory-safe-assembly + assembly { + z := sdiv(mul(x, WAD), y) + } + } + + /// @dev Equivalent to `(x * WAD) / y` rounded up. + function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`. + if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) { + mstore(0x00, 0x7c5f487d) // `DivWadFailed()`. + revert(0x1c, 0x04) + } + z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y)) + } + } + + /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks. + function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y)) + } + } + + /// @dev Equivalent to `x` to the power of `y`. + /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`. + /// Note: This function is an approximation. + function powWad(int256 x, int256 y) internal pure returns (int256) { + // Using `ln(x)` means `x` must be greater than 0. + return expWad((lnWad(x) * y) / int256(WAD)); + } + + /// @dev Returns `exp(x)`, denominated in `WAD`. + /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln + /// Note: This function is an approximation. Monotonically increasing. + function expWad(int256 x) internal pure returns (int256 r) { + unchecked { + // When the result is less than 0.5 we return zero. + // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`. + if (x <= -41446531673892822313) return r; + + /// @solidity memory-safe-assembly + assembly { + // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as + // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`. + if iszero(slt(x, 135305999368893231589)) { + mstore(0x00, 0xa37bfec9) // `ExpOverflow()`. + revert(0x1c, 0x04) + } + } + + // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96` + // for more intermediate precision and a binary basis. This base conversion + // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. + x = (x << 78) / 5 ** 18; + + // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers + // of two such that exp(x) = exp(x') * 2**k, where k is an integer. + // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). + int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96; + x = x - k * 54916777467707473351141471128; + + // `k` is in the range `[-61, 195]`. + + // Evaluate using a (6, 7)-term rational approximation. + // `p` is made monic, we'll multiply by a scale factor later. + int256 y = x + 1346386616545796478920950773328; + y = ((y * x) >> 96) + 57155421227552351082224309758442; + int256 p = y + x - 94201549194550492254356042504812; + p = ((p * y) >> 96) + 28719021644029726153956944680412240; + p = p * x + (4385272521454847904659076985693276 << 96); + + // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. + int256 q = x - 2855989394907223263936484059900; + q = ((q * x) >> 96) + 50020603652535783019961831881945; + q = ((q * x) >> 96) - 533845033583426703283633433725380; + q = ((q * x) >> 96) + 3604857256930695427073651918091429; + q = ((q * x) >> 96) - 14423608567350463180887372962807573; + q = ((q * x) >> 96) + 26449188498355588339934803723976023; + + /// @solidity memory-safe-assembly + assembly { + // Div in assembly because solidity adds a zero check despite the unchecked. + // The q polynomial won't have zeros in the domain as all its roots are complex. + // No scaling is necessary because p is already `2**96` too large. + r := sdiv(p, q) + } + + // r should be in the range `(0.09, 0.25) * 2**96`. + + // We now need to multiply r by: + // - The scale factor `s ≈ 6.031367120`. + // - The `2**k` factor from the range reduction. + // - The `1e18 / 2**96` factor for base conversion. + // We do this all at once, with an intermediate result in `2**213` + // basis, so the final right shift is always by a positive amount. + r = int256( + (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k) + ); + } + } + + /// @dev Returns `ln(x)`, denominated in `WAD`. + /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln + /// Note: This function is an approximation. Monotonically increasing. + function lnWad(int256 x) internal pure returns (int256 r) { + /// @solidity memory-safe-assembly + assembly { + if iszero(sgt(x, 0)) { + mstore(0x00, 0x1615e638) // `LnWadUndefined()`. + revert(0x1c, 0x04) + } + + // We want to convert `x` from `10**18` fixed point to `2**96` fixed point. + // We do this by multiplying by `2**96 / 10**18`. But since + // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here + // and add `ln(2**96 / 10**18)` at the end. + // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 - (255 - clz(x)) + // then k = clz(x)`. + r := clz(x) + // Reduce range of x to (1, 2) * 2**96 + // ln(2^k * x) = k * ln(2) + ln(x) + x := shr(159, shl(r, x)) + + // Evaluate using a (8, 8)-term rational approximation. + // `p` is made monic, we will multiply by a scale factor later. + // forgefmt: disable-next-item + let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir. + sar(96, mul(add(43456485725739037958740375743393, + sar(96, mul(add(24828157081833163892658089445524, + sar(96, mul(add(3273285459638523848632254066296, + x), x))), x))), x)), 11111509109440967052023855526967) + p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857) + p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526) + p := sub(mul(p, x), shl(96, 795164235651350426258249787498)) + // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. + + // `q` is monic by convention. + let q := add(5573035233440673466300451813936, x) + q := add(71694874799317883764090561454958, sar(96, mul(x, q))) + q := add(283447036172924575727196451306956, sar(96, mul(x, q))) + q := add(401686690394027663651624208769553, sar(96, mul(x, q))) + q := add(204048457590392012362485061816622, sar(96, mul(x, q))) + q := add(31853899698501571402653359427138, sar(96, mul(x, q))) + q := add(909429971244387300277376558375, sar(96, mul(x, q))) + + // `p / q` is in the range `(0, 0.125) * 2**96`. + + // Finalization, we need to: + // - Multiply by the scale factor `s = 5.549…`. + // - Add `ln(2**96 / 10**18)`. + // - Add `k * ln(2)`. + // - Multiply by `10**18 / 2**96 = 5**18 >> 78`. + + // The q polynomial is known not to have zeros in the domain. + // No scaling required because p is already `2**96` too large. + p := sdiv(p, q) + // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`. + p := mul(1677202110996718588342820967067443963516166, p) + // Add `ln(2) * k * 5**18 * 2**192`. + // forgefmt: disable-next-item + p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p) + // Add `ln(2**96 / 10**18) * 5**18 * 2**192`. + p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p) + // Base conversion: mul `2**18 / 2**192`. + r := sar(174, p) + } + } + + /// @dev Returns `W_0(x)`, denominated in `WAD`. + /// See: https://en.wikipedia.org/wiki/Lambert_W_function + /// a.k.a. Product log function. This is an approximation of the principal branch. + /// Note: This function is an approximation. Monotonically increasing. + function lambertW0Wad(int256 x) + internal + pure + returns (int256 w) + { + + // forgefmt: disable-next-item + unchecked { + if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`. + (int256 wad, int256 p) = (int256(WAD), x); + uint256 c; // Whether we need to avoid catastrophic cancellation. + uint256 i = 4; // Number of iterations. + if (w <= 0x1ffffffffffff) { + if (-0x4000000000000 <= w) { + i = 1; // Inputs near zero only take one step to converge. + } else if (w <= -0x3ffffffffffffff) { + i = 32; // Inputs near `-1/e` take very long to converge. + } + } else if (uint256(w >> 63) == uint256(0)) { + /// @solidity memory-safe-assembly + assembly { + // Inline log2 for more performance, since the range is small. + let v := shr(49, w) + let l := shl(3, lt(0xff, v)) + l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)), + 0x0706060506020504060203020504030106050205030304010505030400000000)), 49) + w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13)) + c := gt(l, 60) + i := add(2, add(gt(l, 53), c)) + } + } else { + int256 ll = lnWad(w = lnWad(w)); + /// @solidity memory-safe-assembly + assembly { + // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`. + w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll)) + i := add(3, iszero(shr(68, x))) + c := iszero(shr(143, x)) + } + if (c == uint256(0)) { + do { // If `x` is big, use Newton's so that intermediate values won't overflow. + int256 e = expWad(w); + /// @solidity memory-safe-assembly + assembly { + let t := mul(w, div(e, wad)) + w := sub(w, sdiv(sub(t, x), div(add(e, t), wad))) + } + if (p <= w) break; + p = w; + } while (--i != uint256(0)); + /// @solidity memory-safe-assembly + assembly { + w := sub(w, sgt(w, 2)) + } + return w; + } + } + do { // Otherwise, use Halley's for faster convergence. + int256 e = expWad(w); + /// @solidity memory-safe-assembly + assembly { + let t := add(w, wad) + let s := sub(mul(w, e), mul(x, wad)) + w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t))))) + } + if (p <= w) break; + p = w; + } while (--i != c); + /// @solidity memory-safe-assembly + assembly { + w := sub(w, sgt(w, 2)) + } + // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of + // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation. + if (c == uint256(0)) return w; + int256 t = w | 1; + /// @solidity memory-safe-assembly + assembly { + x := sdiv(mul(x, wad), t) + } + x = (t * (wad + lnWad(x))); + /// @solidity memory-safe-assembly + assembly { + w := sdiv(x, add(wad, t)) + } + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* GENERAL NUMBER UTILITIES */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns `a * b == x * y`, with full precision. + function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y) + internal + pure + returns (bool result) + { + /// @solidity memory-safe-assembly + assembly { + result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0)))) + } + } + + /// @dev Calculates `floor(x * y / d)` with full precision. + /// Throws if result overflows a uint256 or when `d` is zero. + /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv + function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + // 512-bit multiply `[p1 p0] = x * y`. + // Compute the product mod `2**256` and mod `2**256 - 1` + // then use the Chinese Remainder Theorem to reconstruct + // the 512 bit result. The result is stored in two 256 + // variables such that `product = p1 * 2**256 + p0`. + + // Temporarily use `z` as `p0` to save gas. + z := mul(x, y) // Lower 256 bits of `x * y`. + for {} 1 {} { + // If overflows. + if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) { + let mm := mulmod(x, y, not(0)) + let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`. + + /*------------------- 512 by 256 division --------------------*/ + + // Make division exact by subtracting the remainder from `[p1 p0]`. + let r := mulmod(x, y, d) // Compute remainder using mulmod. + let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`. + // Make sure `z` is less than `2**256`. Also prevents `d == 0`. + // Placing the check here seems to give more optimal stack operations. + if iszero(gt(d, p1)) { + mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. + revert(0x1c, 0x04) + } + d := div(d, t) // Divide `d` by `t`, which is a power of two. + // Invert `d mod 2**256` + // Now that `d` is an odd number, it has an inverse + // modulo `2**256` such that `d * inv = 1 mod 2**256`. + // Compute the inverse by starting with a seed that is correct + // correct for four bits. That is, `d * inv = 1 mod 2**4`. + let inv := xor(2, mul(3, d)) + // Now use Newton-Raphson iteration to improve the precision. + // Thanks to Hensel's lifting lemma, this also works in modular + // arithmetic, doubling the correct bits in each step. + inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8 + inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16 + inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32 + inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64 + inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128 + z := mul( + // Divide [p1 p0] by the factors of two. + // Shift in bits from `p1` into `p0`. For this we need + // to flip `t` such that it is `2**256 / t`. + or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)), + mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256 + ) + break + } + z := div(z, d) + break + } + } + } + + /// @dev Calculates `floor(x * y / d)` with full precision. + /// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits. + /// Performs the full 512 bit calculation regardless. + function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d) + internal + pure + returns (uint256 z) + { + /// @solidity memory-safe-assembly + assembly { + z := mul(x, y) + let mm := mulmod(x, y, not(0)) + let p1 := sub(mm, add(z, lt(mm, z))) + let t := and(d, sub(0, d)) + let r := mulmod(x, y, d) + d := div(d, t) + let inv := xor(2, mul(3, d)) + inv := mul(inv, sub(2, mul(d, inv))) + inv := mul(inv, sub(2, mul(d, inv))) + inv := mul(inv, sub(2, mul(d, inv))) + inv := mul(inv, sub(2, mul(d, inv))) + inv := mul(inv, sub(2, mul(d, inv))) + z := mul( + or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)), + mul(sub(2, mul(d, inv)), inv) + ) + } + } + + /// @dev Calculates `floor(x * y / d)` with full precision, rounded up. + /// Throws if result overflows a uint256 or when `d` is zero. + /// Credit to Uniswap-v3-core under MIT license: + /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol + function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { + z = fullMulDiv(x, y, d); + /// @solidity memory-safe-assembly + assembly { + if mulmod(x, y, d) { + z := add(z, 1) + if iszero(z) { + mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. + revert(0x1c, 0x04) + } + } + } + } + + /// @dev Calculates `floor(x * y / 2 ** n)` with full precision. + /// Throws if result overflows a uint256. + /// Credit to Philogy under MIT license: + /// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol + function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + // Temporarily use `z` as `p0` to save gas. + z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`. + for {} 1 {} { + if iszero(or(iszero(x), eq(div(z, x), y))) { + let k := and(n, 0xff) // `n`, cleaned. + let mm := mulmod(x, y, not(0)) + let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`. + // | p1 | z | + // Before: | p1_0 ¦ p1_1 | z_0 ¦ z_1 | + // Final: | 0 ¦ p1_0 | p1_1 ¦ z_0 | + // Check that final `z` doesn't overflow by checking that p1_0 = 0. + if iszero(shr(k, p1)) { + z := add(shl(sub(256, k), p1), shr(k, z)) + break + } + mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. + revert(0x1c, 0x04) + } + z := shr(and(n, 0xff), z) + break + } + } + } + + /// @dev Returns `floor(x * y / d)`. + /// Reverts if `x * y` overflows, or `d` is zero. + function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mul(x, y) + // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`. + if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) { + mstore(0x00, 0xad251c27) // `MulDivFailed()`. + revert(0x1c, 0x04) + } + z := div(z, d) + } + } + + /// @dev Returns `ceil(x * y / d)`. + /// Reverts if `x * y` overflows, or `d` is zero. + function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mul(x, y) + // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`. + if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) { + mstore(0x00, 0xad251c27) // `MulDivFailed()`. + revert(0x1c, 0x04) + } + z := add(iszero(iszero(mod(z, d))), div(z, d)) + } + } + + /// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`. + function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) { + /// @solidity memory-safe-assembly + assembly { + let g := n + let r := mod(a, n) + for { let y := 1 } 1 {} { + let q := div(g, r) + let t := g + g := r + r := sub(t, mul(r, q)) + let u := x + x := y + y := sub(u, mul(y, q)) + if iszero(r) { break } + } + x := mul(eq(g, 1), add(x, mul(slt(x, 0), n))) + } + } + + /// @dev Returns `ceil(x / d)`. + /// Reverts if `d` is zero. + function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + if iszero(d) { + mstore(0x00, 0x65244e4e) // `DivFailed()`. + revert(0x1c, 0x04) + } + z := add(iszero(iszero(mod(x, d))), div(x, d)) + } + } + + /// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`. + function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mul(gt(x, y), sub(x, y)) + } + } + + /// @dev Returns `max(0, x - y)`. + function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mul(gt(x, y), sub(x, y)) + } + } + + /// @dev Returns `min(2 ** 256 - 1, x + y)`. + function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := or(sub(0, lt(add(x, y), x)), add(x, y)) + } + } + + /// @dev Returns `min(2 ** 256 - 1, x * y)`. + function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y)) + } + } + + /// @dev Returns `condition ? x : y`, without branching. + function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := xor(x, mul(xor(x, y), iszero(condition))) + } + } + + /// @dev Returns `condition ? x : y`, without branching. + function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) { + /// @solidity memory-safe-assembly + assembly { + z := xor(x, mul(xor(x, y), iszero(condition))) + } + } + + /// @dev Returns `condition ? x : y`, without branching. + function ternary(bool condition, address x, address y) internal pure returns (address z) { + /// @solidity memory-safe-assembly + assembly { + z := xor(x, mul(xor(x, y), iszero(condition))) + } + } + + /// @dev Returns `x != 0 ? x : y`, without branching. + function coalesce(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := or(x, mul(y, iszero(x))) + } + } + + /// @dev Returns `x != bytes32(0) ? x : y`, without branching. + function coalesce(bytes32 x, bytes32 y) internal pure returns (bytes32 z) { + /// @solidity memory-safe-assembly + assembly { + z := or(x, mul(y, iszero(x))) + } + } + + /// @dev Returns `x != address(0) ? x : y`, without branching. + function coalesce(address x, address y) internal pure returns (address z) { + /// @solidity memory-safe-assembly + assembly { + z := or(x, mul(y, iszero(shl(96, x)))) + } + } + + /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`. + /// Reverts if the computation overflows. + function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`. + if x { + z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x` + let half := shr(1, b) // Divide `b` by 2. + // Divide `y` by 2 every iteration. + for { y := shr(1, y) } y { y := shr(1, y) } { + let xx := mul(x, x) // Store x squared. + let xxRound := add(xx, half) // Round to the nearest number. + // Revert if `xx + half` overflowed, or if `x ** 2` overflows. + if or(lt(xxRound, xx), shr(128, x)) { + mstore(0x00, 0x49f7642b) // `RPowOverflow()`. + revert(0x1c, 0x04) + } + x := div(xxRound, b) // Set `x` to scaled `xxRound`. + // If `y` is odd: + if and(y, 1) { + let zx := mul(z, x) // Compute `z * x`. + let zxRound := add(zx, half) // Round to the nearest number. + // If `z * x` overflowed or `zx + half` overflowed: + if or(xor(div(zx, x), z), lt(zxRound, zx)) { + // Revert if `x` is non-zero. + if x { + mstore(0x00, 0x49f7642b) // `RPowOverflow()`. + revert(0x1c, 0x04) + } + } + z := div(zxRound, b) // Return properly scaled `zxRound`. + } + } + } + } + } + + /// @dev Returns the square root of `x`, rounded down. + function sqrt(uint256 x) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + // Step 1: Get the bit position of the most significant bit + // n = floor(log2(x)) + // For x ≈ 2^n, we know sqrt(x) ≈ 2^(n/2) + // We use (n+1)/2 instead of n/2 to round up slightly + // This gives a better initial approximation + // + // Formula: z = 2^((n+1)/2) = 2^(floor((n+1)/2)) + // Implemented as: z = 1 << ((n+1) >> 1) + z := shl(shr(1, sub(256, clz(x))), 1) + + /// (x/z + z) / 2 + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + + // If `x+1` is a perfect square, the Babylonian method cycles between + // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor. + // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division + z := sub(z, lt(div(x, z), z)) + } + } + + /// @dev Returns the cube root of `x`, rounded down. + /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license: + /// https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/utils/math.vy + /// Formally verified by xuwinnie: + /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf + function cbrt(uint256 x) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + // Initial guess z = 2^ceil((log2(x) + 2) / 3). + // Since log2(x) = 255 - clz(x), the expression shl((257 - clz(x)) / 3, 1) + // computes this over-estimate. Guaranteed ≥ cbrt(x) and safe for Newton-Raphson's. + z := shl(div(sub(257, clz(x)), 3), 1) + // Newton-Raphson's. + z := div(add(add(div(x, mul(z, z)), z), z), 3) + z := div(add(add(div(x, mul(z, z)), z), z), 3) + z := div(add(add(div(x, mul(z, z)), z), z), 3) + z := div(add(add(div(x, mul(z, z)), z), z), 3) + z := div(add(add(div(x, mul(z, z)), z), z), 3) + z := div(add(add(div(x, mul(z, z)), z), z), 3) + z := div(add(add(div(x, mul(z, z)), z), z), 3) + // Round down. + z := sub(z, lt(div(x, mul(z, z)), z)) + } + } + + /// @dev Returns the square root of `x`, denominated in `WAD`, rounded down. + function sqrtWad(uint256 x) internal pure returns (uint256 z) { + unchecked { + if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18); + z = (1 + sqrt(x)) * 10 ** 9; + z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1; + } + /// @solidity memory-safe-assembly + assembly { + z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down. + } + } + + /// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down. + /// Formally verified by xuwinnie: + /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf + function cbrtWad(uint256 x) internal pure returns (uint256 z) { + unchecked { + if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36); + z = (1 + cbrt(x)) * 10 ** 12; + z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3; + } + /// @solidity memory-safe-assembly + assembly { + let p := x + for {} 1 {} { + if iszero(shr(229, p)) { + if iszero(shr(199, p)) { + p := mul(p, 100000000000000000) // 10 ** 17. + break + } + p := mul(p, 100000000) // 10 ** 8. + break + } + if iszero(shr(249, p)) { p := mul(p, 100) } + break + } + let t := mulmod(mul(z, z), z, p) + z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down. + } + } + + /// @dev Returns `sqrt(x * y)`. Also called the geometric mean. + function mulSqrt(uint256 x, uint256 y) internal pure returns (uint256 z) { + if (x == y) return x; + uint256 p = rawMul(x, y); + if (y == rawDiv(p, x)) return sqrt(p); + for (z = saturatingMul(rawAdd(sqrt(x), 1), rawAdd(sqrt(y), 1));; z = avg(z, p)) { + if ((p = fullMulDivUnchecked(x, y, z)) >= z) break; + } + } + + /// @dev Returns the factorial of `x`. + function factorial(uint256 x) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := 1 + if iszero(lt(x, 58)) { + mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`. + revert(0x1c, 0x04) + } + for {} x { x := sub(x, 1) } { z := mul(z, x) } + } + } + + /// @dev Returns the log2 of `x`. + /// Equivalent to computing the index of the most significant bit (MSB) of `x`. + /// Returns 0 if `x` is zero. + function log2(uint256 x) internal pure returns (uint256 r) { + /// @solidity memory-safe-assembly + assembly { + r := sub(255, clz(or(x, 1))) + } + } + + /// @dev Returns the log2 of `x`, rounded up. + /// Returns 0 if `x` is zero. + function log2Up(uint256 x) internal pure returns (uint256 r) { + r = log2(x); + /// @solidity memory-safe-assembly + assembly { + r := add(r, lt(shl(r, 1), x)) + } + } + + /// @dev Returns the log10 of `x`. + /// Returns 0 if `x` is zero. + function log10(uint256 x) internal pure returns (uint256 r) { + /// @solidity memory-safe-assembly + assembly { + r := shr(12, mul(1233, sub(256, clz(x)))) + r := add(sub(iszero(x), lt(x, exp(10, r))), r) + } + } + + /// @dev Returns the log10 of `x`, rounded up. + /// Returns 0 if `x` is zero. + function log10Up(uint256 x) internal pure returns (uint256 r) { + r = log10(x); + /// @solidity memory-safe-assembly + assembly { + r := add(r, lt(exp(10, r), x)) + } + } + + /// @dev Returns the log256 of `x`. + /// Returns 0 if `x` is zero. + function log256(uint256 x) internal pure returns (uint256 r) { + /// @solidity memory-safe-assembly + assembly { + r := shr(3, sub(255, clz(or(x, 1)))) + } + } + + /// @dev Returns the log256 of `x`, rounded up. + /// Returns 0 if `x` is zero. + function log256Up(uint256 x) internal pure returns (uint256 r) { + r = log256(x); + /// @solidity memory-safe-assembly + assembly { + r := add(r, lt(shl(shl(3, r), 1), x)) + } + } + + /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`. + /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent). + function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) { + /// @solidity memory-safe-assembly + assembly { + mantissa := x + if mantissa { + if iszero(mod(mantissa, 1000000000000000000000000000000000)) { + mantissa := div(mantissa, 1000000000000000000000000000000000) + exponent := 33 + } + if iszero(mod(mantissa, 10000000000000000000)) { + mantissa := div(mantissa, 10000000000000000000) + exponent := add(exponent, 19) + } + if iszero(mod(mantissa, 1000000000000)) { + mantissa := div(mantissa, 1000000000000) + exponent := add(exponent, 12) + } + if iszero(mod(mantissa, 1000000)) { + mantissa := div(mantissa, 1000000) + exponent := add(exponent, 6) + } + if iszero(mod(mantissa, 10000)) { + mantissa := div(mantissa, 10000) + exponent := add(exponent, 4) + } + if iszero(mod(mantissa, 100)) { + mantissa := div(mantissa, 100) + exponent := add(exponent, 2) + } + if iszero(mod(mantissa, 10)) { + mantissa := div(mantissa, 10) + exponent := add(exponent, 1) + } + } + } + } + + /// @dev Convenience function for packing `x` into a smaller number using `sci`. + /// The `mantissa` will be in bits [7..255] (the upper 249 bits). + /// The `exponent` will be in bits [0..6] (the lower 7 bits). + /// Use `SafeCastLib` to safely ensure that the `packed` number is small + /// enough to fit in the desired unsigned integer type: + /// ``` + /// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether)); + /// ``` + function packSci(uint256 x) internal pure returns (uint256 packed) { + (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`. + /// @solidity memory-safe-assembly + assembly { + if shr(249, x) { + mstore(0x00, 0xce30380c) // `MantissaOverflow()`. + revert(0x1c, 0x04) + } + packed := or(shl(7, x), packed) + } + } + + /// @dev Convenience function for unpacking a packed number from `packSci`. + function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) { + unchecked { + unpacked = (packed >> 7) * 10 ** (packed & 0x7f); + } + } + + /// @dev Returns the average of `x` and `y`. Rounds towards zero. + function avg(uint256 x, uint256 y) internal pure returns (uint256 z) { + unchecked { + z = (x & y) + ((x ^ y) >> 1); + } + } + + /// @dev Returns the average of `x` and `y`. Rounds towards negative infinity. + function avg(int256 x, int256 y) internal pure returns (int256 z) { + unchecked { + z = (x >> 1) + (y >> 1) + (x & y & 1); + } + } + + /// @dev Returns the absolute value of `x`. + function abs(int256 x) internal pure returns (uint256 z) { + unchecked { + z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255); + } + } + + /// @dev Returns the absolute distance between `x` and `y`. + function dist(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y)) + } + } + + /// @dev Returns the absolute distance between `x` and `y`. + function dist(int256 x, int256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y)) + } + } + + /// @dev Returns the minimum of `x` and `y`. + function min(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := xor(x, mul(xor(x, y), lt(y, x))) + } + } + + /// @dev Returns the minimum of `x` and `y`. + function min(int256 x, int256 y) internal pure returns (int256 z) { + /// @solidity memory-safe-assembly + assembly { + z := xor(x, mul(xor(x, y), slt(y, x))) + } + } + + /// @dev Returns the maximum of `x` and `y`. + function max(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := xor(x, mul(xor(x, y), gt(y, x))) + } + } + + /// @dev Returns the maximum of `x` and `y`. + function max(int256 x, int256 y) internal pure returns (int256 z) { + /// @solidity memory-safe-assembly + assembly { + z := xor(x, mul(xor(x, y), sgt(y, x))) + } + } + + /// @dev Returns `x`, bounded to `minValue` and `maxValue`. + function clamp(uint256 x, uint256 minValue, uint256 maxValue) + internal + pure + returns (uint256 z) + { + /// @solidity memory-safe-assembly + assembly { + z := xor(x, mul(xor(x, minValue), gt(minValue, x))) + z := xor(z, mul(xor(z, maxValue), lt(maxValue, z))) + } + } + + /// @dev Returns `x`, bounded to `minValue` and `maxValue`. + function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) { + /// @solidity memory-safe-assembly + assembly { + z := xor(x, mul(xor(x, minValue), sgt(minValue, x))) + z := xor(z, mul(xor(z, maxValue), slt(maxValue, z))) + } + } + + /// @dev Returns greatest common divisor of `x` and `y`. + function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + for { z := x } y {} { + let t := y + y := mod(z, y) + z := t + } + } + } + + /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`, + /// with `t` clamped between `begin` and `end` (inclusive). + /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`). + /// If `begins == end`, returns `t <= begin ? a : b`. + function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end) + internal + pure + returns (uint256) + { + if (begin > end) (t, begin, end) = (~t, ~begin, ~end); + if (t <= begin) return a; + if (t >= end) return b; + unchecked { + if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin); + return a - fullMulDiv(a - b, t - begin, end - begin); + } + } + + /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`. + /// with `t` clamped between `begin` and `end` (inclusive). + /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`). + /// If `begins == end`, returns `t <= begin ? a : b`. + function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end) + internal + pure + returns (int256) + { + if (begin > end) (t, begin, end) = (~t, ~begin, ~end); + if (t <= begin) return a; + if (t >= end) return b; + // forgefmt: disable-next-item + unchecked { + if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a), + uint256(t - begin), uint256(end - begin))); + return int256(uint256(a) - fullMulDiv(uint256(a - b), + uint256(t - begin), uint256(end - begin))); + } + } + + /// @dev Returns if `x` is an even number. Some people may need this. + function isEven(uint256 x) internal pure returns (bool) { + return x & uint256(1) == uint256(0); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* RAW NUMBER OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns `x + y`, without checking for overflow. + function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) { + unchecked { + z = x + y; + } + } + + /// @dev Returns `x + y`, without checking for overflow. + function rawAdd(int256 x, int256 y) internal pure returns (int256 z) { + unchecked { + z = x + y; + } + } + + /// @dev Returns `x - y`, without checking for underflow. + function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) { + unchecked { + z = x - y; + } + } + + /// @dev Returns `x - y`, without checking for underflow. + function rawSub(int256 x, int256 y) internal pure returns (int256 z) { + unchecked { + z = x - y; + } + } + + /// @dev Returns `x * y`, without checking for overflow. + function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) { + unchecked { + z = x * y; + } + } + + /// @dev Returns `x * y`, without checking for overflow. + function rawMul(int256 x, int256 y) internal pure returns (int256 z) { + unchecked { + z = x * y; + } + } + + /// @dev Returns `x / y`, returning 0 if `y` is zero. + function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := div(x, y) + } + } + + /// @dev Returns `x / y`, returning 0 if `y` is zero. + function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) { + /// @solidity memory-safe-assembly + assembly { + z := sdiv(x, y) + } + } + + /// @dev Returns `x % y`, returning 0 if `y` is zero. + function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mod(x, y) + } + } + + /// @dev Returns `x % y`, returning 0 if `y` is zero. + function rawSMod(int256 x, int256 y) internal pure returns (int256 z) { + /// @solidity memory-safe-assembly + assembly { + z := smod(x, y) + } + } + + /// @dev Returns `(x + y) % d`, return 0 if `d` if zero. + function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := addmod(x, y, d) + } + } + + /// @dev Returns `(x * y) % d`, return 0 if `d` if zero. + function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { + /// @solidity memory-safe-assembly + assembly { + z := mulmod(x, y, d) + } + } +} diff --git a/src/utils/clz/LibBit.sol b/src/utils/clz/LibBit.sol new file mode 100644 index 000000000..6841db21a --- /dev/null +++ b/src/utils/clz/LibBit.sol @@ -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, add(s, s)), 0) // Zeroize slot after result. + mstore(0x40, add(0x40, add(o, add(s, s)))) // 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)) + } + } +} diff --git a/src/utils/clz/LibZip.sol b/src/utils/clz/LibZip.sol new file mode 100644 index 000000000..2c57e0797 --- /dev/null +++ b/src/utils/clz/LibZip.sol @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +/// @notice Library for compressing and decompressing bytes. +/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibZip.sol) +/// @author Calldata compression by clabby (https://github.com/clabby/op-kompressor) +/// @author FastLZ by ariya (https://github.com/ariya/FastLZ) +/// +/// @dev Note: +/// The accompanying solady.js library includes implementations of +/// FastLZ and calldata operations for convenience. +library LibZip { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* FAST LZ OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + // LZ77 implementation based on FastLZ. + // Equivalent to level 1 compression and decompression at the following commit: + // https://github.com/ariya/FastLZ/commit/344eb4025f9ae866ebf7a2ec48850f7113a97a42 + // Decompression is backwards compatible. + + /// @dev Returns the compressed `data`. + function flzCompress(bytes memory data) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + function ms8(d_, v_) -> _d { + mstore8(d_, v_) + _d := add(d_, 1) + } + function u24(p_) -> _u { + _u := mload(p_) + _u := or(shl(16, byte(2, _u)), or(shl(8, byte(1, _u)), byte(0, _u))) + } + function cmp(p_, q_, e_) -> _l { + for { e_ := sub(e_, q_) } lt(_l, e_) { _l := add(_l, 1) } { + e_ := mul(iszero(byte(0, xor(mload(add(p_, _l)), mload(add(q_, _l))))), e_) + } + } + function literals(runs_, src_, dest_) -> _o { + for { _o := dest_ } iszero(lt(runs_, 0x20)) { runs_ := sub(runs_, 0x20) } { + mstore(ms8(_o, 31), mload(src_)) + _o := add(_o, 0x21) + src_ := add(src_, 0x20) + } + if iszero(runs_) { leave } + mstore(ms8(_o, sub(runs_, 1)), mload(src_)) + _o := add(1, add(_o, runs_)) + } + function mt(l_, d_, o_) -> _o { + for { d_ := sub(d_, 1) } iszero(lt(l_, 263)) { l_ := sub(l_, 262) } { + o_ := ms8(ms8(ms8(o_, add(224, shr(8, d_))), 253), and(0xff, d_)) + } + if iszero(lt(l_, 7)) { + _o := ms8(ms8(ms8(o_, add(224, shr(8, d_))), sub(l_, 7)), and(0xff, d_)) + leave + } + _o := ms8(ms8(o_, add(shl(5, l_), shr(8, d_))), and(0xff, d_)) + } + function setHash(i_, v_) { + let p_ := add(mload(0x40), shl(2, i_)) + mstore(p_, xor(mload(p_), shl(224, xor(shr(224, mload(p_)), v_)))) + } + function getHash(i_) -> _h { + _h := shr(224, mload(add(mload(0x40), shl(2, i_)))) + } + function hash(v_) -> _r { + _r := and(shr(19, mul(2654435769, v_)), 0x1fff) + } + function setNextHash(ip_, ipStart_) -> _ip { + setHash(hash(u24(ip_)), sub(ip_, ipStart_)) + _ip := add(ip_, 1) + } + result := mload(0x40) + calldatacopy(result, calldatasize(), 0x8000) // Zeroize the hashmap. + let op := add(result, 0x8000) + let a := add(data, 0x20) + let ipStart := a + let ipLimit := sub(add(ipStart, mload(data)), 13) + for { let ip := add(2, a) } lt(ip, ipLimit) {} { + let r := 0 + let d := 0 + for {} 1 {} { + let s := u24(ip) + let h := hash(s) + r := add(ipStart, getHash(h)) + setHash(h, sub(ip, ipStart)) + d := sub(ip, r) + if iszero(lt(ip, ipLimit)) { break } + ip := add(ip, 1) + if iszero(gt(d, 0x1fff)) { if eq(s, u24(r)) { break } } + } + if iszero(lt(ip, ipLimit)) { break } + ip := sub(ip, 1) + if gt(ip, a) { op := literals(sub(ip, a), a, op) } + let l := cmp(add(r, 3), add(ip, 3), add(ipLimit, 9)) + op := mt(l, d, op) + ip := setNextHash(setNextHash(add(ip, l), ipStart), ipStart) + a := ip + } + // Copy the result to compact the memory, overwriting the hashmap. + let end := sub(literals(sub(add(ipStart, mload(data)), a), a, op), 0x7fe0) + let o := add(result, 0x20) + mstore(result, sub(end, o)) // Store the length. + for {} iszero(gt(o, end)) { o := add(o, 0x20) } { mstore(o, mload(add(o, 0x7fe0))) } + mstore(end, 0) // Zeroize the slot after the string. + mstore(0x40, add(end, 0x20)) // Allocate the memory. + } + } + + /// @dev Returns the decompressed `data`. + function flzDecompress(bytes memory data) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + let op := add(result, 0x20) + let end := add(add(data, 0x20), mload(data)) + for { data := add(data, 0x20) } lt(data, end) {} { + let w := mload(data) + let c := byte(0, w) + let t := shr(5, c) + if iszero(t) { + mstore(op, mload(add(data, 1))) + data := add(data, add(2, c)) + op := add(op, add(1, c)) + continue + } + for { + let g := eq(t, 7) + let l := add(2, xor(t, mul(g, xor(t, add(7, byte(1, w)))))) // M + let s := add(add(shl(8, and(0x1f, c)), byte(add(1, g), w)), 1) // R + let r := sub(op, s) + let f := xor(s, mul(gt(s, 0x20), xor(s, 0x20))) + let j := 0 + } 1 {} { + mstore(add(op, j), mload(add(r, j))) + j := add(j, f) + if lt(j, l) { continue } + data := add(data, add(2, g)) + op := add(op, l) + break + } + } + mstore(result, sub(op, add(result, 0x20))) // Store the length. + mstore(op, 0) // Zeroize the slot after the string. + mstore(0x40, add(op, 0x20)) // Allocate the memory. + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CALLDATA OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + // Calldata compression and decompression using selective run length encoding: + // - Sequences of 0x00 (up to 128 consecutive). + // - Sequences of 0xff (up to 32 consecutive). + // + // A run length encoded block consists of two bytes: + // (0) 0x00 + // (1) A control byte with the following bit layout: + // - [7] `0: 0x00, 1: 0xff`. + // - [0..6] `runLength - 1`. + // + // The first 4 bytes are bitwise negated so that the compressed calldata + // can be dispatched into the `fallback` and `receive` functions. + + /// @dev Returns the compressed `data`. + function cdCompress(bytes memory data) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + function min(x_, y_) -> _z { + _z := xor(x_, mul(xor(x_, y_), lt(y_, x_))) + } + + result := mload(0x40) + let end := add(data, mload(data)) + let m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f + let o := add(result, 0x20) + + for { let i := data } iszero(eq(i, end)) {} { + i := add(i, 1) + let c := byte(31, mload(i)) + + /// check data[i] == 00 or data[i] == ff + if iszero(c) { + for {} 1 {} { + let x := mload(add(i, 0x20)) + if iszero(x) { + let r := min(sub(end, i), 0x20) + r := min(sub(0x7f, c), r) + i := add(i, r) + c := add(c, r) + if iszero(gt(r, 0x1f)) { break } + continue + } + + let r := shr(3, clz(x)) + r := min(sub(end, i), r) + i := add(i, r) + c := add(c, r) + break + } + mstore(o, shl(240, c)) + o := add(o, 2) + continue + } + if eq(c, 0xff) { + let r := 0x20 + let x := not(mload(add(i, r))) + if x { r := shr(3, clz(x)) } + r := min(min(sub(end, i), r), 0x1f) + i := add(i, r) + mstore(o, shl(240, or(r, 0x80))) + o := add(o, 2) + continue + } + mstore8(o, c) + o := add(o, 1) + c := mload(add(i, 0x20)) + mstore(o, c) + // `.each(b => b == 0x00 || b == 0xff ? 0x80 : 0x00)`. + c := not(or(and(or(add(and(c, m), m), c), or(add(and(not(c), m), m), not(c))), m)) + let r := shl(7, lt(0x8421084210842108cc6318c6db6d54be, c)) // Save bytecode. + r := or(shl(6, lt(0xffffffffffffffff, shr(r, c))), r) + // forgefmt: disable-next-item + r := add(iszero(c), shr(3, xor(byte(and(0x1f, shr(byte(24, + mul(0x02040810204081, shr(r, c))), 0x8421084210842108cc6318c6db6d54be)), + 0xc0c8c8d0c8e8d0d8c8e8e0e8d0d8e0f0c8d0e8d0e0e0d8f0d0d0e0d8f8f8f8f8), r))) + r := min(sub(end, i), r) + o := add(o, r) + i := add(i, r) + } + // Bitwise negate the first 4 bytes. + mstore(add(result, 4), not(mload(add(result, 4)))) + mstore(result, sub(o, add(result, 0x20))) // Store the length. + mstore(o, 0) // Zeroize the slot after the string. + mstore(0x40, add(o, 0x20)) // Allocate the memory. + } + } + + /// @dev Returns the decompressed `data`. + function cdDecompress(bytes memory data) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + if mload(data) { + result := mload(0x40) + let s := add(data, 4) + let v := mload(s) + let end := add(add(0x20, data), mload(data)) + let m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f + let o := add(result, 0x20) + mstore(s, not(v)) // Bitwise negate the first 4 bytes. + for { let i := add(0x20, data) } 1 {} { + let c := mload(i) + if iszero(byte(0, c)) { + c := add(1, byte(1, c)) + if iszero(gt(c, 0x80)) { + i := add(i, 2) + calldatacopy(o, calldatasize(), c) // Fill with 0x00. + o := add(o, c) + if iszero(lt(i, end)) { break } + continue + } + i := add(i, 2) + mstore(o, not(0)) // Fill with 0xff. + o := add(o, sub(c, 0x80)) + if iszero(lt(i, end)) { break } + continue + } + mstore(o, c) + c := not(or(or(add(and(c, m), m), c), m)) // `.each(b => b == 0x00 ? 0x80 : 0x00)`. + let r := shl(7, lt(0x8421084210842108cc6318c6db6d54be, c)) // Save bytecode. + r := or(shl(6, lt(0xffffffffffffffff, shr(r, c))), r) + // forgefmt: disable-next-item + c := add(iszero(c), shr(3, xor(byte(and(0x1f, shr(byte(24, + mul(0x02040810204081, shr(r, c))), 0x8421084210842108cc6318c6db6d54be)), + 0xc0c8c8d0c8e8d0d8c8e8e0e8d0d8e0f0c8d0e8d0e0e0d8f0d0d0e0d8f8f8f8f8), r))) + o := add(o, c) + i := add(i, c) + if lt(i, end) { continue } + if gt(i, end) { o := sub(o, sub(i, end)) } + break + } + mstore(s, v) // Restore the first 4 bytes. + mstore(result, sub(o, add(result, 0x20))) // Store the length. + mstore(o, 0) // Zeroize the slot after the string. + mstore(0x40, add(o, 0x20)) // Allocate the memory. + } + } + } + + /// @dev To be called in the `fallback` function. + /// ``` + /// fallback() external payable { LibZip.cdFallback(); } + /// receive() external payable {} // Silence compiler warning to add a `receive` function. + /// ``` + /// For efficiency, this function will directly return the results, terminating the context. + /// If called internally, it must be called at the end of the function. + function cdFallback() internal { + /// @solidity memory-safe-assembly + assembly { + if iszero(calldatasize()) { return(calldatasize(), calldatasize()) } + let o := 0 + let f := not(3) // For negating the first 4 bytes. + for { let i := 0 } lt(i, calldatasize()) {} { + let c := byte(0, xor(add(i, f), calldataload(i))) + i := add(i, 1) + if iszero(c) { + let d := byte(0, xor(add(i, f), calldataload(i))) + i := add(i, 1) + // Fill with either 0xff or 0x00. + mstore(o, not(0)) + if iszero(gt(d, 0x7f)) { calldatacopy(o, calldatasize(), add(d, 1)) } + o := add(o, add(and(d, 0x7f), 1)) + continue + } + mstore8(o, c) + o := add(o, 1) + } + let success := delegatecall(gas(), address(), 0x00, o, codesize(), 0x00) + returndatacopy(0x00, 0x00, returndatasize()) + if iszero(success) { revert(0x00, returndatasize()) } + return(0x00, returndatasize()) + } + } +}