blob: fe892f37f6541dec5cee3bfb3d9824cb599f4432 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.commons.compress.compressors.lz77support;
/**
* Parameters of the {@link LZ77Compressor compressor}.
*/
public final class Parameters {
/**
* The hard-coded absolute minimal length of a back-reference.
*/
public static final int TRUE_MIN_BACK_REFERENCE_LENGTH = LZ77Compressor.NUMBER_OF_BYTES_IN_HASH;
/**
* Initializes the builder for the compressor's parameters with a
* <code>minBackReferenceLength</code> of 3 and <code>max*Length</code>
* equal to <code>windowSize - 1</code>.
*
* <p>It is recommended to not use this method directly but rather
* tune a pre-configured builder created by a format specific
* factory like {@link
* org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.</p>
*
* @param windowSize the size of the sliding window - this
* determines the maximum offset a back-reference can take. Must
* be a power of two.
* @throws IllegalArgumentException if windowSize is not a power of two.
* @return a builder configured for the given window size
*/
public static Builder builder(int windowSize) {
return new Builder(windowSize);
}
/**
* Builder for {@link Parameters} instances.
*/
public static class Builder {
private final int windowSize;
private int minBackReferenceLength, maxBackReferenceLength, maxOffset, maxLiteralLength;
private Integer niceBackReferenceLength, maxCandidates, lazyThreshold;
private Boolean lazyMatches;
private Builder(int windowSize) {
if (windowSize < 2 || !isPowerOfTwo(windowSize)) {
throw new IllegalArgumentException("windowSize must be a power of two");
}
this.windowSize = windowSize;
minBackReferenceLength = TRUE_MIN_BACK_REFERENCE_LENGTH;
maxBackReferenceLength = windowSize - 1;
maxOffset = windowSize - 1;
maxLiteralLength = windowSize;
}
/**
* Sets the mininal length of a back-reference.
*
* <p>Ensures <code>maxBackReferenceLength</code> is not
* smaller than <code>minBackReferenceLength</code>.
*
* <p>It is recommended to not use this method directly but
* rather tune a pre-configured builder created by a format
* specific factory like {@link
* org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.</p>
*
* @param minBackReferenceLength the minimal length of a back-reference found. A
* true minimum of 3 is hard-coded inside of this implemention
* but bigger lengths can be configured.
* @throws IllegalArgumentException if <code>windowSize</code>
* is smaller than <code>minBackReferenceLength</code>.
* @return the builder
*/
public Builder withMinBackReferenceLength(int minBackReferenceLength) {
this.minBackReferenceLength = Math.max(TRUE_MIN_BACK_REFERENCE_LENGTH, minBackReferenceLength);
if (windowSize < this.minBackReferenceLength) {
throw new IllegalArgumentException("minBackReferenceLength can't be bigger than windowSize");
}
if (maxBackReferenceLength < this.minBackReferenceLength) {
maxBackReferenceLength = this.minBackReferenceLength;
}
return this;
}
/**
* Sets the maximal length of a back-reference.
*
* <p>It is recommended to not use this method directly but
* rather tune a pre-configured builder created by a format
* specific factory like {@link
* org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.</p>
*
* @param maxBackReferenceLength maximal length of a
* back-reference found. A value smaller than
* <code>minBackReferenceLength</code> is interpreted as
* <code>minBackReferenceLength</code>. <code>maxBackReferenceLength</code>
* is capped at <code>windowSize - 1</code>.
* @return the builder
*/
public Builder withMaxBackReferenceLength(int maxBackReferenceLength) {
this.maxBackReferenceLength = maxBackReferenceLength < minBackReferenceLength ? minBackReferenceLength
: Math.min(maxBackReferenceLength, windowSize - 1);
return this;
}
/**
* Sets the maximal offset of a back-reference.
*
* <p>It is recommended to not use this method directly but
* rather tune a pre-configured builder created by a format
* specific factory like {@link
* org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.</p>
*
* @param maxOffset maximal offset of a back-reference. A
* non-positive value as well as values bigger than
* <code>windowSize - 1</code> are interpreted as <code>windowSize
* - 1</code>.
* @return the builder
*/
public Builder withMaxOffset(int maxOffset) {
this.maxOffset = maxOffset < 1 ? windowSize - 1 : Math.min(maxOffset, windowSize - 1);
return this;
}
/**
* Sets the maximal length of a literal block.
*
* <p>It is recommended to not use this method directly but
* rather tune a pre-configured builder created by a format
* specific factory like {@link
* org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.</p>
*
* @param maxLiteralLength maximal length of a literal
* block. Negative numbers and 0 as well as values bigger than
* <code>windowSize</code> are interpreted as
* <code>windowSize</code>.
* @return the builder
*/
public Builder withMaxLiteralLength(int maxLiteralLength) {
this.maxLiteralLength = maxLiteralLength < 1 ? windowSize
: Math.min(maxLiteralLength, windowSize);
return this;
}
/**
* Sets the "nice length" of a back-reference.
*
* <p>When a back-references if this size has been found, stop searching for longer back-references.</p>
*
* <p>This settings can be used to tune the tradeoff between compression speed and compression ratio.</p>
* @param niceLen the "nice length" of a back-reference
* @return the builder
*/
public Builder withNiceBackReferenceLength(int niceLen) {
niceBackReferenceLength = niceLen;
return this;
}
/**
* Sets the maximum number of back-reference candidates that should be consulted.
*
* <p>This settings can be used to tune the tradeoff between compression speed and compression ratio.</p>
* @param maxCandidates maximum number of back-reference candidates
* @return the builder
*/
public Builder withMaxNumberOfCandidates(int maxCandidates) {
this.maxCandidates = maxCandidates;
return this;
}
/**
* Sets whether lazy matching should be performed.
*
* <p>Lazy matching means that after a back-reference for a certain position has been found the compressor will
* try to find a longer match for the next position.</p>
*
* <p>Lazy matching is enabled by default and disabled when tuning for speed.</p>
* @param lazy whether lazy matching should be performed
* @return the builder
*/
public Builder withLazyMatching(boolean lazy) {
lazyMatches = lazy;
return this;
}
/**
* Sets the threshold for lazy matching.
*
* <p>Even if lazy matching is enabled it will not be performed if the length of the back-reference found for
* the current position is longer than this value.</p>
* @param threshold the threshold for lazy matching
* @return the builder
*/
public Builder withLazyThreshold(int threshold) {
lazyThreshold = threshold;
return this;
}
/**
* Changes the default setting for "nice back-reference length" and "maximum number of candidates" for improved
* compression speed at the cost of compression ratio.
*
* <p>Use this method after configuring "maximum back-reference length".</p>
* @return the builder
*/
public Builder tunedForSpeed() {
niceBackReferenceLength = Math.max(minBackReferenceLength, maxBackReferenceLength / 8);
maxCandidates = Math.max(32, windowSize / 1024);
lazyMatches = false;
lazyThreshold = minBackReferenceLength;
return this;
}
/**
* Changes the default setting for "nice back-reference length" and "maximum number of candidates" for improved
* compression ratio at the cost of compression speed.
*
* <p>Use this method after configuring "maximum back-reference length".</p>
* @return the builder
*/
public Builder tunedForCompressionRatio() {
niceBackReferenceLength = lazyThreshold = maxBackReferenceLength;
maxCandidates = Math.max(32, windowSize / 16);
lazyMatches = true;
return this;
}
/**
* Creates the {@link Parameters} instance.
* @return the configured {@link Parameters} instance.
*/
public Parameters build() {
// default settings tuned for a compromise of good compression and acceptable speed
int niceLen = niceBackReferenceLength != null ? niceBackReferenceLength
: Math.max(minBackReferenceLength, maxBackReferenceLength / 2);
int candidates = maxCandidates != null ? maxCandidates : Math.max(256, windowSize / 128);
boolean lazy = lazyMatches == null || lazyMatches;
int threshold = lazy ? (lazyThreshold != null ? lazyThreshold : niceLen) : minBackReferenceLength;
return new Parameters(windowSize, minBackReferenceLength, maxBackReferenceLength,
maxOffset, maxLiteralLength, niceLen, candidates, lazy, threshold);
}
}
private final int windowSize, minBackReferenceLength, maxBackReferenceLength, maxOffset, maxLiteralLength,
niceBackReferenceLength, maxCandidates, lazyThreshold;
private final boolean lazyMatching;
private Parameters(int windowSize, int minBackReferenceLength, int maxBackReferenceLength, int maxOffset,
int maxLiteralLength, int niceBackReferenceLength, int maxCandidates, boolean lazyMatching,
int lazyThreshold) {
this.windowSize = windowSize;
this.minBackReferenceLength = minBackReferenceLength;
this.maxBackReferenceLength = maxBackReferenceLength;
this.maxOffset = maxOffset;
this.maxLiteralLength = maxLiteralLength;
this.niceBackReferenceLength = niceBackReferenceLength;
this.maxCandidates = maxCandidates;
this.lazyMatching = lazyMatching;
this.lazyThreshold = lazyThreshold;
}
/**
* Gets the size of the sliding window - this determines the
* maximum offset a back-reference can take.
* @return the size of the sliding window
*/
public int getWindowSize() {
return windowSize;
}
/**
* Gets the minimal length of a back-reference found.
* @return the minimal length of a back-reference found
*/
public int getMinBackReferenceLength() {
return minBackReferenceLength;
}
/**
* Gets the maximal length of a back-reference found.
* @return the maximal length of a back-reference found
*/
public int getMaxBackReferenceLength() {
return maxBackReferenceLength;
}
/**
* Gets the maximal offset of a back-reference found.
* @return the maximal offset of a back-reference found
*/
public int getMaxOffset() {
return maxOffset;
}
/**
* Gets the maximal length of a literal block.
* @return the maximal length of a literal block
*/
public int getMaxLiteralLength() {
return maxLiteralLength;
}
/**
* Gets the length of a back-reference that is considered nice enough to stop searching for longer ones.
* @return the length of a back-reference that is considered nice enough to stop searching
*/
public int getNiceBackReferenceLength() {
return niceBackReferenceLength;
}
/**
* Gets the maximum number of back-reference candidates to consider.
* @return the maximum number of back-reference candidates to consider
*/
public int getMaxCandidates() {
return maxCandidates;
}
/**
* Gets whether to perform lazy matching.
* @return whether to perform lazy matching
*/
public boolean getLazyMatching() {
return lazyMatching;
}
/**
* Gets the threshold for lazy matching.
* @return the threshold for lazy matching
*/
public int getLazyMatchingThreshold() {
return lazyThreshold;
}
private static final boolean isPowerOfTwo(int x) {
// pre-condition: x > 0
return (x & (x - 1)) == 0;
}
}