1 // Auto-generated: DO NOT EDIT
3 package net.jpountz.lz4;
5 import static net.jpountz.lz4.LZ4Constants.*;
6 import static net.jpountz.lz4.LZ4Utils.*;
8 import java.nio.ByteBuffer;
9 import java.util.Arrays;
11 import net.jpountz.lz4.LZ4Utils.Match;
12 import net.jpountz.util.ByteBufferUtils;
13 import net.jpountz.util.SafeUtils;
16 * High compression compressor.
18 final class LZ4HCJavaSafeCompressor extends LZ4Compressor {
20 public static final LZ4Compressor INSTANCE = new LZ4HCJavaSafeCompressor();
22 private final int maxAttempts;
23 final int compressionLevel;
25 LZ4HCJavaSafeCompressor() { this(DEFAULT_COMPRESSION_LEVEL); }
26 LZ4HCJavaSafeCompressor(int compressionLevel) {
27 this.maxAttempts = 1<<(compressionLevel-1);
28 this.compressionLevel = compressionLevel;
31 private class HashTable {
32 static final int MASK = MAX_DISTANCE - 1;
34 private final int base;
35 private final int[] hashTable;
36 private final short[] chainTable;
41 hashTable = new int[HASH_TABLE_SIZE_HC];
42 Arrays.fill(hashTable, -1);
43 chainTable = new short[MAX_DISTANCE];
46 private int hashPointer(byte[] bytes, int off) {
47 final int v = SafeUtils.readInt(bytes, off);
48 return hashPointer(v);
51 private int hashPointer(ByteBuffer bytes, int off) {
52 final int v = ByteBufferUtils.readInt(bytes, off);
53 return hashPointer(v);
56 private int hashPointer(int v) {
57 final int h = hashHC(v);
61 private int next(int off) {
62 return off - (chainTable[off & MASK] & 0xFFFF);
65 private void addHash(byte[] bytes, int off) {
66 final int v = SafeUtils.readInt(bytes, off);
70 private void addHash(ByteBuffer bytes, int off) {
71 final int v = ByteBufferUtils.readInt(bytes, off);
75 private void addHash(int v, int off) {
76 final int h = hashHC(v);
77 int delta = off - hashTable[h];
78 assert delta > 0 : delta;
79 if (delta >= MAX_DISTANCE) {
80 delta = MAX_DISTANCE - 1;
82 chainTable[off & MASK] = (short) delta;
86 void insert(int off, byte[] bytes) {
87 for (; nextToUpdate < off; ++nextToUpdate) {
88 addHash(bytes, nextToUpdate);
92 void insert(int off, ByteBuffer bytes) {
93 for (; nextToUpdate < off; ++nextToUpdate) {
94 addHash(bytes, nextToUpdate);
100 boolean insertAndFindBestMatch(byte[] buf, int off, int matchLimit, Match match) {
108 int ref = hashPointer(buf, off);
110 if (ref >= off - 4 && ref <= off && ref >= base) { // potential repetition
111 if (LZ4SafeUtils.readIntEquals(buf, ref, off)) { // confirmed
113 repl = match.len = MIN_MATCH + LZ4SafeUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit);
119 for (int i = 0; i < maxAttempts; ++i) {
120 if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) {
123 if (LZ4SafeUtils.readIntEquals(buf, ref, off)) {
124 final int matchLen = MIN_MATCH + LZ4SafeUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit);
125 if (matchLen > match.len) {
127 match.len = matchLen;
135 final int end = off + repl - (MIN_MATCH - 1);
136 while (ptr < end - delta) {
137 chainTable[ptr & MASK] = (short) delta; // pre load
141 chainTable[ptr & MASK] = (short) delta;
142 hashTable[hashHC(SafeUtils.readInt(buf, ptr))] = ptr;
148 return match.len != 0;
151 boolean insertAndFindWiderMatch(byte[] buf, int off, int startLimit, int matchLimit, int minLen, Match match) {
156 //final int delta = off - startLimit;
157 int ref = hashPointer(buf, off);
158 for (int i = 0; i < maxAttempts; ++i) {
159 if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) {
162 if (LZ4SafeUtils.readIntEquals(buf, ref, off)) {
163 final int matchLenForward = MIN_MATCH +LZ4SafeUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit);
164 final int matchLenBackward = LZ4SafeUtils.commonBytesBackward(buf, ref, off, base, startLimit);
165 final int matchLen = matchLenBackward + matchLenForward;
166 if (matchLen > match.len) {
167 match.len = matchLen;
168 match.ref = ref - matchLenBackward;
169 match.start = off - matchLenBackward;
175 return match.len > minLen;
179 boolean insertAndFindBestMatch(ByteBuffer buf, int off, int matchLimit, Match match) {
187 int ref = hashPointer(buf, off);
189 if (ref >= off - 4 && ref <= off && ref >= base) { // potential repetition
190 if (LZ4ByteBufferUtils.readIntEquals(buf, ref, off)) { // confirmed
192 repl = match.len = MIN_MATCH + LZ4ByteBufferUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit);
198 for (int i = 0; i < maxAttempts; ++i) {
199 if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) {
202 if (LZ4ByteBufferUtils.readIntEquals(buf, ref, off)) {
203 final int matchLen = MIN_MATCH + LZ4ByteBufferUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit);
204 if (matchLen > match.len) {
206 match.len = matchLen;
214 final int end = off + repl - (MIN_MATCH - 1);
215 while (ptr < end - delta) {
216 chainTable[ptr & MASK] = (short) delta; // pre load
220 chainTable[ptr & MASK] = (short) delta;
221 hashTable[hashHC(ByteBufferUtils.readInt(buf, ptr))] = ptr;
227 return match.len != 0;
230 boolean insertAndFindWiderMatch(ByteBuffer buf, int off, int startLimit, int matchLimit, int minLen, Match match) {
235 //final int delta = off - startLimit;
236 int ref = hashPointer(buf, off);
237 for (int i = 0; i < maxAttempts; ++i) {
238 if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) {
241 if (LZ4ByteBufferUtils.readIntEquals(buf, ref, off)) {
242 final int matchLenForward = MIN_MATCH +LZ4ByteBufferUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit);
243 final int matchLenBackward = LZ4ByteBufferUtils.commonBytesBackward(buf, ref, off, base, startLimit);
244 final int matchLen = matchLenBackward + matchLenForward;
245 if (matchLen > match.len) {
246 match.len = matchLen;
247 match.ref = ref - matchLenBackward;
248 match.start = off - matchLenBackward;
254 return match.len > minLen;
261 public int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen) {
263 SafeUtils.checkRange(src, srcOff, srcLen);
264 SafeUtils.checkRange(dest, destOff, maxDestLen);
266 final int srcEnd = srcOff + srcLen;
267 final int destEnd = destOff + maxDestLen;
268 final int mfLimit = srcEnd - MF_LIMIT;
269 final int matchLimit = srcEnd - LAST_LITERALS;
275 final HashTable ht = new HashTable(srcOff);
276 final Match match0 = new Match();
277 final Match match1 = new Match();
278 final Match match2 = new Match();
279 final Match match3 = new Match();
282 while (sOff < mfLimit) {
283 if (!ht.insertAndFindBestMatch(src, sOff, matchLimit, match1)) {
288 // saved, in case we would skip too much
289 copyTo(match1, match0);
293 assert match1.start >= anchor;
294 if (match1.end() >= mfLimit
295 || !ht.insertAndFindWiderMatch(src, match1.end() - 2, match1.start + 1, matchLimit, match1.len, match2)) {
297 dOff = LZ4SafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd);
298 anchor = sOff = match1.end();
302 if (match0.start < match1.start) {
303 if (match2.start < match1.start + match0.len) { // empirical
304 copyTo(match0, match1);
307 assert match2.start > match1.start;
309 if (match2.start - match1.start < 3) { // First Match too small : removed
310 copyTo(match2, match1);
316 if (match2.start - match1.start < OPTIMAL_ML) {
317 int newMatchLen = match1.len;
318 if (newMatchLen > OPTIMAL_ML) {
319 newMatchLen = OPTIMAL_ML;
321 if (match1.start + newMatchLen > match2.end() - MIN_MATCH) {
322 newMatchLen = match2.start - match1.start + match2.len - MIN_MATCH;
324 final int correction = newMatchLen - (match2.start - match1.start);
325 if (correction > 0) {
326 match2.fix(correction);
330 if (match2.start + match2.len >= mfLimit
331 || !ht.insertAndFindWiderMatch(src, match2.end() - 3, match2.start, matchLimit, match2.len, match3)) {
332 // no better match -> 2 sequences to encode
333 if (match2.start < match1.end()) {
334 match1.len = match2.start - match1.start;
337 dOff = LZ4SafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd);
338 anchor = sOff = match1.end();
340 dOff = LZ4SafeUtils.encodeSequence(src, anchor, match2.start, match2.ref, match2.len, dest, dOff, destEnd);
341 anchor = sOff = match2.end();
345 if (match3.start < match1.end() + 3) { // Not enough space for match 2 : remove it
346 if (match3.start >= match1.end()) { // // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1
347 if (match2.start < match1.end()) {
348 final int correction = match1.end() - match2.start;
349 match2.fix(correction);
350 if (match2.len < MIN_MATCH) {
351 copyTo(match3, match2);
355 dOff = LZ4SafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd);
356 anchor = sOff = match1.end();
358 copyTo(match3, match1);
359 copyTo(match2, match0);
364 copyTo(match3, match2);
368 // OK, now we have 3 ascending matches; let's write at least the first one
369 if (match2.start < match1.end()) {
370 if (match2.start - match1.start < ML_MASK) {
371 if (match1.len > OPTIMAL_ML) {
372 match1.len = OPTIMAL_ML;
374 if (match1.end() > match2.end() - MIN_MATCH) {
375 match1.len = match2.end() - match1.start - MIN_MATCH;
377 final int correction = match1.end() - match2.start;
378 match2.fix(correction);
380 match1.len = match2.start - match1.start;
384 dOff = LZ4SafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd);
385 anchor = sOff = match1.end();
387 copyTo(match2, match1);
388 copyTo(match3, match2);
397 dOff = LZ4SafeUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd);
398 return dOff - destOff;
403 public int compress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen) {
405 if (src.hasArray() && dest.hasArray()) {
406 return compress(src.array(), srcOff + src.arrayOffset(), srcLen, dest.array(), destOff + dest.arrayOffset(), maxDestLen);
408 src = ByteBufferUtils.inNativeByteOrder(src);
409 dest = ByteBufferUtils.inNativeByteOrder(dest);
411 ByteBufferUtils.checkRange(src, srcOff, srcLen);
412 ByteBufferUtils.checkRange(dest, destOff, maxDestLen);
414 final int srcEnd = srcOff + srcLen;
415 final int destEnd = destOff + maxDestLen;
416 final int mfLimit = srcEnd - MF_LIMIT;
417 final int matchLimit = srcEnd - LAST_LITERALS;
423 final HashTable ht = new HashTable(srcOff);
424 final Match match0 = new Match();
425 final Match match1 = new Match();
426 final Match match2 = new Match();
427 final Match match3 = new Match();
430 while (sOff < mfLimit) {
431 if (!ht.insertAndFindBestMatch(src, sOff, matchLimit, match1)) {
436 // saved, in case we would skip too much
437 copyTo(match1, match0);
441 assert match1.start >= anchor;
442 if (match1.end() >= mfLimit
443 || !ht.insertAndFindWiderMatch(src, match1.end() - 2, match1.start + 1, matchLimit, match1.len, match2)) {
445 dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd);
446 anchor = sOff = match1.end();
450 if (match0.start < match1.start) {
451 if (match2.start < match1.start + match0.len) { // empirical
452 copyTo(match0, match1);
455 assert match2.start > match1.start;
457 if (match2.start - match1.start < 3) { // First Match too small : removed
458 copyTo(match2, match1);
464 if (match2.start - match1.start < OPTIMAL_ML) {
465 int newMatchLen = match1.len;
466 if (newMatchLen > OPTIMAL_ML) {
467 newMatchLen = OPTIMAL_ML;
469 if (match1.start + newMatchLen > match2.end() - MIN_MATCH) {
470 newMatchLen = match2.start - match1.start + match2.len - MIN_MATCH;
472 final int correction = newMatchLen - (match2.start - match1.start);
473 if (correction > 0) {
474 match2.fix(correction);
478 if (match2.start + match2.len >= mfLimit
479 || !ht.insertAndFindWiderMatch(src, match2.end() - 3, match2.start, matchLimit, match2.len, match3)) {
480 // no better match -> 2 sequences to encode
481 if (match2.start < match1.end()) {
482 match1.len = match2.start - match1.start;
485 dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd);
486 anchor = sOff = match1.end();
488 dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match2.start, match2.ref, match2.len, dest, dOff, destEnd);
489 anchor = sOff = match2.end();
493 if (match3.start < match1.end() + 3) { // Not enough space for match 2 : remove it
494 if (match3.start >= match1.end()) { // // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1
495 if (match2.start < match1.end()) {
496 final int correction = match1.end() - match2.start;
497 match2.fix(correction);
498 if (match2.len < MIN_MATCH) {
499 copyTo(match3, match2);
503 dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd);
504 anchor = sOff = match1.end();
506 copyTo(match3, match1);
507 copyTo(match2, match0);
512 copyTo(match3, match2);
516 // OK, now we have 3 ascending matches; let's write at least the first one
517 if (match2.start < match1.end()) {
518 if (match2.start - match1.start < ML_MASK) {
519 if (match1.len > OPTIMAL_ML) {
520 match1.len = OPTIMAL_ML;
522 if (match1.end() > match2.end() - MIN_MATCH) {
523 match1.len = match2.end() - match1.start - MIN_MATCH;
525 final int correction = match1.end() - match2.start;
526 match2.fix(correction);
528 match1.len = match2.start - match1.start;
532 dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd);
533 anchor = sOff = match1.end();
535 copyTo(match2, match1);
536 copyTo(match3, match2);
545 dOff = LZ4ByteBufferUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd);
546 return dOff - destOff;