Skip to content

Commit 40fad74

Browse files
committed
add LZF compressor
1 parent 98e2126 commit 40fad74

2 files changed

Lines changed: 154 additions & 0 deletions

File tree

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package com.github.myibu.algorithm.compress;
2+
3+
import java.nio.charset.StandardCharsets;
4+
5+
public class LZFCompressor {
6+
private static final int LZF_HSLOT_BIAS = 0;
7+
8+
private static final int HLOG = 16;
9+
private static final int HSIZE = (1 << HLOG);
10+
11+
private static final int MAX_LIT = (1 << 5);
12+
private static final int MAX_OFF = (1 << 13);
13+
private static final int MAX_REF = ((1 << 8) + (1 << 3));
14+
15+
private static int FRST(byte[] p, int offset) {
16+
return ((p[offset+0] << 8) | p[offset+1]);
17+
}
18+
19+
private static int NEXT(int v, byte[] p, int offset) {
20+
return ((v << 8) | p[offset+2]);
21+
}
22+
23+
private static int IDX(int h) {
24+
return ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1));
25+
}
26+
27+
public int compress(byte[] in_data, int in_len,
28+
byte[] out_data, int out_len) {
29+
if (in_len == 0 || out_len == 0)
30+
return 0;
31+
int ip = 0, op = 0;
32+
int lit;
33+
lit = 0; op++; /* start run */
34+
int hslot;
35+
int[] htab = new int[HSIZE];
36+
int ref;
37+
int off;
38+
39+
int hval = FRST(in_data, ip);
40+
while (ip < in_len - 2) {
41+
hval = NEXT(hval, in_data, ip);
42+
hslot = IDX(hval);
43+
ref = htab[hslot];
44+
htab[hslot] = ip;
45+
46+
if ((off = ip - ref - 1) < MAX_OFF
47+
&& ref > 0
48+
&& in_data[ref + 2] == in_data[ip + 2]
49+
&& ((in_data[ref+1] << 8) | in_data[ref+0]) == ((in_data[ip+1] << 8) | in_data[ip+0])) {
50+
/* match found at *ref++ */
51+
int len = 2;
52+
int maxlen = in_len - ip - len;
53+
maxlen = maxlen > MAX_REF ? MAX_REF : maxlen;
54+
55+
if (op + 3 + 1 >= out_len) /* first a faster conservative test */
56+
if (op - (lit==0 ? 1: 0) + 3 + 1 >= out_len) /* second the exact but rare test */
57+
return 0;
58+
59+
out_data[op- lit - 1] = (byte)(lit - 1); /* end run */
60+
op -= (lit==0 ? 1: 0); /* undo run if length is zero */
61+
62+
do
63+
len++;
64+
while (len < maxlen && in_data[ref+len] == in_data[ip+len]);
65+
66+
len -= 2; /* len is now #octets - 1 */
67+
ip++;
68+
69+
if (len < 7) {
70+
out_data[op++] = (byte)((off >> 8) + (len << 5));
71+
} else {
72+
out_data[op++] = (byte)((off >> 8) + ( 7 << 5));
73+
out_data[op++] = (byte)(len - 7);
74+
}
75+
76+
out_data[op++] = (byte)(off);
77+
78+
lit = 0; op++; /* start run */
79+
80+
ip += len+1;
81+
82+
if (ip >= in_len - 2)
83+
break;
84+
85+
hval = FRST(in_data, ip);
86+
87+
hval = NEXT(hval, in_data, ip);
88+
htab[IDX(hval)] = ip - LZF_HSLOT_BIAS;
89+
ip++;
90+
91+
ip -= len + 1;
92+
93+
do
94+
{
95+
hval = NEXT(hval, in_data, ip);
96+
htab[IDX(hval)] = ip - LZF_HSLOT_BIAS;
97+
ip++;
98+
}
99+
while ((--len) > 0);
100+
}
101+
else {
102+
lit++;
103+
out_data[op++] = in_data[ip++];
104+
if (lit == MAX_LIT) {
105+
out_data[op- lit - 1] = (byte)(lit - 1); /* stop run */
106+
lit = 0; op++; /* start run */
107+
}
108+
}
109+
}
110+
111+
if (op + 3 > out_len) /* at most 3 bytes can be missing here */
112+
return 0;
113+
114+
while (ip < in_len) {
115+
lit++;
116+
out_data[op++] = in_data[ip++];
117+
if (lit == MAX_LIT) {
118+
out_data[op- lit - 1] = (byte)(lit - 1); /* stop run */
119+
lit = 0; op++; /* start run */
120+
}
121+
}
122+
123+
out_data[op- lit - 1] = (byte)(lit - 1); /* end run */
124+
op -= (lit==0 ? 1: 0); /* undo run if length is zero */
125+
126+
return op;
127+
}
128+
}

src/test/java/com/github/myibu/algorithm/AlgorithmTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.myibu.algorithm;
22

3+
import com.github.myibu.algorithm.compress.LZFCompressor;
34
import com.github.myibu.algorithm.data.Bits;
45
import com.github.myibu.algorithm.data.Bytes;
56
import com.github.myibu.algorithm.filter.*;
@@ -15,6 +16,7 @@
1516
import org.junit.Test;
1617

1718
import java.nio.charset.StandardCharsets;
19+
import java.util.Arrays;
1820
import java.util.Set;
1921

2022
public class AlgorithmTest {
@@ -128,4 +130,28 @@ public void testSensitiveWordFilter() {
128130
filter1.addWords(Set.of("黄色", "绿色", "红色"));
129131
System.out.println(filter1.searchWords("昨天我过马路的时候先遇到红色灯,再遇到绿灯"));
130132
}
133+
134+
@Test
135+
public void testLZFCompressor() {
136+
/**
137+
* 1111122222
138+
* hex: 01-31-31-20-00-00-32-20-00-00-32
139+
* binary: 01-49-49-32-00-00-50-32-00-00-50
140+
*
141+
* 111112222233333
142+
* hex: 01-31-31-20-00-00-32-40-00-00-33-20-00-01-33-33
143+
* binary: 01-49-49-32-00-00-50-64-00-00-51-32-00-01-51-51
144+
*
145+
* 111112222233333344444
146+
* '01-31-31-20-00-00-32-40-00-00-33-60-00-00-34-20-00-00-34'
147+
*/
148+
// [1, 49, 49, 32, 0, 32, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
149+
// byte[] in_data = "1111122222".getBytes(StandardCharsets.UTF_8);
150+
151+
byte[] in_data = "111112222233333344444".getBytes(StandardCharsets.UTF_8);
152+
byte[] out_data = new byte[in_data.length*2];
153+
LZFCompressor com = new LZFCompressor();
154+
com.compress(in_data, in_data.length, out_data, out_data.length);
155+
System.out.println(Arrays.toString(out_data));
156+
}
131157
}

0 commit comments

Comments
 (0)