View Javadoc
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  
21  import org.apache.commons.compress.utils.OsgiUtils;
22  
23  /**
24   * Utility code for the Zstandard compression format.
25   *
26   * @ThreadSafe
27   * @since 1.16
28   */
29  public class ZstdUtils {
30  
31      enum CachedAvailability {
32          DONT_CACHE, CACHED_AVAILABLE, CACHED_UNAVAILABLE
33      }
34  
35      /**
36       * Zstandard Frame Magic Bytes.
37       */
38      private static final byte[] ZSTANDARD_FRAME_MAGIC = { (byte) 0x28, (byte) 0xB5, (byte) 0x2F, (byte) 0xFD };
39  
40      /**
41       * Skippable Frame Magic Bytes - the three common bytes.
42       */
43      private static final byte[] SKIPPABLE_FRAME_MAGIC = { (byte) 0x2A, (byte) 0x4D, (byte) 0x18 };
44  
45      private static volatile CachedAvailability cachedZstdAvailability;
46  
47      static {
48          cachedZstdAvailability = CachedAvailability.DONT_CACHE;
49          setCacheZstdAvailablity(!OsgiUtils.isRunningInOsgiEnvironment());
50      }
51  
52      // only exists to support unit tests
53      static CachedAvailability getCachedZstdAvailability() {
54          return cachedZstdAvailability;
55      }
56  
57      private static boolean internalIsZstdCompressionAvailable() {
58          try {
59              Class.forName("com.github.luben.zstd.ZstdInputStream");
60              return true;
61          } catch (final NoClassDefFoundError | Exception error) { // NOSONAR
62              return false;
63          }
64      }
65  
66      /**
67       * Are the classes required to support Zstandard compression available?
68       *
69       * @return true if the classes required to support Zstandard compression are available
70       */
71      public static boolean isZstdCompressionAvailable() {
72          final CachedAvailability cachedResult = cachedZstdAvailability;
73          if (cachedResult != CachedAvailability.DONT_CACHE) {
74              return cachedResult == CachedAvailability.CACHED_AVAILABLE;
75          }
76          return internalIsZstdCompressionAvailable();
77      }
78  
79      /**
80       * Checks if the signature matches what is expected for a Zstandard file.
81       *
82       * @param signature the bytes to check
83       * @param length    the number of bytes to check
84       * @return true if signature matches the Ztstandard or skippable frame magic bytes, false otherwise
85       */
86      public static boolean matches(final byte[] signature, final int length) {
87          if (length < ZSTANDARD_FRAME_MAGIC.length) {
88              return false;
89          }
90  
91          boolean isZstandard = true;
92          for (int i = 0; i < ZSTANDARD_FRAME_MAGIC.length; ++i) {
93              if (signature[i] != ZSTANDARD_FRAME_MAGIC[i]) {
94                  isZstandard = false;
95                  break;
96              }
97          }
98          if (isZstandard) {
99              return true;
100         }
101 
102         if (0x50 == (signature[0] & 0xF0)) {
103             // skippable frame
104             for (int i = 0; i < SKIPPABLE_FRAME_MAGIC.length; ++i) {
105                 if (signature[i + 1] != SKIPPABLE_FRAME_MAGIC[i]) {
106                     return false;
107                 }
108             }
109 
110             return true;
111         }
112 
113         return false;
114     }
115 
116     /**
117      * Whether to cache the result of the Zstandard for Java check.
118      *
119      * <p>
120      * This defaults to {@code false} in an OSGi environment and {@code true} otherwise.
121      * </p>
122      *
123      * @param doCache whether to cache the result
124      */
125     public static void setCacheZstdAvailablity(final boolean doCache) {
126         if (!doCache) {
127             cachedZstdAvailability = CachedAvailability.DONT_CACHE;
128         } else if (cachedZstdAvailability == CachedAvailability.DONT_CACHE) {
129             final boolean hasZstd = internalIsZstdCompressionAvailable();
130             cachedZstdAvailability = hasZstd ? CachedAvailability.CACHED_AVAILABLE : CachedAvailability.CACHED_UNAVAILABLE;
131         }
132     }
133 
134     /** Private constructor to prevent instantiation of this utility class. */
135     private ZstdUtils() {
136     }
137 }