Parameters.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  * http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied.  See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */
  19. package org.apache.commons.compress.compressors.lz77support;

  20. /**
  21.  * Parameters of the {@link LZ77Compressor compressor}.
  22.  */
  23. public final class Parameters {

  24.     /**
  25.      * Builder for {@link Parameters} instances.
  26.      */
  27.     public static class Builder {

  28.         private final int windowSize;
  29.         private int minBackReferenceLength, maxBackReferenceLength, maxOffset, maxLiteralLength;
  30.         private Integer niceBackReferenceLength, maxCandidates, lazyThreshold;
  31.         private Boolean lazyMatches;

  32.         private Builder(final int windowSize) {
  33.             if (windowSize < 2 || !isPowerOfTwo(windowSize)) {
  34.                 throw new IllegalArgumentException("windowSize must be a power of two");
  35.             }
  36.             this.windowSize = windowSize;
  37.             minBackReferenceLength = TRUE_MIN_BACK_REFERENCE_LENGTH;
  38.             maxBackReferenceLength = windowSize - 1;
  39.             maxOffset = windowSize - 1;
  40.             maxLiteralLength = windowSize;
  41.         }

  42.         /**
  43.          * Creates the {@link Parameters} instance.
  44.          *
  45.          * @return the configured {@link Parameters} instance.
  46.          */
  47.         public Parameters build() {
  48.             // default settings tuned for a compromise of good compression and acceptable speed
  49.             final int niceLen = niceBackReferenceLength != null ? niceBackReferenceLength : Math.max(minBackReferenceLength, maxBackReferenceLength / 2);
  50.             final int candidates = maxCandidates != null ? maxCandidates : Math.max(256, windowSize / 128);
  51.             final boolean lazy = lazyMatches == null || lazyMatches;
  52.             final int threshold = lazy ? lazyThreshold != null ? lazyThreshold : niceLen : minBackReferenceLength;

  53.             return new Parameters(windowSize, minBackReferenceLength, maxBackReferenceLength, maxOffset, maxLiteralLength, niceLen, candidates, lazy,
  54.                     threshold);
  55.         }

  56.         /**
  57.          * Changes the default setting for "nice back-reference length" and "maximum number of candidates" for improved compression ratio at the cost of
  58.          * compression speed.
  59.          * <p>
  60.          * Use this method after configuring "maximum back-reference length".
  61.          * </p>
  62.          *
  63.          * @return the builder
  64.          */
  65.         public Builder tunedForCompressionRatio() {
  66.             niceBackReferenceLength = lazyThreshold = maxBackReferenceLength;
  67.             maxCandidates = Math.max(32, windowSize / 16);
  68.             lazyMatches = true;
  69.             return this;
  70.         }

  71.         /**
  72.          * Changes the default setting for "nice back-reference length" and "maximum number of candidates" for improved compression speed at the cost of
  73.          * compression ratio.
  74.          * <p>
  75.          * Use this method after configuring "maximum back-reference length".
  76.          * </p>
  77.          *
  78.          * @return the builder
  79.          */
  80.         public Builder tunedForSpeed() {
  81.             niceBackReferenceLength = Math.max(minBackReferenceLength, maxBackReferenceLength / 8);
  82.             maxCandidates = Math.max(32, windowSize / 1024);
  83.             lazyMatches = false;
  84.             lazyThreshold = minBackReferenceLength;
  85.             return this;
  86.         }

  87.         /**
  88.          * Sets whether lazy matching should be performed.
  89.          * <p>
  90.          * 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
  91.          * position.
  92.          * </p>
  93.          * <p>
  94.          * Lazy matching is enabled by default and disabled when tuning for speed.
  95.          * </p>
  96.          *
  97.          * @param lazy whether lazy matching should be performed
  98.          * @return the builder
  99.          */
  100.         public Builder withLazyMatching(final boolean lazy) {
  101.             lazyMatches = lazy;
  102.             return this;
  103.         }

  104.         /**
  105.          * Sets the threshold for lazy matching.
  106.          * <p>
  107.          * 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
  108.          * value.
  109.          * </p>
  110.          *
  111.          * @param threshold the threshold for lazy matching
  112.          * @return the builder
  113.          */
  114.         public Builder withLazyThreshold(final int threshold) {
  115.             lazyThreshold = threshold;
  116.             return this;
  117.         }

  118.         /**
  119.          * Sets the maximal length of a back-reference.
  120.          * <p>
  121.          * It is recommended to not use this method directly but rather tune a pre-configured builder created by a format specific factory like
  122.          * {@link org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.
  123.          * </p>
  124.          *
  125.          * @param maxBackReferenceLength maximal length of a back-reference found. A value smaller than {@code minBackReferenceLength} is interpreted as
  126.          *                               {@code minBackReferenceLength}. {@code maxBackReferenceLength} is capped at {@code windowSize - 1}.
  127.          * @return the builder
  128.          */
  129.         public Builder withMaxBackReferenceLength(final int maxBackReferenceLength) {
  130.             this.maxBackReferenceLength = maxBackReferenceLength < minBackReferenceLength ? minBackReferenceLength
  131.                     : Math.min(maxBackReferenceLength, windowSize - 1);
  132.             return this;
  133.         }

  134.         /**
  135.          * Sets the maximal length of a literal block.
  136.          * <p>
  137.          * It is recommended to not use this method directly but rather tune a pre-configured builder created by a format specific factory like
  138.          * {@link org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.
  139.          * </p>
  140.          *
  141.          * @param maxLiteralLength maximal length of a literal block. Negative numbers and 0 as well as values bigger than {@code windowSize} are interpreted as
  142.          *                         {@code windowSize}.
  143.          * @return the builder
  144.          */
  145.         public Builder withMaxLiteralLength(final int maxLiteralLength) {
  146.             this.maxLiteralLength = maxLiteralLength < 1 ? windowSize : Math.min(maxLiteralLength, windowSize);
  147.             return this;
  148.         }

  149.         /**
  150.          * Sets the maximum number of back-reference candidates that should be consulted.
  151.          * <p>
  152.          * This settings can be used to tune the tradeoff between compression speed and compression ratio.
  153.          * </p>
  154.          *
  155.          * @param maxCandidates maximum number of back-reference candidates
  156.          * @return the builder
  157.          */
  158.         public Builder withMaxNumberOfCandidates(final int maxCandidates) {
  159.             this.maxCandidates = maxCandidates;
  160.             return this;
  161.         }

  162.         /**
  163.          * Sets the maximal offset of a back-reference.
  164.          * <p>
  165.          * It is recommended to not use this method directly but rather tune a pre-configured builder created by a format specific factory like
  166.          * {@link org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.
  167.          * </p>
  168.          *
  169.          * @param maxOffset maximal offset of a back-reference. A non-positive value as well as values bigger than {@code windowSize - 1} are interpreted as
  170.          *                  {@code windowSize * - 1}.
  171.          * @return the builder
  172.          */
  173.         public Builder withMaxOffset(final int maxOffset) {
  174.             this.maxOffset = maxOffset < 1 ? windowSize - 1 : Math.min(maxOffset, windowSize - 1);
  175.             return this;
  176.         }

  177.         /**
  178.          * Sets the minimal length of a back-reference.
  179.          * <p>
  180.          * Ensures {@code maxBackReferenceLength} is not smaller than {@code minBackReferenceLength}.
  181.          * </p>
  182.          * <p>
  183.          * It is recommended to not use this method directly but rather tune a pre-configured builder created by a format specific factory like
  184.          * {@link org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.
  185.          * </p>
  186.          *
  187.          * @param minBackReferenceLength the minimal length of a back-reference found. A true minimum of 3 is hard-coded inside of this implementation but
  188.          *                               bigger lengths can be configured.
  189.          * @throws IllegalArgumentException if {@code windowSize} is smaller than {@code minBackReferenceLength}.
  190.          * @return the builder
  191.          */
  192.         public Builder withMinBackReferenceLength(final int minBackReferenceLength) {
  193.             this.minBackReferenceLength = Math.max(TRUE_MIN_BACK_REFERENCE_LENGTH, minBackReferenceLength);
  194.             if (windowSize < this.minBackReferenceLength) {
  195.                 throw new IllegalArgumentException("minBackReferenceLength can't be bigger than windowSize");
  196.             }
  197.             if (maxBackReferenceLength < this.minBackReferenceLength) {
  198.                 maxBackReferenceLength = this.minBackReferenceLength;
  199.             }
  200.             return this;
  201.         }

  202.         /**
  203.          * Sets the "nice length" of a back-reference.
  204.          * <p>
  205.          * When a back-references if this size has been found, stop searching for longer back-references.
  206.          * </p>
  207.          * <p>
  208.          * This settings can be used to tune the tradeoff between compression speed and compression ratio.
  209.          * </p>
  210.          *
  211.          * @param niceLen the "nice length" of a back-reference
  212.          * @return the builder
  213.          */
  214.         public Builder withNiceBackReferenceLength(final int niceLen) {
  215.             niceBackReferenceLength = niceLen;
  216.             return this;
  217.         }
  218.     }

  219.     /**
  220.      * The hard-coded absolute minimal length of a back-reference.
  221.      */
  222.     public static final int TRUE_MIN_BACK_REFERENCE_LENGTH = LZ77Compressor.NUMBER_OF_BYTES_IN_HASH;

  223.     /**
  224.      * Initializes the builder for the compressor's parameters with a {@code minBackReferenceLength} of 3 and {@code max*Length} equal to
  225.      * {@code windowSize - 1}.
  226.      * <p>
  227.      * It is recommended to not use this method directly but rather tune a pre-configured builder created by a format specific factory like
  228.      * {@link org.apache.commons.compress.compressors.snappy.SnappyCompressorOutputStream#createParameterBuilder}.
  229.      * </p>
  230.      *
  231.      * @param windowSize the size of the sliding window - this determines the maximum offset a back-reference can take. Must be a power of two.
  232.      * @throws IllegalArgumentException if windowSize is not a power of two.
  233.      * @return a builder configured for the given window size
  234.      */
  235.     public static Builder builder(final int windowSize) {
  236.         return new Builder(windowSize);
  237.     }

  238.     private static boolean isPowerOfTwo(final int x) {
  239.         // pre-condition: x > 0
  240.         return (x & x - 1) == 0;
  241.     }

  242.     private final int windowSize, minBackReferenceLength, maxBackReferenceLength, maxOffset, maxLiteralLength, niceBackReferenceLength, maxCandidates,
  243.             lazyThreshold;

  244.     private final boolean lazyMatching;

  245.     private Parameters(final int windowSize, final int minBackReferenceLength, final int maxBackReferenceLength, final int maxOffset,
  246.             final int maxLiteralLength, final int niceBackReferenceLength, final int maxCandidates, final boolean lazyMatching, final int lazyThreshold) {
  247.         this.windowSize = windowSize;
  248.         this.minBackReferenceLength = minBackReferenceLength;
  249.         this.maxBackReferenceLength = maxBackReferenceLength;
  250.         this.maxOffset = maxOffset;
  251.         this.maxLiteralLength = maxLiteralLength;
  252.         this.niceBackReferenceLength = niceBackReferenceLength;
  253.         this.maxCandidates = maxCandidates;
  254.         this.lazyMatching = lazyMatching;
  255.         this.lazyThreshold = lazyThreshold;
  256.     }

  257.     /**
  258.      * Gets whether to perform lazy matching.
  259.      *
  260.      * @return whether to perform lazy matching
  261.      */
  262.     public boolean getLazyMatching() {
  263.         return lazyMatching;
  264.     }

  265.     /**
  266.      * Gets the threshold for lazy matching.
  267.      *
  268.      * @return the threshold for lazy matching
  269.      */
  270.     public int getLazyMatchingThreshold() {
  271.         return lazyThreshold;
  272.     }

  273.     /**
  274.      * Gets the maximal length of a back-reference found.
  275.      *
  276.      * @return the maximal length of a back-reference found
  277.      */
  278.     public int getMaxBackReferenceLength() {
  279.         return maxBackReferenceLength;
  280.     }

  281.     /**
  282.      * Gets the maximum number of back-reference candidates to consider.
  283.      *
  284.      * @return the maximum number of back-reference candidates to consider
  285.      */
  286.     public int getMaxCandidates() {
  287.         return maxCandidates;
  288.     }

  289.     /**
  290.      * Gets the maximal length of a literal block.
  291.      *
  292.      * @return the maximal length of a literal block
  293.      */
  294.     public int getMaxLiteralLength() {
  295.         return maxLiteralLength;
  296.     }

  297.     /**
  298.      * Gets the maximal offset of a back-reference found.
  299.      *
  300.      * @return the maximal offset of a back-reference found
  301.      */
  302.     public int getMaxOffset() {
  303.         return maxOffset;
  304.     }

  305.     /**
  306.      * Gets the minimal length of a back-reference found.
  307.      *
  308.      * @return the minimal length of a back-reference found
  309.      */
  310.     public int getMinBackReferenceLength() {
  311.         return minBackReferenceLength;
  312.     }

  313.     /**
  314.      * Gets the length of a back-reference that is considered nice enough to stop searching for longer ones.
  315.      *
  316.      * @return the length of a back-reference that is considered nice enough to stop searching
  317.      */
  318.     public int getNiceBackReferenceLength() {
  319.         return niceBackReferenceLength;
  320.     }

  321.     /**
  322.      * Gets the size of the sliding window - this determines the maximum offset a back-reference can take.
  323.      *
  324.      * @return the size of the sliding window
  325.      */
  326.     public int getWindowSize() {
  327.         return windowSize;
  328.     }
  329. }