Last active
January 31, 2024 05:44
-
-
Save mdehoog/d263949d65bc518fa0e18f157d5ca590 to your computer and use it in GitHub Desktop.
Length-only FastLZ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package compression | |
// FlzCompressLen returns the length of the data after compression through FastLZ, based on | |
// https://github.com/Vectorized/solady/blob/5315d937d79b335c668896d7533ac603adac5315/js/solady.js | |
func FlzCompressLen(ib []byte) uint32 { | |
n := uint32(0) | |
ht := make([]uint32, 8192) | |
u24 := func(i uint32) uint32 { | |
return uint32(ib[i]) | (uint32(ib[i+1]) << 8) | (uint32(ib[i+2]) << 16) | |
} | |
cmp := func(p uint32, q uint32, e uint32) uint32 { | |
l := uint32(0) | |
for e -= q; l < e; l++ { | |
if ib[p+l] != ib[q+l] { | |
e = 0 | |
} | |
} | |
return l | |
} | |
literals := func(r uint32) { | |
n += 0x21 * (r / 0x20) | |
r %= 0x20 | |
if r != 0 { | |
n += r + 1 | |
} | |
} | |
match := func(l uint32) { | |
l-- | |
n += 3 * (l / 262) | |
if l%262 >= 6 { | |
n += 3 | |
} else { | |
n += 2 | |
} | |
} | |
hash := func(v uint32) uint32 { | |
return ((2654435769 * v) >> 19) & 0x1fff | |
} | |
setNextHash := func(ip uint32) uint32 { | |
ht[hash(u24(ip))] = ip | |
return ip + 1 | |
} | |
a := uint32(0) | |
ipLimit := uint32(len(ib)) - 13 | |
if len(ib) < 13 { | |
ipLimit = 0 | |
} | |
for ip := a + 2; ip < ipLimit; { | |
r := uint32(0) | |
d := uint32(0) | |
for { | |
s := u24(ip) | |
h := hash(s) | |
r = ht[h] | |
ht[h] = ip | |
d = ip - r | |
if ip >= ipLimit { | |
break | |
} | |
ip++ | |
if d <= 0x1fff && s == u24(r) { | |
break | |
} | |
} | |
if ip >= ipLimit { | |
break | |
} | |
ip-- | |
if ip > a { | |
literals(ip - a) | |
} | |
l := cmp(r+3, ip+3, ipLimit+9) | |
match(l) | |
ip = setNextHash(setNextHash(ip + l)) | |
a = ip | |
} | |
literals(uint32(len(ib)) - a) | |
return n | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.15; | |
/// @title Compression | |
/// @notice Compression is a library containing compression utilities. | |
library Compression { | |
/// @notice Version of https://github.com/Vectorized/solady/blob/main/src/utils/LibZip.sol | |
/// that only returns the length of the data if it were to be compressed. This saves | |
/// gas over actually compressing the data, given we only need the length. | |
/// @dev Returns the length of the compressed data. | |
/// @custom:attribution Solady <https://github.com/Vectorized/solady> | |
function flzCompressLen(bytes memory data) internal pure returns (uint256 n) { | |
/// @solidity memory-safe-assembly | |
assembly { | |
function u24(p_) -> _u { | |
let w := mload(p_) | |
_u := or(shl(16, byte(2, w)), or(shl(8, byte(1, w)), byte(0, w))) | |
} | |
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_, n_) -> _n { | |
let d := div(runs_, 0x20) | |
runs_ := mod(runs_, 0x20) | |
_n := add(n_, mul(0x21, d)) | |
if iszero(runs_) { leave } | |
_n := add(1, add(_n, runs_)) | |
} | |
function match(l_, n_) -> _n { | |
l_ := sub(l_, 1) | |
n_ := add(n_, mul(3, div(l_, 262))) | |
if iszero(lt(mod(l_, 262), 6)) { | |
_n := add(n_, 3) | |
leave | |
} | |
_n := add(n_, 2) | |
} | |
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) | |
} | |
codecopy(mload(0x40), codesize(), 0x8000) // Zeroize the hashmap. | |
n := 0 | |
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) { n := literals(sub(ip, a), n) } | |
let l := cmp(add(r, 3), add(ip, 3), add(ipLimit, 9)) | |
n := match(l, n) | |
ip := setNextHash(setNextHash(add(ip, l), ipStart), ipStart) | |
a := ip | |
} | |
n := literals(sub(add(ipStart, mload(data)), a), n) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment