Skip to content

Instantly share code, notes, and snippets.

@mdehoog
Last active January 31, 2024 05:44
Show Gist options
  • Save mdehoog/d263949d65bc518fa0e18f157d5ca590 to your computer and use it in GitHub Desktop.
Save mdehoog/d263949d65bc518fa0e18f157d5ca590 to your computer and use it in GitHub Desktop.
Length-only FastLZ
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
}
// 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