Skip to content

Commit 30a0a2d

Browse files
committed
wip
1 parent da1dfe6 commit 30a0a2d

File tree

2 files changed

+214
-0
lines changed

2 files changed

+214
-0
lines changed

contracts/utils/Compression.sol

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
/**
6+
* @dev Library for compressing and decompressing buffers. Supported compression algorithm:
7+
* * FastLZ (level1): WIP
8+
* * Calldata optimized: todo
9+
* * ZIP: todo
10+
*/
11+
library Compression {
12+
/**
13+
* @dev FastLZ level 1 decompression.
14+
*
15+
* Based on the reference implementation available here:
16+
* https://github.com/ariya/FastLZ?tab=readme-ov-file#decompressor-reference-implementation
17+
*/
18+
function flzDecompress(bytes memory input) internal pure returns (bytes memory output) {
19+
assembly ("memory-safe") {
20+
// Use new memory allocate at the FMP
21+
output := mload(0x40)
22+
23+
// Decrypted data location
24+
let ptr := add(output, 0x20)
25+
26+
// end of the input data (input.length after the beginning of the data)
27+
let end := add(add(input, 0x20), mload(input))
28+
29+
for {
30+
let data := add(input, 0x20)
31+
} lt(data, end) {} {
32+
let w := mload(data)
33+
let c := byte(0, w)
34+
let t := shr(5, c)
35+
36+
switch t
37+
case 0 {
38+
mstore(ptr, mload(add(data, 1)))
39+
data := add(data, add(2, c))
40+
ptr := add(ptr, add(1, c))
41+
}
42+
case 7 {
43+
let ofs := add(shl(8, and(c, 31)), byte(2, w))
44+
let len := add(9, byte(1, w))
45+
let ref := sub(sub(ptr, ofs), 1)
46+
let step := sub(0x20, mul(lt(ofs, 0x20), sub(0x1f, ofs))) // min(ofs+1, 0x20)
47+
for {
48+
let i := 0
49+
} lt(i, len) {
50+
i := add(i, step)
51+
} {
52+
mstore(add(ptr, i), mload(add(ref, i)))
53+
}
54+
data := add(data, 3)
55+
ptr := add(ptr, len)
56+
}
57+
default {
58+
let ofs := add(shl(8, and(c, 31)), byte(1, w))
59+
let len := add(2, t)
60+
let ref := sub(sub(ptr, ofs), 1)
61+
let step := sub(0x20, mul(lt(ofs, 0x20), sub(0x1f, ofs))) // min(ofs+1, 0x20)
62+
for {
63+
let i := 0
64+
} lt(i, len) {
65+
i := add(i, step)
66+
} {
67+
mstore(add(ptr, i), mload(add(ref, i)))
68+
}
69+
data := add(data, 2)
70+
ptr := add(ptr, len)
71+
}
72+
}
73+
mstore(output, sub(ptr, add(output, 0x20)))
74+
mstore(0x40, ptr)
75+
}
76+
}
77+
78+
/// Copied from solady
79+
function flzCompress(bytes memory input) internal pure returns (bytes memory output) {
80+
assembly ("memory-safe") {
81+
// store 8 bytes (value) at ptr, and return updated ptr
82+
function ms8(ptr, value) -> ret {
83+
mstore8(ptr, value)
84+
ret := add(ptr, 1)
85+
}
86+
// load 24 bytes from a given location in memory, right aligned and in reverse order
87+
function u24(ptr) -> value {
88+
value := mload(ptr)
89+
value := or(shl(16, byte(2, value)), or(shl(8, byte(1, value)), byte(0, value)))
90+
}
91+
function cmp(p_, q_, e_) -> _l {
92+
for {
93+
e_ := sub(e_, q_)
94+
} lt(_l, e_) {
95+
_l := add(_l, 1)
96+
} {
97+
e_ := mul(iszero(byte(0, xor(mload(add(p_, _l)), mload(add(q_, _l))))), e_)
98+
}
99+
}
100+
function literals(runs_, src_, dest_) -> _o {
101+
for {
102+
_o := dest_
103+
} iszero(lt(runs_, 0x20)) {
104+
runs_ := sub(runs_, 0x20)
105+
} {
106+
mstore(ms8(_o, 31), mload(src_))
107+
_o := add(_o, 0x21)
108+
src_ := add(src_, 0x20)
109+
}
110+
if iszero(runs_) {
111+
leave
112+
}
113+
mstore(ms8(_o, sub(runs_, 1)), mload(src_))
114+
_o := add(1, add(_o, runs_))
115+
}
116+
function mt(l_, d_, o_) -> _o {
117+
for {
118+
d_ := sub(d_, 1)
119+
} iszero(lt(l_, 263)) {
120+
l_ := sub(l_, 262)
121+
} {
122+
o_ := ms8(ms8(ms8(o_, add(224, shr(8, d_))), 253), and(0xff, d_))
123+
}
124+
if iszero(lt(l_, 7)) {
125+
_o := ms8(ms8(ms8(o_, add(224, shr(8, d_))), sub(l_, 7)), and(0xff, d_))
126+
leave
127+
}
128+
_o := ms8(ms8(o_, add(shl(5, l_), shr(8, d_))), and(0xff, d_))
129+
}
130+
function setHash(i_, v_) {
131+
let p_ := add(mload(0x40), shl(2, i_))
132+
mstore(p_, xor(mload(p_), shl(224, xor(shr(224, mload(p_)), v_))))
133+
}
134+
function getHash(i_) -> _h {
135+
_h := shr(224, mload(add(mload(0x40), shl(2, i_))))
136+
}
137+
function hash(v_) -> _r {
138+
_r := and(shr(19, mul(2654435769, v_)), 0x1fff)
139+
}
140+
function setNextHash(ip_, ipStart_) -> _ip {
141+
setHash(hash(u24(ip_)), sub(ip_, ipStart_))
142+
_ip := add(ip_, 1)
143+
}
144+
145+
output := mload(0x40)
146+
147+
calldatacopy(output, calldatasize(), 0x8000) // Zeroize the hashmap.
148+
let op := add(output, 0x8000)
149+
150+
let a := add(input, 0x20)
151+
152+
let ipStart := a
153+
let ipLimit := sub(add(ipStart, mload(input)), 13)
154+
for {
155+
let ip := add(2, a)
156+
} lt(ip, ipLimit) {} {
157+
let r := 0
158+
let d := 0
159+
for {} 1 {} {
160+
let s := u24(ip)
161+
let h := hash(s)
162+
r := add(ipStart, getHash(h))
163+
setHash(h, sub(ip, ipStart))
164+
d := sub(ip, r)
165+
if iszero(lt(ip, ipLimit)) {
166+
break
167+
}
168+
ip := add(ip, 1)
169+
if iszero(gt(d, 0x1fff)) {
170+
if eq(s, u24(r)) {
171+
break
172+
}
173+
}
174+
}
175+
if iszero(lt(ip, ipLimit)) {
176+
break
177+
}
178+
ip := sub(ip, 1)
179+
if gt(ip, a) {
180+
op := literals(sub(ip, a), a, op)
181+
}
182+
let l := cmp(add(r, 3), add(ip, 3), add(ipLimit, 9))
183+
op := mt(l, d, op)
184+
ip := setNextHash(setNextHash(add(ip, l), ipStart), ipStart)
185+
a := ip
186+
}
187+
// Copy the result to compact the memory, overwriting the hashmap.
188+
let end := sub(literals(sub(add(ipStart, mload(input)), a), a, op), 0x7fe0)
189+
let o := add(output, 0x20)
190+
mstore(output, sub(end, o)) // Store the length.
191+
for {} iszero(gt(o, end)) {
192+
o := add(o, 0x20)
193+
} {
194+
mstore(o, mload(add(o, 0x7fe0)))
195+
}
196+
197+
mstore(0x40, end)
198+
}
199+
}
200+
}

test/utils/Compression.t.sol

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {Test} from "forge-std/Test.sol";
6+
import {Compression} from "@openzeppelin/contracts/utils/Compression.sol";
7+
8+
contract CompressionTest is Test {
9+
using Compression for bytes;
10+
11+
function testEncodeDecode(bytes memory input) external pure {
12+
assertEq(input.flzCompress().flzDecompress(), input);
13+
}
14+
}

0 commit comments

Comments
 (0)