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.  * 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.zstandard;

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

  21. /**
  22.  * Utility code for the Zstandard compression format.
  23.  *
  24.  * @ThreadSafe
  25.  * @since 1.16
  26.  */
  27. public class ZstdUtils {

  28.     enum CachedAvailability {
  29.         DONT_CACHE, CACHED_AVAILABLE, CACHED_UNAVAILABLE
  30.     }

  31.     /**
  32.      * Zstandard Frame Magic Bytes.
  33.      */
  34.     private static final byte[] ZSTANDARD_FRAME_MAGIC = { (byte) 0x28, (byte) 0xB5, (byte) 0x2F, (byte) 0xFD };

  35.     /**
  36.      * Skippable Frame Magic Bytes - the three common bytes.
  37.      */
  38.     private static final byte[] SKIPPABLE_FRAME_MAGIC = { (byte) 0x2A, (byte) 0x4D, (byte) 0x18 };

  39.     private static volatile CachedAvailability cachedZstdAvailability;

  40.     static {
  41.         cachedZstdAvailability = CachedAvailability.DONT_CACHE;
  42.         setCacheZstdAvailablity(!OsgiUtils.isRunningInOsgiEnvironment());
  43.     }

  44.     // only exists to support unit tests
  45.     static CachedAvailability getCachedZstdAvailability() {
  46.         return cachedZstdAvailability;
  47.     }

  48.     private static boolean internalIsZstdCompressionAvailable() {
  49.         try {
  50.             Class.forName("com.github.luben.zstd.ZstdInputStream");
  51.             return true;
  52.         } catch (final NoClassDefFoundError | Exception error) { // NOSONAR
  53.             return false;
  54.         }
  55.     }

  56.     /**
  57.      * Are the classes required to support Zstandard compression available?
  58.      *
  59.      * @return true if the classes required to support Zstandard compression are available
  60.      */
  61.     public static boolean isZstdCompressionAvailable() {
  62.         final CachedAvailability cachedResult = cachedZstdAvailability;
  63.         if (cachedResult != CachedAvailability.DONT_CACHE) {
  64.             return cachedResult == CachedAvailability.CACHED_AVAILABLE;
  65.         }
  66.         return internalIsZstdCompressionAvailable();
  67.     }

  68.     /**
  69.      * Checks if the signature matches what is expected for a Zstandard file.
  70.      *
  71.      * @param signature the bytes to check
  72.      * @param length    the number of bytes to check
  73.      * @return true if signature matches the Ztstandard or skippable frame magic bytes, false otherwise
  74.      */
  75.     public static boolean matches(final byte[] signature, final int length) {
  76.         if (length < ZSTANDARD_FRAME_MAGIC.length) {
  77.             return false;
  78.         }

  79.         boolean isZstandard = true;
  80.         for (int i = 0; i < ZSTANDARD_FRAME_MAGIC.length; ++i) {
  81.             if (signature[i] != ZSTANDARD_FRAME_MAGIC[i]) {
  82.                 isZstandard = false;
  83.                 break;
  84.             }
  85.         }
  86.         if (isZstandard) {
  87.             return true;
  88.         }

  89.         if (0x50 == (signature[0] & 0xF0)) {
  90.             // skippable frame
  91.             for (int i = 0; i < SKIPPABLE_FRAME_MAGIC.length; ++i) {
  92.                 if (signature[i + 1] != SKIPPABLE_FRAME_MAGIC[i]) {
  93.                     return false;
  94.                 }
  95.             }

  96.             return true;
  97.         }

  98.         return false;
  99.     }

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

  117.     /** Private constructor to prevent instantiation of this utility class. */
  118.     private ZstdUtils() {
  119.     }
  120. }