001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.compressors.zstandard; 020 021import org.apache.commons.compress.utils.OsgiUtils; 022 023/** 024 * Utility code for the Zstandard compression format. 025 * 026 * <p> 027 * This class avoids making the underlying {@code zstd} classes part of the public or protected API. The underlying implementation is provided through the 028 * <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>. 029 * </p> 030 * 031 * @ThreadSafe 032 * @see <a href="https://github.com/luben/zstd-jni/">Zstandard JNI</a> 033 * @see <a href="https://github.com/facebook/zstd/">zstd</a> 034 * @since 1.16 035 */ 036public class ZstdUtils { 037 038 enum CachedAvailability { 039 DONT_CACHE, CACHED_AVAILABLE, CACHED_UNAVAILABLE 040 } 041 042 /** 043 * Zstandard Frame Magic Bytes. 044 */ 045 private static final byte[] ZSTANDARD_FRAME_MAGIC = { (byte) 0x28, (byte) 0xB5, (byte) 0x2F, (byte) 0xFD }; 046 047 /** 048 * Skippable Frame Magic Bytes - the three common bytes. 049 */ 050 private static final byte[] SKIPPABLE_FRAME_MAGIC = { (byte) 0x2A, (byte) 0x4D, (byte) 0x18 }; 051 052 private static volatile CachedAvailability cachedZstdAvailability; 053 054 static { 055 cachedZstdAvailability = CachedAvailability.DONT_CACHE; 056 setCacheZstdAvailablity(!OsgiUtils.isRunningInOsgiEnvironment()); 057 } 058 059 // only exists to support unit tests 060 static CachedAvailability getCachedZstdAvailability() { 061 return cachedZstdAvailability; 062 } 063 064 private static boolean internalIsZstdCompressionAvailable() { 065 try { 066 Class.forName("com.github.luben.zstd.ZstdInputStream"); 067 return true; 068 } catch (final NoClassDefFoundError | Exception error) { // NOSONAR 069 return false; 070 } 071 } 072 073 /** 074 * Are the classes required to support Zstandard compression available? 075 * 076 * @return true if the classes required to support Zstandard compression are available 077 */ 078 public static boolean isZstdCompressionAvailable() { 079 final CachedAvailability cachedResult = cachedZstdAvailability; 080 if (cachedResult != CachedAvailability.DONT_CACHE) { 081 return cachedResult == CachedAvailability.CACHED_AVAILABLE; 082 } 083 return internalIsZstdCompressionAvailable(); 084 } 085 086 /** 087 * Checks if the signature matches what is expected for a Zstandard file. 088 * 089 * @param signature the bytes to check 090 * @param length the number of bytes to check 091 * @return true if signature matches the Ztstandard or skippable frame magic bytes, false otherwise 092 */ 093 public static boolean matches(final byte[] signature, final int length) { 094 if (length < ZSTANDARD_FRAME_MAGIC.length) { 095 return false; 096 } 097 boolean isZstandard = true; 098 for (int i = 0; i < ZSTANDARD_FRAME_MAGIC.length; ++i) { 099 if (signature[i] != ZSTANDARD_FRAME_MAGIC[i]) { 100 isZstandard = false; 101 break; 102 } 103 } 104 if (isZstandard) { 105 return true; 106 } 107 if (0x50 == (signature[0] & 0xF0)) { 108 // skippable frame 109 for (int i = 0; i < SKIPPABLE_FRAME_MAGIC.length; ++i) { 110 if (signature[i + 1] != SKIPPABLE_FRAME_MAGIC[i]) { 111 return false; 112 } 113 } 114 return true; 115 } 116 return false; 117 } 118 119 /** 120 * Sets whether to cache the result of the Zstandard for Java check. 121 * 122 * <p> 123 * This defaults to {@code false} in an OSGi environment and {@code true} otherwise. 124 * </p> 125 * 126 * @param doCache whether to cache the result 127 */ 128 public static void setCacheZstdAvailablity(final boolean doCache) { 129 if (!doCache) { 130 cachedZstdAvailability = CachedAvailability.DONT_CACHE; 131 } else if (cachedZstdAvailability == CachedAvailability.DONT_CACHE) { 132 final boolean hasZstd = internalIsZstdCompressionAvailable(); 133 cachedZstdAvailability = hasZstd ? CachedAvailability.CACHED_AVAILABLE : CachedAvailability.CACHED_UNAVAILABLE; 134 } 135 } 136 137 /** Private constructor to prevent instantiation of this utility class. */ 138 private ZstdUtils() { 139 } 140}