Parameters.java
/*
* 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 {
/**
* 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(final 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;
}
/**
* 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
final int niceLen = niceBackReferenceLength != null ? niceBackReferenceLength : Math.max(minBackReferenceLength, maxBackReferenceLength / 2);
final int candidates = maxCandidates != null ? maxCandidates : Math.max(256, windowSize / 128);
final boolean lazy = lazyMatches == null || lazyMatches;
final int threshold = lazy ? lazyThreshold != null ? lazyThreshold : niceLen : minBackReferenceLength;
return new Parameters(windowSize, minBackReferenceLength, maxBackReferenceLength, maxOffset, maxLiteralLength, niceLen, candidates, lazy,
threshold);
}
/**
* 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;
}
/**
* 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;
}
/**
* 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(final 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(final int threshold) {
lazyThreshold = threshold;
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} is interpreted as
* {@code minBackReferenceLength}. {@code maxBackReferenceLength} is capped at {@code windowSize - 1}.
* @return the builder
*/
public Builder withMaxBackReferenceLength(final int maxBackReferenceLength) {
this.maxBackReferenceLength = maxBackReferenceLength < minBackReferenceLength ? minBackReferenceLength
: Math.min(maxBackReferenceLength, 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} are interpreted as
* {@code windowSize}.
* @return the builder
*/
public Builder withMaxLiteralLength(final int maxLiteralLength) {
this.maxLiteralLength = maxLiteralLength < 1 ? windowSize : Math.min(maxLiteralLength, windowSize);
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(final int maxCandidates) {
this.maxCandidates = maxCandidates;
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} are interpreted as
* {@code windowSize * - 1}.
* @return the builder
*/
public Builder withMaxOffset(final int maxOffset) {
this.maxOffset = maxOffset < 1 ? windowSize - 1 : Math.min(maxOffset, windowSize - 1);
return this;
}
/**
* Sets the minimal length of a back-reference.
* <p>
* Ensures {@code maxBackReferenceLength} is not smaller than {@code minBackReferenceLength}.
* </p>
* <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 implementation but
* bigger lengths can be configured.
* @throws IllegalArgumentException if {@code windowSize} is smaller than {@code minBackReferenceLength}.
* @return the builder
*/
public Builder withMinBackReferenceLength(final 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 "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(final int niceLen) {
niceBackReferenceLength = niceLen;
return this;
}
}
/**
* 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} of 3 and {@code max*Length} equal to
* {@code windowSize - 1}.
* <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(final int windowSize) {
return new Builder(windowSize);
}
private static boolean isPowerOfTwo(final int x) {
// pre-condition: x > 0
return (x & x - 1) == 0;
}
private final int windowSize, minBackReferenceLength, maxBackReferenceLength, maxOffset, maxLiteralLength, niceBackReferenceLength, maxCandidates,
lazyThreshold;
private final boolean lazyMatching;
private Parameters(final int windowSize, final int minBackReferenceLength, final int maxBackReferenceLength, final int maxOffset,
final int maxLiteralLength, final int niceBackReferenceLength, final int maxCandidates, final boolean lazyMatching, final 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 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;
}
/**
* 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 maximum number of back-reference candidates to consider.
*
* @return the maximum number of back-reference candidates to consider
*/
public int getMaxCandidates() {
return maxCandidates;
}
/**
* Gets the maximal length of a literal block.
*
* @return the maximal length of a literal block
*/
public int getMaxLiteralLength() {
return maxLiteralLength;
}
/**
* 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 minimal length of a back-reference found.
*
* @return the minimal length of a back-reference found
*/
public int getMinBackReferenceLength() {
return minBackReferenceLength;
}
/**
* 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 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;
}
}