ZstdUtils.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.  *   https://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.zstandard;

  20. import org.apache.commons.compress.utils.OsgiUtils;

  21. /**
  22.  * Utility code for the Zstandard compression format.
  23.  *
  24.  * <p>
  25.  * This class avoids making the underlying {@code zstd} classes part of the public or protected API. The underlying implementation is provided through the
  26.  * <a href="https://github.com/luben/zstd-jni/">Zstandard JNI</a> library which is based on <a href="https://github.com/facebook/zstd/">zstd</a>.
  27.  * </p>
  28.  *
  29.  * @ThreadSafe
  30.  * @see <a href="https://github.com/luben/zstd-jni/">Zstandard JNI</a>
  31.  * @see <a href="https://github.com/facebook/zstd/">zstd</a>
  32.  * @since 1.16
  33.  */
  34. public class ZstdUtils {

  35.     enum CachedAvailability {
  36.         DONT_CACHE, CACHED_AVAILABLE, CACHED_UNAVAILABLE
  37.     }

  38.     /**
  39.      * Zstandard Frame Magic Bytes.
  40.      */
  41.     private static final byte[] ZSTANDARD_FRAME_MAGIC = { (byte) 0x28, (byte) 0xB5, (byte) 0x2F, (byte) 0xFD };

  42.     /**
  43.      * Skippable Frame Magic Bytes - the three common bytes.
  44.      */
  45.     private static final byte[] SKIPPABLE_FRAME_MAGIC = { (byte) 0x2A, (byte) 0x4D, (byte) 0x18 };

  46.     private static volatile CachedAvailability cachedZstdAvailability;

  47.     static {
  48.         cachedZstdAvailability = CachedAvailability.DONT_CACHE;
  49.         setCacheZstdAvailablity(!OsgiUtils.isRunningInOsgiEnvironment());
  50.     }

  51.     // only exists to support unit tests
  52.     static CachedAvailability getCachedZstdAvailability() {
  53.         return cachedZstdAvailability;
  54.     }

  55.     private static boolean internalIsZstdCompressionAvailable() {
  56.         try {
  57.             Class.forName("com.github.luben.zstd.ZstdInputStream");
  58.             return true;
  59.         } catch (final NoClassDefFoundError | Exception error) { // NOSONAR
  60.             return false;
  61.         }
  62.     }

  63.     /**
  64.      * Are the classes required to support Zstandard compression available?
  65.      *
  66.      * @return true if the classes required to support Zstandard compression are available
  67.      */
  68.     public static boolean isZstdCompressionAvailable() {
  69.         final CachedAvailability cachedResult = cachedZstdAvailability;
  70.         if (cachedResult != CachedAvailability.DONT_CACHE) {
  71.             return cachedResult == CachedAvailability.CACHED_AVAILABLE;
  72.         }
  73.         return internalIsZstdCompressionAvailable();
  74.     }

  75.     /**
  76.      * Checks if the signature matches what is expected for a Zstandard file.
  77.      *
  78.      * @param signature the bytes to check
  79.      * @param length    the number of bytes to check
  80.      * @return true if signature matches the Ztstandard or skippable frame magic bytes, false otherwise
  81.      */
  82.     public static boolean matches(final byte[] signature, final int length) {
  83.         if (length < ZSTANDARD_FRAME_MAGIC.length) {
  84.             return false;
  85.         }
  86.         boolean isZstandard = true;
  87.         for (int i = 0; i < ZSTANDARD_FRAME_MAGIC.length; ++i) {
  88.             if (signature[i] != ZSTANDARD_FRAME_MAGIC[i]) {
  89.                 isZstandard = false;
  90.                 break;
  91.             }
  92.         }
  93.         if (isZstandard) {
  94.             return true;
  95.         }
  96.         if (0x50 == (signature[0] & 0xF0)) {
  97.             // skippable frame
  98.             for (int i = 0; i < SKIPPABLE_FRAME_MAGIC.length; ++i) {
  99.                 if (signature[i + 1] != SKIPPABLE_FRAME_MAGIC[i]) {
  100.                     return false;
  101.                 }
  102.             }
  103.             return true;
  104.         }
  105.         return false;
  106.     }

  107.     /**
  108.      * Sets whether to cache the result of the Zstandard for Java check.
  109.      *
  110.      * <p>
  111.      * This defaults to {@code false} in an OSGi environment and {@code true} otherwise.
  112.      * </p>
  113.      *
  114.      * @param doCache whether to cache the result
  115.      */
  116.     public static void setCacheZstdAvailablity(final boolean doCache) {
  117.         if (!doCache) {
  118.             cachedZstdAvailability = CachedAvailability.DONT_CACHE;
  119.         } else if (cachedZstdAvailability == CachedAvailability.DONT_CACHE) {
  120.             final boolean hasZstd = internalIsZstdCompressionAvailable();
  121.             cachedZstdAvailability = hasZstd ? CachedAvailability.CACHED_AVAILABLE : CachedAvailability.CACHED_UNAVAILABLE;
  122.         }
  123.     }

  124.     /** Private constructor to prevent instantiation of this utility class. */
  125.     private ZstdUtils() {
  126.     }
  127. }