/* * 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. */ 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>. */ 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>. */ 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>. */ 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>. */ 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> */ 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> */ 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> */ 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> */ 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> */ 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> */ 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 : true; 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. */ public int getNiceBackReferenceLength() { return niceBackReferenceLength; } /** * Gets the maximum number of back-reference candidates to consider. */ public int getMaxCandidates() { return maxCandidates; } /** * Gets whether to perform lazy matching. */ public boolean getLazyMatching() { return lazyMatching; } /** * Gets 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; } }