| /* |
| * LZMAEncoderFast |
| * |
| * Authors: Lasse Collin <lasse.collin@tukaani.org> |
| * Igor Pavlov <http://7-zip.org/> |
| * |
| * This file has been put into the public domain. |
| * You can do whatever you want with this file. |
| */ |
| |
| package org.tukaani.xz.lzma; |
| |
| import org.tukaani.xz.lz.LZEncoder; |
| import org.tukaani.xz.lz.Matches; |
| import org.tukaani.xz.rangecoder.RangeEncoder; |
| |
| final class LZMAEncoderFast extends LZMAEncoder { |
| private static final int EXTRA_SIZE_BEFORE = 1; |
| private static final int EXTRA_SIZE_AFTER = MATCH_LEN_MAX - 1; |
| |
| private Matches matches = null; |
| |
| static int getMemoryUsage(int dictSize, int extraSizeBefore, int mf) { |
| return LZEncoder.getMemoryUsage( |
| dictSize, Math.max(extraSizeBefore, EXTRA_SIZE_BEFORE), |
| EXTRA_SIZE_AFTER, MATCH_LEN_MAX, mf); |
| } |
| |
| LZMAEncoderFast(RangeEncoder rc, int lc, int lp, int pb, |
| int dictSize, int extraSizeBefore, |
| int niceLen, int mf, int depthLimit) { |
| super(rc, LZEncoder.getInstance(dictSize, |
| Math.max(extraSizeBefore, |
| EXTRA_SIZE_BEFORE), |
| EXTRA_SIZE_AFTER, |
| niceLen, MATCH_LEN_MAX, |
| mf, depthLimit), |
| lc, lp, pb, dictSize, niceLen); |
| } |
| |
| private boolean changePair(int smallDist, int bigDist) { |
| return smallDist < (bigDist >>> 7); |
| } |
| |
| int getNextSymbol() { |
| // Get the matches for the next byte unless readAhead indicates |
| // that we already got the new matches during the previous call |
| // to this function. |
| if (readAhead == -1) |
| matches = getMatches(); |
| |
| back = -1; |
| |
| // Get the number of bytes available in the dictionary, but |
| // not more than the maximum match length. If there aren't |
| // enough bytes remaining to encode a match at all, return |
| // immediately to encode this byte as a literal. |
| int avail = Math.min(lz.getAvail(), MATCH_LEN_MAX); |
| if (avail < MATCH_LEN_MIN) |
| return 1; |
| |
| // Look for a match from the previous four match distances. |
| int bestRepLen = 0; |
| int bestRepIndex = 0; |
| for (int rep = 0; rep < REPS; ++rep) { |
| int len = lz.getMatchLen(reps[rep], avail); |
| if (len < MATCH_LEN_MIN) |
| continue; |
| |
| // If it is long enough, return it. |
| if (len >= niceLen) { |
| back = rep; |
| skip(len - 1); |
| return len; |
| } |
| |
| // Remember the index and length of the best repeated match. |
| if (len > bestRepLen) { |
| bestRepIndex = rep; |
| bestRepLen = len; |
| } |
| } |
| |
| int mainLen = 0; |
| int mainDist = 0; |
| |
| if (matches.count > 0) { |
| mainLen = matches.len[matches.count - 1]; |
| mainDist = matches.dist[matches.count - 1]; |
| |
| if (mainLen >= niceLen) { |
| back = mainDist + REPS; |
| skip(mainLen - 1); |
| return mainLen; |
| } |
| |
| while (matches.count > 1 |
| && mainLen == matches.len[matches.count - 2] + 1) { |
| if (!changePair(matches.dist[matches.count - 2], mainDist)) |
| break; |
| |
| --matches.count; |
| mainLen = matches.len[matches.count - 1]; |
| mainDist = matches.dist[matches.count - 1]; |
| } |
| |
| if (mainLen == MATCH_LEN_MIN && mainDist >= 0x80) |
| mainLen = 1; |
| } |
| |
| if (bestRepLen >= MATCH_LEN_MIN) { |
| if (bestRepLen + 1 >= mainLen |
| || (bestRepLen + 2 >= mainLen && mainDist >= (1 << 9)) |
| || (bestRepLen + 3 >= mainLen && mainDist >= (1 << 15))) { |
| back = bestRepIndex; |
| skip(bestRepLen - 1); |
| return bestRepLen; |
| } |
| } |
| |
| if (mainLen < MATCH_LEN_MIN || avail <= MATCH_LEN_MIN) |
| return 1; |
| |
| // Get the next match. Test if it is better than the current match. |
| // If so, encode the current byte as a literal. |
| matches = getMatches(); |
| |
| if (matches.count > 0) { |
| int newLen = matches.len[matches.count - 1]; |
| int newDist = matches.dist[matches.count - 1]; |
| |
| if ((newLen >= mainLen && newDist < mainDist) |
| || (newLen == mainLen + 1 |
| && !changePair(mainDist, newDist)) |
| || newLen > mainLen + 1 |
| || (newLen + 1 >= mainLen |
| && mainLen >= MATCH_LEN_MIN + 1 |
| && changePair(newDist, mainDist))) |
| return 1; |
| } |
| |
| int limit = Math.max(mainLen - 1, MATCH_LEN_MIN); |
| for (int rep = 0; rep < REPS; ++rep) |
| if (lz.getMatchLen(reps[rep], limit) == limit) |
| return 1; |
| |
| back = mainDist + REPS; |
| skip(mainLen - 2); |
| return mainLen; |
| } |
| } |