├── .classpath ├── .gitignore ├── .gitmodules ├── .project ├── .settings ├── org.eclipse.jdt.core.prefs └── org.eclipse.jdt.ui.prefs ├── CHANGES.md ├── LICENSE.txt ├── README.md ├── build.xml ├── ivy-pure-java.xml ├── ivy.xml ├── lz4-java.bnd ├── lz4-pure-java.bnd └── src ├── build ├── gen_sources.mvel ├── pom-pure-java.template ├── pom.template └── source_templates │ ├── compress.template │ ├── compress_hc.template │ ├── compressor.template │ ├── compressor_hc.template │ ├── decompress.template │ ├── decompressor.template │ ├── hashtable.template │ ├── xxhash32.template │ ├── xxhash32_hash.template │ ├── xxhash32_streaming.template │ ├── xxhash64.template │ ├── xxhash64_hash.template │ └── xxhash64_streaming.template ├── java-unsafe └── net │ └── jpountz │ ├── lz4 │ └── LZ4UnsafeUtils.java │ └── util │ └── UnsafeUtils.java ├── java ├── net │ └── jpountz │ │ ├── lz4 │ │ ├── LZ4BlockInputStream.java │ │ ├── LZ4BlockOutputStream.java │ │ ├── LZ4ByteBufferUtils.java │ │ ├── LZ4Compressor.java │ │ ├── LZ4CompressorWithLength.java │ │ ├── LZ4Constants.java │ │ ├── LZ4Decompressor.java │ │ ├── LZ4DecompressorWithLength.java │ │ ├── LZ4Exception.java │ │ ├── LZ4Factory.java │ │ ├── LZ4FastDecompressor.java │ │ ├── LZ4FrameInputStream.java │ │ ├── LZ4FrameOutputStream.java │ │ ├── LZ4HCJNICompressor.java │ │ ├── LZ4JNI.java │ │ ├── LZ4JNICompressor.java │ │ ├── LZ4JNIFastDecompressor.java │ │ ├── LZ4JNISafeDecompressor.java │ │ ├── LZ4SafeDecompressor.java │ │ ├── LZ4SafeUtils.java │ │ ├── LZ4UnknownSizeDecompressor.java │ │ ├── LZ4Utils.java │ │ └── package.html │ │ ├── util │ │ ├── ByteBufferUtils.java │ │ ├── Native.java │ │ ├── SafeUtils.java │ │ ├── Utils.java │ │ └── package.html │ │ └── xxhash │ │ ├── AbstractStreamingXXHash32Java.java │ │ ├── AbstractStreamingXXHash64Java.java │ │ ├── StreamingXXHash32.java │ │ ├── StreamingXXHash32JNI.java │ │ ├── StreamingXXHash64.java │ │ ├── StreamingXXHash64JNI.java │ │ ├── XXHash32.java │ │ ├── XXHash32JNI.java │ │ ├── XXHash64.java │ │ ├── XXHash64JNI.java │ │ ├── XXHashConstants.java │ │ ├── XXHashFactory.java │ │ ├── XXHashJNI.java │ │ └── package.html └── overview.html ├── jni ├── net_jpountz_lz4_LZ4JNI.c └── net_jpountz_xxhash_XXHashJNI.c ├── resources └── net │ └── jpountz │ └── util │ ├── darwin │ ├── aarch64 │ │ └── liblz4-java.dylib │ └── x86_64 │ │ └── liblz4-java.dylib │ ├── linux │ ├── aarch64 │ │ └── liblz4-java.so │ ├── amd64 │ │ └── liblz4-java.so │ ├── i386 │ │ └── liblz4-java.so │ ├── ppc64le │ │ └── liblz4-java.so │ └── s390x │ │ └── liblz4-java.so │ └── win32 │ └── amd64 │ └── liblz4-java.so ├── test-resources └── calgary │ ├── README │ ├── book1 │ ├── geo │ └── pic └── test └── net └── jpountz ├── example ├── LZ4Example.java └── XXHashExample.java ├── lz4 ├── AbstractLZ4Test.java ├── Instances.java ├── LZ4BlockStreamingTest.java ├── LZ4FactoryTest.java ├── LZ4FrameIOStreamTest.java └── LZ4Test.java └── xxhash ├── XXHash32Test.java ├── XXHash64Test.java └── XXHashFactoryTest.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | bin 4 | lib 5 | !src/build 6 | .idea 7 | out 8 | */*.iml 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/lz4"] 2 | path = src/lz4 3 | url = https://github.com/lz4/lz4.git 4 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | lz4-java 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | org.eclipse.jdt.core.javanature 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | #Tue Jul 17 16:36:09 CEST 2012 2 | eclipse.preferences.version=1 3 | formatter_profile=_2 spaces 4 | formatter_settings_version=12 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LZ4 Java 2 | 3 | LZ4 compression for Java, based on Yann Collet's work available at 4 | http://code.google.com/p/lz4/. 5 | 6 | This library provides access to two compression methods that both generate a 7 | valid LZ4 stream: 8 | - fast scan (LZ4): 9 | - low memory footprint (~ 16 KB), 10 | - very fast (fast scan with skipping heuristics in case the input looks 11 | incompressible), 12 | - reasonable compression ratio (depending on the redundancy of the input). 13 | - high compression (LZ4 HC): 14 | - medium memory footprint (~ 256 KB), 15 | - rather slow (~ 10 times slower than LZ4), 16 | - good compression ratio (depending on the size and the redundancy of the 17 | input). 18 | - interoperable (LZ4 Frame): 19 | - overlay on top of lz4, that makes it system and library independent 20 | - this is the version to employ in order to share lz4 compressed data 21 | with other systems using different lz4 frameworks. 22 | 23 | The streams produced by LZ4 and LZ4 HC use the same compression format, 24 | are very fast to decompress and can be decompressed by the same lz4-java decompressor instance. 25 | 26 | The streams produced by LZ4 Frame can be read by other independent implementations of LZ4 Frame in any other language, including the `lz4` CLI tool. 27 | 28 | ## Implementations 29 | 30 | For LZ4 compressors, LZ4 HC compressors and decompressors, 3 implementations are 31 | available: 32 | - JNI bindings to the original C implementation by Yann Collet, 33 | - a pure Java port of the compression and decompression algorithms, 34 | - a Java port that uses the sun.misc.Unsafe API in order to achieve compression 35 | and decompression speeds close to the C implementation. 36 | 37 | Have a look at LZ4Factory for more information. 38 | 39 | ## Compatibility notes 40 | 41 | - Compressors and decompressors are interchangeable: it is perfectly correct 42 | to compress with the JNI bindings and to decompress with a Java port, or the 43 | other way around. 44 | 45 | - Compressors might not generate the same compressed streams on all platforms, 46 | especially if CPU endianness differs, but the compressed streams can be 47 | safely decompressed by any decompressor implementation on any platform. 48 | 49 | ## Examples 50 | 51 | ```java 52 | LZ4Factory factory = LZ4Factory.fastestInstance(); 53 | 54 | byte[] data = "12345345234572".getBytes("UTF-8"); 55 | final int decompressedLength = data.length; 56 | 57 | // compress data 58 | LZ4Compressor compressor = factory.fastCompressor(); 59 | int maxCompressedLength = compressor.maxCompressedLength(decompressedLength); 60 | byte[] compressed = new byte[maxCompressedLength]; 61 | int compressedLength = compressor.compress(data, 0, decompressedLength, compressed, 0, maxCompressedLength); 62 | 63 | // decompress data 64 | // - method 1: when the decompressed length is known 65 | LZ4FastDecompressor decompressor = factory.fastDecompressor(); 66 | byte[] restored = new byte[decompressedLength]; 67 | int compressedLength2 = decompressor.decompress(compressed, 0, restored, 0, decompressedLength); 68 | // compressedLength == compressedLength2 69 | 70 | // - method 2: when the compressed length is known (a little slower) 71 | // the destination buffer needs to be over-sized 72 | LZ4SafeDecompressor decompressor2 = factory.safeDecompressor(); 73 | int decompressedLength2 = decompressor2.decompress(compressed, 0, compressedLength, restored, 0); 74 | // decompressedLength == decompressedLength2 75 | ``` 76 | 77 | ```java 78 | byte[] data = "12345345234572".getBytes("UTF-8"); 79 | final int decompressedLength = data.length; 80 | 81 | LZ4FrameOutputStream outStream = new LZ4FrameOutputStream(new FileOutputStream(new File("test.lz4"))); 82 | outStream.write(data); 83 | outStream.close(); 84 | 85 | byte[] restored = new byte[decompressedLength]; 86 | LZ4FrameInputStream inStream = new LZ4FrameInputStream(new FileInputStream(new File("test.lz4"))); 87 | inStream.read(restored); 88 | inStream.close(); 89 | ``` 90 | 91 | # xxhash Java 92 | 93 | xxhash hashing for Java, based on Yann Collet's work available at https://github.com/Cyan4973/xxHash (old version 94 | http://code.google.com/p/xxhash/). xxhash is a non-cryptographic, extremly fast 95 | and high-quality ([SMHasher](http://code.google.com/p/smhasher/wiki/SMHasher) 96 | score of 10) hash function. 97 | 98 | ## Implementations 99 | 100 | Similarly to LZ4, 3 implementations are available: JNI bindings, pure Java port 101 | and pure Java port that uses sun.misc.Unsafe. 102 | 103 | Have a look at XXHashFactory for more information. 104 | 105 | ## Compatibility notes 106 | 107 | - All implementation return the same hash for the same input bytes: 108 | - on any JVM, 109 | - on any platform (even if the endianness or integer size differs). 110 | 111 | ## Example 112 | 113 | ```java 114 | XXHashFactory factory = XXHashFactory.fastestInstance(); 115 | 116 | byte[] data = "12345345234572".getBytes("UTF-8"); 117 | ByteArrayInputStream in = new ByteArrayInputStream(data); 118 | 119 | int seed = 0x9747b28c; // used to initialize the hash value, use whatever 120 | // value you want, but always the same 121 | StreamingXXHash32 hash32 = factory.newStreamingHash32(seed); 122 | byte[] buf = new byte[8]; // for real-world usage, use a larger buffer, like 8192 bytes 123 | for (;;) { 124 | int read = in.read(buf); 125 | if (read == -1) { 126 | break; 127 | } 128 | hash32.update(buf, 0, read); 129 | } 130 | int hash = hash32.getValue(); 131 | ``` 132 | 133 | # Download 134 | 135 | You can download released artifacts from [Maven Central](https://search.maven.org/search?q=g:org.lz4%20a:lz4-java). 136 | 137 | You can download pure-Java lz4-java from [Maven Central](https://search.maven.org/search?q=g:org.lz4%20a:lz4-pure-java). These artifacts include the Safe and Unsafe Java versions but not JNI bindings. (Experimental) 138 | 139 | # Documentation 140 | 141 | - [lz4](https://lz4.github.io/lz4-java/1.8.0/docs/net/jpountz/lz4/package-summary.html) 142 | - [xxhash](https://lz4.github.io/lz4-java/1.8.0/docs/net/jpountz/xxhash/package-summary.html) 143 | - [changelog](https://github.com/lz4/lz4-java/blob/master/CHANGES.md) 144 | 145 | # Performance 146 | 147 | Both lz4 and xxhash focus on speed. Although compression, decompression and 148 | hashing performance can depend a lot on the input (there are lies, damn lies 149 | and benchmarks), here are some benchmarks that try to give a sense of the 150 | speed at which they compress/decompress/hash bytes. 151 | 152 | - [lz4 compression](https://lz4.github.io/lz4-java/1.8.0/lz4-compression-benchmark/) 153 | - [lz4 decompression](https://lz4.github.io/lz4-java/1.8.0/lz4-decompression-benchmark/) 154 | - [xxhash hashing](https://lz4.github.io/lz4-java/1.3.0/xxhash-benchmark/) 155 | 156 | # Build 157 | 158 | ## Requirements 159 | 160 | - JDK version 7 or newer, 161 | - ant version 1.10.2 or newer, 162 | - ivy. 163 | 164 | If ivy is not installed yet, ant can take care of it for you, just run 165 | `ant ivy-bootstrap`. The library will be installed under ${user.home}/.ant/lib. 166 | 167 | You might hit an error like the following when the ivy in ${user.home}/.ant/lib is old. You can delete it and then run `ant ivy-bootstrap` again to install the latest version. 168 | ``` 169 | [ivy:resolve] :::::::::::::::::::::::::::::::::::::::::::::: 170 | [ivy:resolve] :: UNRESOLVED DEPENDENCIES :: 171 | [ivy:resolve] :::::::::::::::::::::::::::::::::::::::::::::: 172 | ``` 173 | 174 | ## Instructions 175 | 176 | For lz4-java 1.5.0 or newer, first run `git submodule init` and then `git submodule update` 177 | to initialize the `lz4` submodule in `src/lz4`. 178 | 179 | Then run `ant`. It will: 180 | 181 | - generate some Java source files in `build/java` from the templates that are 182 | located under `src/build`, 183 | - compile the lz4 and xxhash libraries and their JNI (Java Native Interface) 184 | bindings, 185 | - compile Java sources in `src/java` (normal sources), `src/java-unsafe` 186 | (sources that make use of `sun.misc.Unsafe`) and `build/java` 187 | (auto-generated sources) to `build/classes`, `build/unsafe-classes` and 188 | `build/generated-classes`, 189 | - generate a JAR file called lz4-${version}.jar under the `dist` directory. 190 | 191 | The JAR file that is generated contains Java class files, the native library 192 | and the JNI bindings. If you add this JAR to your classpath, the native library 193 | will be copied to a temporary directory and dynamically linked to your Java 194 | application. 195 | -------------------------------------------------------------------------------- /ivy-pure-java.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ivy.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /lz4-java.bnd: -------------------------------------------------------------------------------- 1 | Bundle-SymbolicName: lz4-java 2 | Bundle-Name: LZ4 Java Compression 3 | Bundle-Version:${ivy.revision} 4 | Export-Package: net.jpountz.*;version:=${packages.version} 5 | 6 | -------------------------------------------------------------------------------- /lz4-pure-java.bnd: -------------------------------------------------------------------------------- 1 | Bundle-SymbolicName: lz4-pure-java 2 | Bundle-Name: LZ4 Java Compression 3 | Bundle-Version:${ivy.revision} 4 | Export-Package: net.jpountz.*;version:=${packages.version} 5 | 6 | -------------------------------------------------------------------------------- /src/build/gen_sources.mvel: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.io.*; 17 | import java.util.*; 18 | import org.mvel2.templates.*; 19 | 20 | outDir = System.getProperty("out.dir"); 21 | 22 | def get_template(file) { 23 | template = new File(file); 24 | return TemplateCompiler.compileTemplate(template, Collections.emptyMap()); 25 | } 26 | 27 | def execute_template(template, dest, args) { 28 | System.out.println("Generating " + dest); 29 | dest.getParentFile().mkdirs(); 30 | String result = (String) TemplateRuntime.execute(compiledTemplate, null, args); 31 | writer = new PrintWriter(dest, "UTF-8"); 32 | writer.print(result); 33 | writer.close(); 34 | } 35 | 36 | def dest_file(path) { 37 | return new File(outDir + "/net/jpountz/" + path); 38 | } 39 | 40 | def generate_decompressors() { 41 | compiledTemplate = get_template("decompressor.template"); 42 | for (type : ["Safe", "Unsafe"]) { 43 | for (size : ["Fast", "Safe"]) { 44 | dest = dest_file("lz4/LZ4Java" + type + size + "Decompressor.java"); 45 | args = new HashMap(); 46 | args.put("type", type); 47 | args.put("size", size); 48 | execute_template(compiledTemplate, dest, args); 49 | } 50 | } 51 | } 52 | 53 | def generate_compressors() { 54 | compiledTemplate = get_template("compressor.template"); 55 | for (type : ["Safe", "Unsafe"]) { 56 | dest = dest_file("lz4/LZ4Java" + type + "Compressor.java"); 57 | args = new HashMap(); 58 | args.put("type", type); 59 | execute_template(compiledTemplate, dest, args); 60 | } 61 | } 62 | 63 | def generate_hc_compressors() { 64 | compiledTemplate = get_template("compressor_hc.template"); 65 | for (type : ["Safe", "Unsafe"]) { 66 | dest = dest_file("lz4/LZ4HCJava" + type + "Compressor.java"); 67 | args = new HashMap(); 68 | args.put("type", type); 69 | execute_template(compiledTemplate, dest, args); 70 | } 71 | } 72 | 73 | def generate_xxhash() { 74 | for (bitness : ["32", "64"]) { 75 | compiledTemplate = get_template("xxhash" + bitness + ".template"); 76 | for (type : ["Safe", "Unsafe"]) { 77 | dest = dest_file("xxhash/XXHash" + bitness + "Java" + type + ".java"); 78 | args = new HashMap(); 79 | args.put("type", type); 80 | execute_template(compiledTemplate, dest, args); 81 | } 82 | } 83 | } 84 | 85 | def generate_streaming_xxhash() { 86 | for (bitness : ["32", "64"]) { 87 | compiledTemplate = get_template("xxhash" + bitness + "_streaming.template"); 88 | for (type : ["Safe", "Unsafe"]) { 89 | dest = dest_file("xxhash/StreamingXXHash" + bitness + "Java" + type + ".java"); 90 | args = new HashMap(); 91 | args.put("type", type); 92 | execute_template(compiledTemplate, dest, args); 93 | } 94 | } 95 | } 96 | 97 | generate_decompressors(); 98 | generate_compressors(); 99 | generate_hc_compressors(); 100 | generate_xxhash(); 101 | generate_streaming_xxhash(); 102 | -------------------------------------------------------------------------------- /src/build/pom-pure-java.template: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 21 | 22 | Pure-java LZ4 and xxHash 23 | Pure java ports of the LZ4 compression algorithm and the xxHash hashing algorithm 24 | https://github.com/lz4/lz4-java 25 | 4.0.0 26 | ${ivy.pom.groupId} 27 | ${ivy.pom.artifactId} 28 | ${ivy.pom.packaging} 29 | ${ivy.pom.version} 30 | 31 | 32 | The Apache Software License, Version 2.0 33 | http://www.apache.org/licenses/LICENSE-2.0.txt 34 | repo 35 | 36 | 37 | 38 | git://github.com/lz4/lz4-java.git 39 | https://github.com/lz4/lz4-java 40 | 41 | 42 | 43 | jpountz 44 | Adrien Grand 45 | jpountz@gmail.com 46 | 47 | 48 | odaira 49 | Rei Odaira 50 | Rei.Odaira@gmail.com 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/build/pom.template: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 21 | 22 | LZ4 and xxHash 23 | Java ports and bindings of the LZ4 compression algorithm and the xxHash hashing algorithm 24 | https://github.com/lz4/lz4-java 25 | 4.0.0 26 | ${ivy.pom.groupId} 27 | ${ivy.pom.artifactId} 28 | ${ivy.pom.packaging} 29 | ${ivy.pom.version} 30 | 31 | 32 | The Apache Software License, Version 2.0 33 | http://www.apache.org/licenses/LICENSE-2.0.txt 34 | repo 35 | 36 | 37 | 38 | git://github.com/lz4/lz4-java.git 39 | https://github.com/lz4/lz4-java 40 | 41 | 42 | 43 | jpountz 44 | Adrien Grand 45 | jpountz@gmail.com 46 | 47 | 48 | odaira 49 | Rei Odaira 50 | Rei.Odaira@gmail.com 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/build/source_templates/compress_hc.template: -------------------------------------------------------------------------------- 1 | @comment{ 2 | Copyright 2020 Adrien Grand and the lz4-java contributors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | } 16 | @Override 17 | public int compress(${storage} src, int srcOff, int srcLen, ${storage} dest, int destOff, int maxDestLen) { 18 | @if{ storage == "ByteBuffer"} 19 | if (src.hasArray() && dest.hasArray()) { 20 | return compress(src.array(), srcOff + src.arrayOffset(), srcLen, dest.array(), destOff + dest.arrayOffset(), maxDestLen); 21 | } 22 | src = ${utils}.inNativeByteOrder(src); 23 | dest = ${utils}.inNativeByteOrder(dest); 24 | @end{} 25 | ${utils}.checkRange(src, srcOff, srcLen); 26 | ${utils}.checkRange(dest, destOff, maxDestLen); 27 | 28 | final int srcEnd = srcOff + srcLen; 29 | final int destEnd = destOff + maxDestLen; 30 | final int mfLimit = srcEnd - MF_LIMIT; 31 | final int matchLimit = srcEnd - LAST_LITERALS; 32 | 33 | int sOff = srcOff; 34 | int dOff = destOff; 35 | int anchor = sOff++; 36 | 37 | final HashTable ht = new HashTable(srcOff); 38 | final Match match0 = new Match(); 39 | final Match match1 = new Match(); 40 | final Match match2 = new Match(); 41 | final Match match3 = new Match(); 42 | 43 | main: 44 | while (sOff < mfLimit) { 45 | if (!ht.insertAndFindBestMatch(src, sOff, matchLimit, match1)) { 46 | ++sOff; 47 | continue; 48 | } 49 | 50 | // saved, in case we would skip too much 51 | copyTo(match1, match0); 52 | 53 | search2: 54 | while (true) { 55 | assert match1.start >= anchor; 56 | if (match1.end() >= mfLimit 57 | || !ht.insertAndFindWiderMatch(src, match1.end() - 2, match1.start + 1, matchLimit, match1.len, match2)) { 58 | // no better match 59 | dOff = LZ4${utils}.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); 60 | anchor = sOff = match1.end(); 61 | continue main; 62 | } 63 | 64 | if (match0.start < match1.start) { 65 | if (match2.start < match1.start + match0.len) { // empirical 66 | copyTo(match0, match1); 67 | } 68 | } 69 | assert match2.start > match1.start; 70 | 71 | if (match2.start - match1.start < 3) { // First Match too small : removed 72 | copyTo(match2, match1); 73 | continue search2; 74 | } 75 | 76 | search3: 77 | while (true) { 78 | if (match2.start - match1.start < OPTIMAL_ML) { 79 | int newMatchLen = match1.len; 80 | if (newMatchLen > OPTIMAL_ML) { 81 | newMatchLen = OPTIMAL_ML; 82 | } 83 | if (match1.start + newMatchLen > match2.end() - MIN_MATCH) { 84 | newMatchLen = match2.start - match1.start + match2.len - MIN_MATCH; 85 | } 86 | final int correction = newMatchLen - (match2.start - match1.start); 87 | if (correction > 0) { 88 | match2.fix(correction); 89 | } 90 | } 91 | 92 | if (match2.start + match2.len >= mfLimit 93 | || !ht.insertAndFindWiderMatch(src, match2.end() - 3, match2.start, matchLimit, match2.len, match3)) { 94 | // no better match -> 2 sequences to encode 95 | if (match2.start < match1.end()) { 96 | match1.len = match2.start - match1.start; 97 | } 98 | // encode seq 1 99 | dOff = LZ4${utils}.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); 100 | anchor = sOff = match1.end(); 101 | // encode seq 2 102 | dOff = LZ4${utils}.encodeSequence(src, anchor, match2.start, match2.ref, match2.len, dest, dOff, destEnd); 103 | anchor = sOff = match2.end(); 104 | continue main; 105 | } 106 | 107 | if (match3.start < match1.end() + 3) { // Not enough space for match 2 : remove it 108 | if (match3.start >= match1.end()) { // // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 109 | if (match2.start < match1.end()) { 110 | final int correction = match1.end() - match2.start; 111 | match2.fix(correction); 112 | if (match2.len < MIN_MATCH) { 113 | copyTo(match3, match2); 114 | } 115 | } 116 | 117 | dOff = LZ4${utils}.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); 118 | anchor = sOff = match1.end(); 119 | 120 | copyTo(match3, match1); 121 | copyTo(match2, match0); 122 | 123 | continue search2; 124 | } 125 | 126 | copyTo(match3, match2); 127 | continue search3; 128 | } 129 | 130 | // OK, now we have 3 ascending matches; let's write at least the first one 131 | if (match2.start < match1.end()) { 132 | if (match2.start - match1.start < ML_MASK) { 133 | if (match1.len > OPTIMAL_ML) { 134 | match1.len = OPTIMAL_ML; 135 | } 136 | if (match1.end() > match2.end() - MIN_MATCH) { 137 | match1.len = match2.end() - match1.start - MIN_MATCH; 138 | } 139 | final int correction = match1.end() - match2.start; 140 | match2.fix(correction); 141 | } else { 142 | match1.len = match2.start - match1.start; 143 | } 144 | } 145 | 146 | dOff = LZ4${utils}.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); 147 | anchor = sOff = match1.end(); 148 | 149 | copyTo(match2, match1); 150 | copyTo(match3, match2); 151 | 152 | continue search3; 153 | } 154 | 155 | } 156 | 157 | } 158 | 159 | dOff = LZ4${utils}.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); 160 | return dOff - destOff; 161 | } 162 | 163 | -------------------------------------------------------------------------------- /src/build/source_templates/compressor.template: -------------------------------------------------------------------------------- 1 | // Auto-generated: DO NOT EDIT 2 | 3 | package net.jpountz.lz4; 4 | 5 | import static net.jpountz.lz4.LZ4Constants.*; 6 | import static net.jpountz.lz4.LZ4Utils.*; 7 | 8 | import java.nio.ByteBuffer; 9 | import java.util.Arrays; 10 | 11 | import net.jpountz.util.ByteBufferUtils; 12 | import net.jpountz.util.${type}Utils; 13 | 14 | /** 15 | * Compressor. 16 | */ 17 | final class LZ4Java${type}Compressor extends LZ4Compressor { 18 | 19 | public static final LZ4Compressor INSTANCE = new LZ4Java${type}Compressor(); 20 | 21 | @include{"compress.template"; storage = "byte[]"; utils = "${type}Utils"} 22 | @include{"compress.template"; storage = "ByteBuffer"; utils = "ByteBufferUtils"} 23 | } 24 | -------------------------------------------------------------------------------- /src/build/source_templates/compressor_hc.template: -------------------------------------------------------------------------------- 1 | // Auto-generated: DO NOT EDIT 2 | 3 | package net.jpountz.lz4; 4 | 5 | import static net.jpountz.lz4.LZ4Constants.*; 6 | import static net.jpountz.lz4.LZ4Utils.*; 7 | 8 | import java.nio.ByteBuffer; 9 | import java.util.Arrays; 10 | 11 | import net.jpountz.lz4.LZ4Utils.Match; 12 | import net.jpountz.util.ByteBufferUtils; 13 | import net.jpountz.util.${type}Utils; 14 | 15 | /** 16 | * High compression compressor. 17 | */ 18 | final class LZ4HCJava${type}Compressor extends LZ4Compressor { 19 | 20 | public static final LZ4Compressor INSTANCE = new LZ4HCJava${type}Compressor(); 21 | 22 | private final int maxAttempts; 23 | final int compressionLevel; 24 | 25 | LZ4HCJava${type}Compressor() { this(DEFAULT_COMPRESSION_LEVEL); } 26 | LZ4HCJava${type}Compressor(int compressionLevel) { 27 | this.maxAttempts = 1<<(compressionLevel-1); 28 | this.compressionLevel = compressionLevel; 29 | } 30 | 31 | @include{"hashtable.template"} 32 | @include{"compress_hc.template"; storage = "byte[]"; utils = "${type}Utils"} 33 | @include{"compress_hc.template"; storage = "ByteBuffer"; utils = "ByteBufferUtils"} 34 | } 35 | -------------------------------------------------------------------------------- /src/build/source_templates/decompress.template: -------------------------------------------------------------------------------- 1 | @comment{ 2 | Copyright 2020 Adrien Grand and the lz4-java contributors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | } 16 | @Override 17 | public int decompress(${storage} src, final int srcOff@if{ size == "Safe" }, final int srcLen @end{}, ${storage} dest, final int destOff, int destLen) { 18 | @if{ storage == "ByteBuffer"} 19 | if (src.hasArray() && dest.hasArray()) { 20 | return decompress(src.array(), srcOff + src.arrayOffset()@if{ size == "Safe" }, srcLen@end{}, dest.array(), destOff + dest.arrayOffset(), destLen); 21 | } 22 | src = ${utils}.inNativeByteOrder(src); 23 | dest = ${utils}.inNativeByteOrder(dest); 24 | @end{} 25 | @if{ size == "Safe" } 26 | ${utils}.checkRange(src, srcOff, srcLen); 27 | ${utils}.checkRange(dest, destOff, destLen); 28 | 29 | if (destLen == 0) { 30 | if (srcLen != 1 || ${utils}.readByte(src, srcOff) != 0) { 31 | throw new LZ4Exception("Output buffer too small"); 32 | } 33 | return 0; 34 | } 35 | 36 | final int srcEnd = srcOff + srcLen; 37 | @else{} 38 | ${utils}.checkRange(src, srcOff); 39 | ${utils}.checkRange(dest, destOff, destLen); 40 | 41 | if (destLen == 0) { 42 | if (${utils}.readByte(src, srcOff) != 0) { 43 | throw new LZ4Exception("Malformed input at " + srcOff); 44 | } 45 | return 1; 46 | } 47 | @end{} 48 | 49 | final int destEnd = destOff + destLen; 50 | 51 | int sOff = srcOff; 52 | int dOff = destOff; 53 | 54 | while (true) { 55 | final int token = ${utils}.readByte(src, sOff) & 0xFF; 56 | ++sOff; 57 | 58 | // literals 59 | int literalLen = token >>> ML_BITS; 60 | if (literalLen == RUN_MASK) { 61 | byte len = (byte) 0xFF; 62 | while (@if{ size == "Safe" }sOff < srcEnd &&@end{}(len = ${utils}.readByte(src, sOff++)) == (byte) 0xFF) { 63 | literalLen += 0xFF; 64 | } 65 | literalLen += len & 0xFF; 66 | } 67 | 68 | final int literalCopyEnd = dOff + literalLen; 69 | @if{ size == "Fast" } 70 | if (literalCopyEnd > destEnd - COPY_LENGTH) { 71 | if (literalCopyEnd != destEnd) { 72 | throw new LZ4Exception("Malformed input at " + sOff); 73 | @else{} 74 | if (literalCopyEnd > destEnd - COPY_LENGTH || sOff + literalLen > srcEnd - COPY_LENGTH) { 75 | if (literalCopyEnd > destEnd) { 76 | throw new LZ4Exception(); 77 | } else if (sOff + literalLen != srcEnd) { 78 | throw new LZ4Exception("Malformed input at " + sOff); 79 | @end{} 80 | } else { 81 | LZ4${utils}.safeArraycopy(src, sOff, dest, dOff, literalLen); 82 | sOff += literalLen; 83 | dOff = literalCopyEnd; 84 | break; // EOF 85 | } 86 | } 87 | 88 | LZ4${utils}.wildArraycopy(src, sOff, dest, dOff, literalLen); 89 | sOff += literalLen; 90 | dOff = literalCopyEnd; 91 | 92 | // matchs 93 | final int matchDec = ${utils}.readShortLE(src, sOff); 94 | sOff += 2; 95 | int matchOff = dOff - matchDec; 96 | 97 | if (matchOff < destOff) { 98 | throw new LZ4Exception("Malformed input at " + sOff); 99 | } 100 | 101 | int matchLen = token & ML_MASK; 102 | if (matchLen == ML_MASK) { 103 | byte len = (byte) 0xFF; 104 | while (@if{ size == "Safe" }sOff < srcEnd &&@end{}(len = ${utils}.readByte(src, sOff++)) == (byte) 0xFF) { 105 | matchLen += 0xFF; 106 | } 107 | matchLen += len & 0xFF; 108 | } 109 | matchLen += MIN_MATCH; 110 | 111 | final int matchCopyEnd = dOff + matchLen; 112 | 113 | if (matchCopyEnd > destEnd - COPY_LENGTH) { 114 | if (matchCopyEnd > destEnd) { 115 | throw new LZ4Exception("Malformed input at " + sOff); 116 | } 117 | LZ4${utils}.safeIncrementalCopy(dest, matchOff, dOff, matchLen); 118 | } else { 119 | LZ4${utils}.wildIncrementalCopy(dest, matchOff, dOff, matchCopyEnd); 120 | } 121 | dOff = matchCopyEnd; 122 | } 123 | 124 | @if{ size == "Fast" } 125 | return sOff - srcOff; 126 | @else{} 127 | return dOff - destOff; 128 | @end{} 129 | } 130 | -------------------------------------------------------------------------------- /src/build/source_templates/decompressor.template: -------------------------------------------------------------------------------- 1 | // Auto-generated: DO NOT EDIT 2 | 3 | package net.jpountz.lz4; 4 | 5 | import static net.jpountz.lz4.LZ4Constants.*; 6 | 7 | import java.nio.ByteBuffer; 8 | 9 | import net.jpountz.util.ByteBufferUtils; 10 | import net.jpountz.util.${type}Utils; 11 | 12 | /** 13 | * Decompressor. 14 | */ 15 | final class LZ4Java@{type}${size}Decompressor extends LZ4${size}Decompressor { 16 | 17 | public static final LZ4${size}Decompressor INSTANCE = new LZ4Java${type}${size}Decompressor(); 18 | 19 | @include{"decompress.template"; storage = "byte[]"; utils = "${type}Utils"} 20 | @include{"decompress.template"; storage = "ByteBuffer"; utils = "ByteBufferUtils"} 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/build/source_templates/hashtable.template: -------------------------------------------------------------------------------- 1 | @comment{ 2 | Copyright 2020 Adrien Grand and the lz4-java contributors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | } 16 | private class HashTable { 17 | static final int MASK = MAX_DISTANCE - 1; 18 | int nextToUpdate; 19 | private final int base; 20 | private final int[] hashTable; 21 | private final short[] chainTable; 22 | 23 | HashTable(int base) { 24 | this.base = base; 25 | nextToUpdate = base; 26 | hashTable = new int[HASH_TABLE_SIZE_HC]; 27 | Arrays.fill(hashTable, -1); 28 | chainTable = new short[MAX_DISTANCE]; 29 | } 30 | 31 | private int hashPointer(byte[] bytes, int off) { 32 | final int v = ${type}Utils.readInt(bytes, off); 33 | return hashPointer(v); 34 | } 35 | 36 | private int hashPointer(ByteBuffer bytes, int off) { 37 | final int v = ByteBufferUtils.readInt(bytes, off); 38 | return hashPointer(v); 39 | } 40 | 41 | private int hashPointer(int v) { 42 | final int h = hashHC(v); 43 | return hashTable[h]; 44 | } 45 | 46 | private int next(int off) { 47 | return off - (chainTable[off & MASK] & 0xFFFF); 48 | } 49 | 50 | private void addHash(byte[] bytes, int off) { 51 | final int v = ${type}Utils.readInt(bytes, off); 52 | addHash(v, off); 53 | } 54 | 55 | private void addHash(ByteBuffer bytes, int off) { 56 | final int v = ByteBufferUtils.readInt(bytes, off); 57 | addHash(v, off); 58 | } 59 | 60 | private void addHash(int v, int off) { 61 | final int h = hashHC(v); 62 | int delta = off - hashTable[h]; 63 | assert delta > 0 : delta; 64 | if (delta >= MAX_DISTANCE) { 65 | delta = MAX_DISTANCE - 1; 66 | } 67 | chainTable[off & MASK] = (short) delta; 68 | hashTable[h] = off; 69 | } 70 | 71 | void insert(int off, byte[] bytes) { 72 | for (; nextToUpdate < off; ++nextToUpdate) { 73 | addHash(bytes, nextToUpdate); 74 | } 75 | } 76 | 77 | void insert(int off, ByteBuffer bytes) { 78 | for (; nextToUpdate < off; ++nextToUpdate) { 79 | addHash(bytes, nextToUpdate); 80 | } 81 | } 82 | 83 | @foreach{ storage : ["byte[]", "ByteBuffer"] } 84 | @code{ utils = type + "Utils"; if (storage == "ByteBuffer") { utils = "ByteBufferUtils" } } 85 | boolean insertAndFindBestMatch(${storage} buf, int off, int matchLimit, Match match) { 86 | match.start = off; 87 | match.len = 0; 88 | int delta = 0; 89 | int repl = 0; 90 | 91 | insert(off, buf); 92 | 93 | int ref = hashPointer(buf, off); 94 | 95 | if (ref >= off - 4 && ref <= off && ref >= base) { // potential repetition 96 | if (LZ4${utils}.readIntEquals(buf, ref, off)) { // confirmed 97 | delta = off - ref; 98 | repl = match.len = MIN_MATCH + LZ4${utils}.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); 99 | match.ref = ref; 100 | } 101 | ref = next(ref); 102 | } 103 | 104 | for (int i = 0; i < maxAttempts; ++i) { 105 | if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { 106 | break; 107 | } 108 | if (LZ4${utils}.readIntEquals(buf, ref, off)) { 109 | final int matchLen = MIN_MATCH + LZ4${utils}.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); 110 | if (matchLen > match.len) { 111 | match.ref = ref; 112 | match.len = matchLen; 113 | } 114 | } 115 | ref = next(ref); 116 | } 117 | 118 | if (repl != 0) { 119 | int ptr = off; 120 | final int end = off + repl - (MIN_MATCH - 1); 121 | while (ptr < end - delta) { 122 | chainTable[ptr & MASK] = (short) delta; // pre load 123 | ++ptr; 124 | } 125 | do { 126 | chainTable[ptr & MASK] = (short) delta; 127 | hashTable[hashHC(${utils}.readInt(buf, ptr))] = ptr; 128 | ++ptr; 129 | } while (ptr < end); 130 | nextToUpdate = end; 131 | } 132 | 133 | return match.len != 0; 134 | } 135 | 136 | boolean insertAndFindWiderMatch(${storage} buf, int off, int startLimit, int matchLimit, int minLen, Match match) { 137 | match.len = minLen; 138 | 139 | insert(off, buf); 140 | 141 | final int delta = off - startLimit; 142 | int ref = hashPointer(buf, off); 143 | for (int i = 0; i < maxAttempts; ++i) { 144 | if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { 145 | break; 146 | } 147 | if (LZ4${utils}.readIntEquals(buf, ref, off)) { 148 | final int matchLenForward = MIN_MATCH +LZ4${utils}.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); 149 | final int matchLenBackward = LZ4${utils}.commonBytesBackward(buf, ref, off, base, startLimit); 150 | final int matchLen = matchLenBackward + matchLenForward; 151 | if (matchLen > match.len) { 152 | match.len = matchLen; 153 | match.ref = ref - matchLenBackward; 154 | match.start = off - matchLenBackward; 155 | } 156 | } 157 | ref = next(ref); 158 | } 159 | 160 | return match.len > minLen; 161 | } 162 | @end{} 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/build/source_templates/xxhash32.template: -------------------------------------------------------------------------------- 1 | // Auto-generated: DO NOT EDIT 2 | 3 | package net.jpountz.xxhash; 4 | 5 | import static net.jpountz.xxhash.XXHashConstants.*; 6 | import static java.lang.Integer.rotateLeft; 7 | 8 | import java.nio.ByteBuffer; 9 | 10 | import net.jpountz.util.${type}Utils; 11 | import net.jpountz.util.ByteBufferUtils; 12 | 13 | /** 14 | * {@link XXHash32} implementation. 15 | */ 16 | final class XXHash32Java${type} extends XXHash32 { 17 | 18 | public static final XXHash32 INSTANCE = new XXHash32Java${type}(); 19 | 20 | @include{"xxhash32_hash.template"; storage = "byte[]"; utils = type + "Utils"} 21 | @include{"xxhash32_hash.template"; storage = "ByteBuffer"; utils = "ByteBufferUtils"} 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/build/source_templates/xxhash32_hash.template: -------------------------------------------------------------------------------- 1 | @comment{ 2 | Copyright 2020 Adrien Grand and the lz4-java contributors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | } 16 | @Override 17 | public int hash(${storage} buf, int off, int len, int seed) { 18 | @if{storage == "ByteBuffer"} 19 | if (buf.hasArray()) { 20 | return hash(buf.array(), off + buf.arrayOffset(), len, seed); 21 | } 22 | ${utils}.checkRange(buf, off, len); 23 | buf = ${utils}.inLittleEndianOrder(buf); 24 | @else{} 25 | ${utils}.checkRange(buf, off, len); 26 | @end{} 27 | final int end = off + len; 28 | int h32; 29 | 30 | if (len >= 16) { 31 | final int limit = end - 16; 32 | int v1 = seed + PRIME1 + PRIME2; 33 | int v2 = seed + PRIME2; 34 | int v3 = seed + 0; 35 | int v4 = seed - PRIME1; 36 | do { 37 | v1 += ${utils}.readIntLE(buf, off) * PRIME2; 38 | v1 = rotateLeft(v1, 13); 39 | v1 *= PRIME1; 40 | off += 4; 41 | 42 | v2 += ${utils}.readIntLE(buf, off) * PRIME2; 43 | v2 = rotateLeft(v2, 13); 44 | v2 *= PRIME1; 45 | off += 4; 46 | 47 | v3 += ${utils}.readIntLE(buf, off) * PRIME2; 48 | v3 = rotateLeft(v3, 13); 49 | v3 *= PRIME1; 50 | off += 4; 51 | 52 | v4 += ${utils}.readIntLE(buf, off) * PRIME2; 53 | v4 = rotateLeft(v4, 13); 54 | v4 *= PRIME1; 55 | off += 4; 56 | } while (off <= limit); 57 | 58 | h32 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); 59 | } else { 60 | h32 = seed + PRIME5; 61 | } 62 | 63 | h32 += len; 64 | 65 | while (off <= end - 4) { 66 | h32 += ${utils}.readIntLE(buf, off) * PRIME3; 67 | h32 = rotateLeft(h32, 17) * PRIME4; 68 | off += 4; 69 | } 70 | 71 | while (off < end) { 72 | h32 += (${utils}.readByte(buf, off) & 0xFF) * PRIME5; 73 | h32 = rotateLeft(h32, 11) * PRIME1; 74 | ++off; 75 | } 76 | 77 | h32 ^= h32 >>> 15; 78 | h32 *= PRIME2; 79 | h32 ^= h32 >>> 13; 80 | h32 *= PRIME3; 81 | h32 ^= h32 >>> 16; 82 | 83 | return h32; 84 | } 85 | -------------------------------------------------------------------------------- /src/build/source_templates/xxhash32_streaming.template: -------------------------------------------------------------------------------- 1 | // Auto-generated: DO NOT EDIT 2 | 3 | package net.jpountz.xxhash; 4 | 5 | import static net.jpountz.xxhash.XXHashConstants.*; 6 | import static net.jpountz.util.${type}Utils.*; 7 | import static net.jpountz.util.SafeUtils.checkRange; 8 | import static java.lang.Integer.rotateLeft; 9 | 10 | /** 11 | * Streaming xxhash. 12 | */ 13 | final class StreamingXXHash32Java${type} extends AbstractStreamingXXHash32Java { 14 | 15 | static class Factory implements StreamingXXHash32.Factory { 16 | 17 | public static final StreamingXXHash32.Factory INSTANCE = new Factory(); 18 | 19 | @Override 20 | public StreamingXXHash32 newStreamingHash(int seed) { 21 | return new StreamingXXHash32Java${type}(seed); 22 | } 23 | 24 | } 25 | 26 | StreamingXXHash32Java${type}(int seed) { 27 | super(seed); 28 | } 29 | 30 | @Override 31 | public int getValue() { 32 | int h32; 33 | if (totalLen >= 16) { 34 | h32 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); 35 | } else { 36 | h32 = seed + PRIME5; 37 | } 38 | 39 | h32 += totalLen; 40 | 41 | int off = 0; 42 | while (off <= memSize - 4) { 43 | h32 += readIntLE(memory, off) * PRIME3; 44 | h32 = rotateLeft(h32, 17) * PRIME4; 45 | off += 4; 46 | } 47 | 48 | while (off < memSize) { 49 | h32 += (readByte(memory, off) & 0xFF) * PRIME5; 50 | h32 = rotateLeft(h32, 11) * PRIME1; 51 | ++off; 52 | } 53 | 54 | h32 ^= h32 >>> 15; 55 | h32 *= PRIME2; 56 | h32 ^= h32 >>> 13; 57 | h32 *= PRIME3; 58 | h32 ^= h32 >>> 16; 59 | 60 | return h32; 61 | } 62 | 63 | @Override 64 | public void update(byte[] buf, int off, int len) { 65 | checkRange(buf, off, len); 66 | 67 | totalLen += len; 68 | 69 | if (memSize + len < 16) { // fill in tmp buffer 70 | System.arraycopy(buf, off, memory, memSize, len); 71 | memSize += len; 72 | return; 73 | } 74 | 75 | final int end = off + len; 76 | 77 | if (memSize > 0) { // data left from previous update 78 | System.arraycopy(buf, off, memory, memSize, 16 - memSize); 79 | 80 | v1 += readIntLE(memory, 0) * PRIME2; 81 | v1 = rotateLeft(v1, 13); 82 | v1 *= PRIME1; 83 | 84 | v2 += readIntLE(memory, 4) * PRIME2; 85 | v2 = rotateLeft(v2, 13); 86 | v2 *= PRIME1; 87 | 88 | v3 += readIntLE(memory, 8) * PRIME2; 89 | v3 = rotateLeft(v3, 13); 90 | v3 *= PRIME1; 91 | 92 | v4 += readIntLE(memory, 12) * PRIME2; 93 | v4 = rotateLeft(v4, 13); 94 | v4 *= PRIME1; 95 | 96 | off += 16 - memSize; 97 | memSize = 0; 98 | } 99 | 100 | { 101 | final int limit = end - 16; 102 | int v1 = this.v1; 103 | int v2 = this.v2; 104 | int v3 = this.v3; 105 | int v4 = this.v4; 106 | 107 | while (off <= limit) { 108 | v1 += readIntLE(buf, off) * PRIME2; 109 | v1 = rotateLeft(v1, 13); 110 | v1 *= PRIME1; 111 | off += 4; 112 | 113 | v2 += readIntLE(buf, off) * PRIME2; 114 | v2 = rotateLeft(v2, 13); 115 | v2 *= PRIME1; 116 | off += 4; 117 | 118 | v3 += readIntLE(buf, off) * PRIME2; 119 | v3 = rotateLeft(v3, 13); 120 | v3 *= PRIME1; 121 | off += 4; 122 | 123 | v4 += readIntLE(buf, off) * PRIME2; 124 | v4 = rotateLeft(v4, 13); 125 | v4 *= PRIME1; 126 | off += 4; 127 | } 128 | 129 | this.v1 = v1; 130 | this.v2 = v2; 131 | this.v3 = v3; 132 | this.v4 = v4; 133 | } 134 | 135 | if (off < end) { 136 | System.arraycopy(buf, off, memory, 0, end - off); 137 | memSize = end - off; 138 | } 139 | } 140 | 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/build/source_templates/xxhash64.template: -------------------------------------------------------------------------------- 1 | // Auto-generated: DO NOT EDIT 2 | 3 | package net.jpountz.xxhash; 4 | 5 | import static net.jpountz.xxhash.XXHashConstants.*; 6 | import static java.lang.Long.rotateLeft; 7 | 8 | import java.nio.ByteBuffer; 9 | 10 | import net.jpountz.util.${type}Utils; 11 | import net.jpountz.util.ByteBufferUtils; 12 | 13 | /** 14 | * {@link XXHash64} implementation. 15 | */ 16 | final class XXHash64Java${type} extends XXHash64 { 17 | 18 | public static final XXHash64 INSTANCE = new XXHash64Java${type}(); 19 | 20 | @include{"xxhash64_hash.template"; storage = "byte[]"; utils = type + "Utils"} 21 | @include{"xxhash64_hash.template"; storage = "ByteBuffer"; utils = "ByteBufferUtils"} 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/build/source_templates/xxhash64_hash.template: -------------------------------------------------------------------------------- 1 | @comment{ 2 | Copyright 2020 Adrien Grand and the lz4-java contributors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | } 16 | @Override 17 | public long hash(${storage} buf, int off, int len, long seed) { 18 | @if{storage == "ByteBuffer"} 19 | if (buf.hasArray()) { 20 | return hash(buf.array(), off + buf.arrayOffset(), len, seed); 21 | } 22 | ${utils}.checkRange(buf, off, len); 23 | buf = ${utils}.inLittleEndianOrder(buf); 24 | @else{} 25 | ${utils}.checkRange(buf, off, len); 26 | @end{} 27 | final int end = off + len; 28 | long h64; 29 | 30 | if (len >= 32) { 31 | final int limit = end - 32; 32 | long v1 = seed + PRIME64_1 + PRIME64_2; 33 | long v2 = seed + PRIME64_2; 34 | long v3 = seed + 0; 35 | long v4 = seed - PRIME64_1; 36 | do { 37 | v1 += ${utils}.readLongLE(buf, off) * PRIME64_2; 38 | v1 = rotateLeft(v1, 31); 39 | v1 *= PRIME64_1; 40 | off += 8; 41 | 42 | v2 += ${utils}.readLongLE(buf, off) * PRIME64_2; 43 | v2 = rotateLeft(v2, 31); 44 | v2 *= PRIME64_1; 45 | off += 8; 46 | 47 | v3 += ${utils}.readLongLE(buf, off) * PRIME64_2; 48 | v3 = rotateLeft(v3, 31); 49 | v3 *= PRIME64_1; 50 | off += 8; 51 | 52 | v4 += ${utils}.readLongLE(buf, off) * PRIME64_2; 53 | v4 = rotateLeft(v4, 31); 54 | v4 *= PRIME64_1; 55 | off += 8; 56 | } while (off <= limit); 57 | 58 | h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); 59 | 60 | v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; 61 | h64 = h64 * PRIME64_1 + PRIME64_4; 62 | 63 | v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; 64 | h64 = h64 * PRIME64_1 + PRIME64_4; 65 | 66 | v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; 67 | h64 = h64 * PRIME64_1 + PRIME64_4; 68 | 69 | v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; 70 | h64 = h64 * PRIME64_1 + PRIME64_4; 71 | } else { 72 | h64 = seed + PRIME64_5; 73 | } 74 | 75 | h64 += len; 76 | 77 | while (off <= end - 8) { 78 | long k1 = ${utils}.readLongLE(buf, off); 79 | k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; 80 | h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; 81 | off += 8; 82 | } 83 | 84 | if (off <= end - 4) { 85 | h64 ^= (${utils}.readIntLE(buf, off) & 0xFFFFFFFFL) * PRIME64_1; 86 | h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; 87 | off += 4; 88 | } 89 | 90 | while (off < end) { 91 | h64 ^= (${utils}.readByte(buf, off) & 0xFF) * PRIME64_5; 92 | h64 = rotateLeft(h64, 11) * PRIME64_1; 93 | ++off; 94 | } 95 | 96 | h64 ^= h64 >>> 33; 97 | h64 *= PRIME64_2; 98 | h64 ^= h64 >>> 29; 99 | h64 *= PRIME64_3; 100 | h64 ^= h64 >>> 32; 101 | 102 | return h64; 103 | } 104 | -------------------------------------------------------------------------------- /src/build/source_templates/xxhash64_streaming.template: -------------------------------------------------------------------------------- 1 | // Auto-generated: DO NOT EDIT 2 | 3 | package net.jpountz.xxhash; 4 | 5 | import static net.jpountz.xxhash.XXHashConstants.*; 6 | import static net.jpountz.util.${type}Utils.*; 7 | import static net.jpountz.util.SafeUtils.checkRange; 8 | import static java.lang.Long.rotateLeft; 9 | 10 | /** 11 | * Streaming xxhash. 12 | */ 13 | final class StreamingXXHash64Java${type} extends AbstractStreamingXXHash64Java { 14 | 15 | static class Factory implements StreamingXXHash64.Factory { 16 | 17 | public static final StreamingXXHash64.Factory INSTANCE = new Factory(); 18 | 19 | @Override 20 | public StreamingXXHash64 newStreamingHash(long seed) { 21 | return new StreamingXXHash64Java${type}(seed); 22 | } 23 | 24 | } 25 | 26 | StreamingXXHash64Java${type}(long seed) { 27 | super(seed); 28 | } 29 | 30 | @Override 31 | public long getValue() { 32 | long h64; 33 | if (totalLen >= 32) { 34 | long v1 = this.v1; 35 | long v2 = this.v2; 36 | long v3 = this.v3; 37 | long v4 = this.v4; 38 | 39 | h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); 40 | 41 | v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; 42 | h64 = h64*PRIME64_1 + PRIME64_4; 43 | 44 | v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; 45 | h64 = h64*PRIME64_1 + PRIME64_4; 46 | 47 | v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; 48 | h64 = h64*PRIME64_1 + PRIME64_4; 49 | 50 | v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; 51 | h64 = h64*PRIME64_1 + PRIME64_4; 52 | } else { 53 | h64 = seed + PRIME64_5; 54 | } 55 | 56 | h64 += totalLen; 57 | 58 | int off = 0; 59 | while (off <= memSize - 8) { 60 | long k1 = readLongLE(memory, off); 61 | k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; 62 | h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; 63 | off += 8; 64 | } 65 | 66 | if (off <= memSize - 4) { 67 | h64 ^= (readIntLE(memory, off) & 0xFFFFFFFFL) * PRIME64_1; 68 | h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; 69 | off += 4; 70 | } 71 | 72 | while (off < memSize) { 73 | h64 ^= (memory[off] & 0xFF) * PRIME64_5; 74 | h64 = rotateLeft(h64, 11) * PRIME64_1; 75 | ++off; 76 | } 77 | 78 | h64 ^= h64 >>> 33; 79 | h64 *= PRIME64_2; 80 | h64 ^= h64 >>> 29; 81 | h64 *= PRIME64_3; 82 | h64 ^= h64 >>> 32; 83 | 84 | return h64; 85 | } 86 | 87 | @Override 88 | public void update(byte[] buf, int off, int len) { 89 | checkRange(buf, off, len); 90 | 91 | totalLen += len; 92 | 93 | if (memSize + len < 32) { // fill in tmp buffer 94 | System.arraycopy(buf, off, memory, memSize, len); 95 | memSize += len; 96 | return; 97 | } 98 | 99 | final int end = off + len; 100 | 101 | if (memSize > 0) { // data left from previous update 102 | System.arraycopy(buf, off, memory, memSize, 32 - memSize); 103 | 104 | v1 += readLongLE(memory, 0) * PRIME64_2; 105 | v1 = rotateLeft(v1, 31); 106 | v1 *= PRIME64_1; 107 | 108 | v2 += readLongLE(memory, 8) * PRIME64_2; 109 | v2 = rotateLeft(v2, 31); 110 | v2 *= PRIME64_1; 111 | 112 | v3 += readLongLE(memory, 16) * PRIME64_2; 113 | v3 = rotateLeft(v3, 31); 114 | v3 *= PRIME64_1; 115 | 116 | v4 += readLongLE(memory, 24) * PRIME64_2; 117 | v4 = rotateLeft(v4, 31); 118 | v4 *= PRIME64_1; 119 | 120 | off += 32 - memSize; 121 | memSize = 0; 122 | } 123 | 124 | { 125 | final int limit = end - 32; 126 | long v1 = this.v1; 127 | long v2 = this.v2; 128 | long v3 = this.v3; 129 | long v4 = this.v4; 130 | 131 | while (off <= limit) { 132 | v1 += readLongLE(buf, off) * PRIME64_2; 133 | v1 = rotateLeft(v1, 31); 134 | v1 *= PRIME64_1; 135 | off += 8; 136 | 137 | v2 += readLongLE(buf, off) * PRIME64_2; 138 | v2 = rotateLeft(v2, 31); 139 | v2 *= PRIME64_1; 140 | off += 8; 141 | 142 | v3 += readLongLE(buf, off) * PRIME64_2; 143 | v3 = rotateLeft(v3, 31); 144 | v3 *= PRIME64_1; 145 | off += 8; 146 | 147 | v4 += readLongLE(buf, off) * PRIME64_2; 148 | v4 = rotateLeft(v4, 31); 149 | v4 *= PRIME64_1; 150 | off += 8; 151 | } 152 | 153 | this.v1 = v1; 154 | this.v2 = v2; 155 | this.v3 = v3; 156 | this.v4 = v4; 157 | } 158 | 159 | if (off < end) { 160 | System.arraycopy(buf, off, memory, 0, end - off); 161 | memSize = end - off; 162 | } 163 | } 164 | 165 | } 166 | 167 | -------------------------------------------------------------------------------- /src/java-unsafe/net/jpountz/lz4/LZ4UnsafeUtils.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.lz4.LZ4Constants.COPY_LENGTH; 20 | import static net.jpountz.lz4.LZ4Constants.LAST_LITERALS; 21 | import static net.jpountz.lz4.LZ4Constants.ML_BITS; 22 | import static net.jpountz.lz4.LZ4Constants.ML_MASK; 23 | import static net.jpountz.lz4.LZ4Constants.RUN_MASK; 24 | import static net.jpountz.util.UnsafeUtils.readByte; 25 | import static net.jpountz.util.UnsafeUtils.readInt; 26 | import static net.jpountz.util.UnsafeUtils.readLong; 27 | import static net.jpountz.util.UnsafeUtils.readShort; 28 | import static net.jpountz.util.UnsafeUtils.writeByte; 29 | import static net.jpountz.util.UnsafeUtils.writeInt; 30 | import static net.jpountz.util.UnsafeUtils.writeLong; 31 | import static net.jpountz.util.UnsafeUtils.writeShort; 32 | import static net.jpountz.util.Utils.NATIVE_BYTE_ORDER; 33 | 34 | import java.nio.ByteOrder; 35 | 36 | enum LZ4UnsafeUtils { 37 | ; 38 | 39 | static void safeArraycopy(byte[] src, int srcOff, byte[] dest, int destOff, int len) { 40 | final int fastLen = len & 0xFFFFFFF8; 41 | wildArraycopy(src, srcOff, dest, destOff, fastLen); 42 | for (int i = 0, slowLen = len & 0x7; i < slowLen; i += 1) { 43 | writeByte(dest, destOff + fastLen + i, readByte(src, srcOff + fastLen + i)); 44 | } 45 | } 46 | 47 | static void wildArraycopy(byte[] src, int srcOff, byte[] dest, int destOff, int len) { 48 | for (int i = 0; i < len; i += 8) { 49 | writeLong(dest, destOff + i, readLong(src, srcOff + i)); 50 | } 51 | } 52 | 53 | static void wildIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchCopyEnd) { 54 | if (dOff - matchOff < 4) { 55 | for (int i = 0; i < 4; ++i) { 56 | writeByte(dest, dOff+i, readByte(dest, matchOff+i)); 57 | } 58 | dOff += 4; 59 | matchOff += 4; 60 | int dec = 0; 61 | assert dOff >= matchOff && dOff - matchOff < 8; 62 | switch (dOff - matchOff) { 63 | case 1: 64 | matchOff -= 3; 65 | break; 66 | case 2: 67 | matchOff -= 2; 68 | break; 69 | case 3: 70 | matchOff -= 3; 71 | dec = -1; 72 | break; 73 | case 5: 74 | dec = 1; 75 | break; 76 | case 6: 77 | dec = 2; 78 | break; 79 | case 7: 80 | dec = 3; 81 | break; 82 | default: 83 | break; 84 | } 85 | writeInt(dest, dOff, readInt(dest, matchOff)); 86 | dOff += 4; 87 | matchOff -= dec; 88 | } else if (dOff - matchOff < COPY_LENGTH) { 89 | writeLong(dest, dOff, readLong(dest, matchOff)); 90 | dOff += dOff - matchOff; 91 | } 92 | while (dOff < matchCopyEnd) { 93 | writeLong(dest, dOff, readLong(dest, matchOff)); 94 | dOff += 8; 95 | matchOff += 8; 96 | } 97 | } 98 | 99 | static void safeIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchLen) { 100 | for (int i = 0; i < matchLen; ++i) { 101 | dest[dOff + i] = dest[matchOff + i]; 102 | writeByte(dest, dOff + i, readByte(dest, matchOff + i)); 103 | } 104 | } 105 | 106 | static int readShortLittleEndian(byte[] src, int srcOff) { 107 | short s = readShort(src, srcOff); 108 | if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { 109 | s = Short.reverseBytes(s); 110 | } 111 | return s & 0xFFFF; 112 | } 113 | 114 | static void writeShortLittleEndian(byte[] dest, int destOff, int value) { 115 | short s = (short) value; 116 | if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { 117 | s = Short.reverseBytes(s); 118 | } 119 | writeShort(dest, destOff, s); 120 | } 121 | 122 | static boolean readIntEquals(byte[] src, int ref, int sOff) { 123 | return readInt(src, ref) == readInt(src, sOff); 124 | } 125 | 126 | static int commonBytes(byte[] src, int ref, int sOff, int srcLimit) { 127 | int matchLen = 0; 128 | while (sOff <= srcLimit - 8) { 129 | if (readLong(src, sOff) == readLong(src, ref)) { 130 | matchLen += 8; 131 | ref += 8; 132 | sOff += 8; 133 | } else { 134 | final int zeroBits; 135 | if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { 136 | zeroBits = Long.numberOfLeadingZeros(readLong(src, sOff) ^ readLong(src, ref)); 137 | } else { 138 | zeroBits = Long.numberOfTrailingZeros(readLong(src, sOff) ^ readLong(src, ref)); 139 | } 140 | return matchLen + (zeroBits >>> 3); 141 | } 142 | } 143 | while (sOff < srcLimit && readByte(src, ref++) == readByte(src, sOff++)) { 144 | ++matchLen; 145 | } 146 | return matchLen; 147 | } 148 | 149 | static int writeLen(int len, byte[] dest, int dOff) { 150 | while (len >= 0xFF) { 151 | writeByte(dest, dOff++, 0xFF); 152 | len -= 0xFF; 153 | } 154 | writeByte(dest, dOff++, len); 155 | return dOff; 156 | } 157 | 158 | static int encodeSequence(byte[] src, int anchor, int matchOff, int matchRef, int matchLen, byte[] dest, int dOff, int destEnd) { 159 | final int runLen = matchOff - anchor; 160 | final int tokenOff = dOff++; 161 | int token; 162 | 163 | if (runLen >= RUN_MASK) { 164 | token = (byte) (RUN_MASK << ML_BITS); 165 | dOff = writeLen(runLen - RUN_MASK, dest, dOff); 166 | } else { 167 | token = runLen << ML_BITS; 168 | } 169 | 170 | // copy literals 171 | wildArraycopy(src, anchor, dest, dOff, runLen); 172 | dOff += runLen; 173 | 174 | // encode offset 175 | final int matchDec = matchOff - matchRef; 176 | dest[dOff++] = (byte) matchDec; 177 | dest[dOff++] = (byte) (matchDec >>> 8); 178 | 179 | // encode match len 180 | matchLen -= 4; 181 | if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { 182 | throw new LZ4Exception("maxDestLen is too small"); 183 | } 184 | if (matchLen >= ML_MASK) { 185 | token |= ML_MASK; 186 | dOff = writeLen(matchLen - RUN_MASK, dest, dOff); 187 | } else { 188 | token |= matchLen; 189 | } 190 | 191 | dest[tokenOff] = (byte) token; 192 | 193 | return dOff; 194 | } 195 | 196 | static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) { 197 | int count = 0; 198 | while (o1 > l1 && o2 > l2 && readByte(b, --o1) == readByte(b, --o2)) { 199 | ++count; 200 | } 201 | return count; 202 | } 203 | 204 | static int lastLiterals(byte[] src, int sOff, int srcLen, byte[] dest, int dOff, int destEnd) { 205 | return LZ4SafeUtils.lastLiterals(src, sOff, srcLen, dest, dOff, destEnd); 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /src/java-unsafe/net/jpountz/util/UnsafeUtils.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.util; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.util.Utils.NATIVE_BYTE_ORDER; 20 | 21 | import java.lang.reflect.Field; 22 | import java.nio.ByteOrder; 23 | 24 | import sun.misc.Unsafe; 25 | 26 | public enum UnsafeUtils { 27 | ; 28 | 29 | private static final Unsafe UNSAFE; 30 | private static final long BYTE_ARRAY_OFFSET; 31 | private static final int BYTE_ARRAY_SCALE; 32 | private static final long INT_ARRAY_OFFSET; 33 | private static final int INT_ARRAY_SCALE; 34 | private static final long SHORT_ARRAY_OFFSET; 35 | private static final int SHORT_ARRAY_SCALE; 36 | 37 | static { 38 | try { 39 | Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); 40 | theUnsafe.setAccessible(true); 41 | UNSAFE = (Unsafe) theUnsafe.get(null); 42 | BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); 43 | BYTE_ARRAY_SCALE = UNSAFE.arrayIndexScale(byte[].class); 44 | INT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(int[].class); 45 | INT_ARRAY_SCALE = UNSAFE.arrayIndexScale(int[].class); 46 | SHORT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(short[].class); 47 | SHORT_ARRAY_SCALE = UNSAFE.arrayIndexScale(short[].class); 48 | } catch (IllegalAccessException e) { 49 | throw new ExceptionInInitializerError("Cannot access Unsafe"); 50 | } catch (NoSuchFieldException e) { 51 | throw new ExceptionInInitializerError("Cannot access Unsafe"); 52 | } catch (SecurityException e) { 53 | throw new ExceptionInInitializerError("Cannot access Unsafe"); 54 | } 55 | } 56 | 57 | public static void checkRange(byte[] buf, int off) { 58 | SafeUtils.checkRange(buf, off); 59 | } 60 | 61 | public static void checkRange(byte[] buf, int off, int len) { 62 | SafeUtils.checkRange(buf, off, len); 63 | } 64 | 65 | public static void checkLength(int len) { 66 | SafeUtils.checkLength(len); 67 | } 68 | 69 | public static byte readByte(byte[] src, int srcOff) { 70 | return UNSAFE.getByte(src, BYTE_ARRAY_OFFSET + BYTE_ARRAY_SCALE * srcOff); 71 | } 72 | 73 | public static void writeByte(byte[] src, int srcOff, byte value) { 74 | UNSAFE.putByte(src, BYTE_ARRAY_OFFSET + BYTE_ARRAY_SCALE * srcOff, (byte) value); 75 | } 76 | 77 | public static void writeByte(byte[] src, int srcOff, int value) { 78 | writeByte(src, srcOff, (byte) value); 79 | } 80 | 81 | public static long readLong(byte[] src, int srcOff) { 82 | return UNSAFE.getLong(src, BYTE_ARRAY_OFFSET + srcOff); 83 | } 84 | 85 | public static long readLongLE(byte[] src, int srcOff) { 86 | long i = readLong(src, srcOff); 87 | if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { 88 | i = Long.reverseBytes(i); 89 | } 90 | return i; 91 | } 92 | 93 | public static void writeLong(byte[] dest, int destOff, long value) { 94 | UNSAFE.putLong(dest, BYTE_ARRAY_OFFSET + destOff, value); 95 | } 96 | 97 | public static int readInt(byte[] src, int srcOff) { 98 | return UNSAFE.getInt(src, BYTE_ARRAY_OFFSET + srcOff); 99 | } 100 | 101 | public static int readIntLE(byte[] src, int srcOff) { 102 | int i = readInt(src, srcOff); 103 | if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { 104 | i = Integer.reverseBytes(i); 105 | } 106 | return i; 107 | } 108 | 109 | public static void writeInt(byte[] dest, int destOff, int value) { 110 | UNSAFE.putInt(dest, BYTE_ARRAY_OFFSET + destOff, value); 111 | } 112 | 113 | public static short readShort(byte[] src, int srcOff) { 114 | return UNSAFE.getShort(src, BYTE_ARRAY_OFFSET + srcOff); 115 | } 116 | 117 | public static int readShortLE(byte[] src, int srcOff) { 118 | short s = readShort(src, srcOff); 119 | if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { 120 | s = Short.reverseBytes(s); 121 | } 122 | return s & 0xFFFF; 123 | } 124 | 125 | public static void writeShort(byte[] dest, int destOff, short value) { 126 | UNSAFE.putShort(dest, BYTE_ARRAY_OFFSET + destOff, value); 127 | } 128 | 129 | public static void writeShortLE(byte[] buf, int off, int v) { 130 | writeByte(buf, off, (byte) v); 131 | writeByte(buf, off + 1, (byte) (v >>> 8)); 132 | } 133 | 134 | public static int readInt(int[] src, int srcOff) { 135 | return UNSAFE.getInt(src, INT_ARRAY_OFFSET + INT_ARRAY_SCALE * srcOff); 136 | } 137 | 138 | public static void writeInt(int[] dest, int destOff, int value) { 139 | UNSAFE.putInt(dest, INT_ARRAY_OFFSET + INT_ARRAY_SCALE * destOff, value); 140 | } 141 | 142 | public static int readShort(short[] src, int srcOff) { 143 | return UNSAFE.getShort(src, SHORT_ARRAY_OFFSET + SHORT_ARRAY_SCALE * srcOff) & 0xFFFF; 144 | } 145 | 146 | public static void writeShort(short[] dest, int destOff, int value) { 147 | UNSAFE.putShort(dest, SHORT_ARRAY_OFFSET + SHORT_ARRAY_SCALE * destOff, (short) value); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4ByteBufferUtils.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.lz4.LZ4Constants.COPY_LENGTH; 20 | import static net.jpountz.lz4.LZ4Constants.LAST_LITERALS; 21 | import static net.jpountz.lz4.LZ4Constants.ML_BITS; 22 | import static net.jpountz.lz4.LZ4Constants.ML_MASK; 23 | import static net.jpountz.lz4.LZ4Constants.RUN_MASK; 24 | import static net.jpountz.util.ByteBufferUtils.readByte; 25 | import static net.jpountz.util.ByteBufferUtils.readInt; 26 | import static net.jpountz.util.ByteBufferUtils.readLong; 27 | import static net.jpountz.util.ByteBufferUtils.writeByte; 28 | import static net.jpountz.util.ByteBufferUtils.writeInt; 29 | import static net.jpountz.util.ByteBufferUtils.writeLong; 30 | 31 | import java.nio.ByteBuffer; 32 | import java.nio.ByteOrder; 33 | 34 | enum LZ4ByteBufferUtils { 35 | ; 36 | static int hash(ByteBuffer buf, int i) { 37 | return LZ4Utils.hash(readInt(buf, i)); 38 | } 39 | 40 | static int hash64k(ByteBuffer buf, int i) { 41 | return LZ4Utils.hash64k(readInt(buf, i)); 42 | } 43 | 44 | static boolean readIntEquals(ByteBuffer buf, int i, int j) { 45 | return buf.getInt(i) == buf.getInt(j); 46 | } 47 | 48 | static void safeIncrementalCopy(ByteBuffer dest, int matchOff, int dOff, int matchLen) { 49 | for (int i = 0; i < matchLen; ++i) { 50 | dest.put(dOff + i, dest.get(matchOff + i)); 51 | } 52 | } 53 | 54 | static void wildIncrementalCopy(ByteBuffer dest, int matchOff, int dOff, int matchCopyEnd) { 55 | if (dOff - matchOff < 4) { 56 | for (int i = 0; i < 4; ++i) { 57 | writeByte(dest, dOff+i, readByte(dest, matchOff+i)); 58 | } 59 | dOff += 4; 60 | matchOff += 4; 61 | int dec = 0; 62 | assert dOff >= matchOff && dOff - matchOff < 8; 63 | switch (dOff - matchOff) { 64 | case 1: 65 | matchOff -= 3; 66 | break; 67 | case 2: 68 | matchOff -= 2; 69 | break; 70 | case 3: 71 | matchOff -= 3; 72 | dec = -1; 73 | break; 74 | case 5: 75 | dec = 1; 76 | break; 77 | case 6: 78 | dec = 2; 79 | break; 80 | case 7: 81 | dec = 3; 82 | break; 83 | default: 84 | break; 85 | } 86 | writeInt(dest, dOff, readInt(dest, matchOff)); 87 | dOff += 4; 88 | matchOff -= dec; 89 | } else if (dOff - matchOff < COPY_LENGTH) { 90 | writeLong(dest, dOff, readLong(dest, matchOff)); 91 | dOff += dOff - matchOff; 92 | } 93 | while (dOff < matchCopyEnd) { 94 | writeLong(dest, dOff, readLong(dest, matchOff)); 95 | dOff += 8; 96 | matchOff += 8; 97 | } 98 | } 99 | 100 | static int commonBytes(ByteBuffer src, int ref, int sOff, int srcLimit) { 101 | int matchLen = 0; 102 | while (sOff <= srcLimit - 8) { 103 | if (readLong(src, sOff) == readLong(src, ref)) { 104 | matchLen += 8; 105 | ref += 8; 106 | sOff += 8; 107 | } else { 108 | final int zeroBits; 109 | if (src.order() == ByteOrder.BIG_ENDIAN) { 110 | zeroBits = Long.numberOfLeadingZeros(readLong(src, sOff) ^ readLong(src, ref)); 111 | } else { 112 | zeroBits = Long.numberOfTrailingZeros(readLong(src, sOff) ^ readLong(src, ref)); 113 | } 114 | return matchLen + (zeroBits >>> 3); 115 | } 116 | } 117 | while (sOff < srcLimit && readByte(src, ref++) == readByte(src, sOff++)) { 118 | ++matchLen; 119 | } 120 | return matchLen; 121 | } 122 | 123 | static int commonBytesBackward(ByteBuffer b, int o1, int o2, int l1, int l2) { 124 | int count = 0; 125 | while (o1 > l1 && o2 > l2 && b.get(--o1) == b.get(--o2)) { 126 | ++count; 127 | } 128 | return count; 129 | } 130 | 131 | static void safeArraycopy(ByteBuffer src, int sOff, ByteBuffer dest, int dOff, int len) { 132 | for (int i = 0; i < len; ++i) { 133 | dest.put(dOff + i, src.get(sOff + i)); 134 | } 135 | } 136 | 137 | static void wildArraycopy(ByteBuffer src, int sOff, ByteBuffer dest, int dOff, int len) { 138 | assert src.order().equals(dest.order()); 139 | try { 140 | for (int i = 0; i < len; i += 8) { 141 | dest.putLong(dOff + i, src.getLong(sOff + i)); 142 | } 143 | } catch (IndexOutOfBoundsException e) { 144 | throw new LZ4Exception("Malformed input at offset " + sOff); 145 | } 146 | } 147 | 148 | static int encodeSequence(ByteBuffer src, int anchor, int matchOff, int matchRef, int matchLen, ByteBuffer dest, int dOff, int destEnd) { 149 | final int runLen = matchOff - anchor; 150 | final int tokenOff = dOff++; 151 | 152 | if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { 153 | throw new LZ4Exception("maxDestLen is too small"); 154 | } 155 | 156 | int token; 157 | if (runLen >= RUN_MASK) { 158 | token = (byte) (RUN_MASK << ML_BITS); 159 | dOff = writeLen(runLen - RUN_MASK, dest, dOff); 160 | } else { 161 | token = runLen << ML_BITS; 162 | } 163 | 164 | // copy literals 165 | wildArraycopy(src, anchor, dest, dOff, runLen); 166 | dOff += runLen; 167 | 168 | // encode offset 169 | final int matchDec = matchOff - matchRef; 170 | dest.put(dOff++, (byte) matchDec); 171 | dest.put(dOff++, (byte) (matchDec >>> 8)); 172 | 173 | // encode match len 174 | matchLen -= 4; 175 | if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { 176 | throw new LZ4Exception("maxDestLen is too small"); 177 | } 178 | if (matchLen >= ML_MASK) { 179 | token |= ML_MASK; 180 | dOff = writeLen(matchLen - RUN_MASK, dest, dOff); 181 | } else { 182 | token |= matchLen; 183 | } 184 | 185 | dest.put(tokenOff, (byte) token); 186 | 187 | return dOff; 188 | } 189 | 190 | static int lastLiterals(ByteBuffer src, int sOff, int srcLen, ByteBuffer dest, int dOff, int destEnd) { 191 | final int runLen = srcLen; 192 | 193 | if (dOff + runLen + 1 + (runLen + 255 - RUN_MASK) / 255 > destEnd) { 194 | throw new LZ4Exception(); 195 | } 196 | 197 | if (runLen >= RUN_MASK) { 198 | dest.put(dOff++, (byte) (RUN_MASK << ML_BITS)); 199 | dOff = writeLen(runLen - RUN_MASK, dest, dOff); 200 | } else { 201 | dest.put(dOff++, (byte) (runLen << ML_BITS)); 202 | } 203 | // copy literals 204 | safeArraycopy(src, sOff, dest, dOff, runLen); 205 | dOff += runLen; 206 | 207 | return dOff; 208 | } 209 | 210 | static int writeLen(int len, ByteBuffer dest, int dOff) { 211 | while (len >= 0xFF) { 212 | dest.put(dOff++, (byte) 0xFF); 213 | len -= 0xFF; 214 | } 215 | dest.put(dOff++, (byte) len); 216 | return dOff; 217 | } 218 | 219 | static class Match { 220 | int start, ref, len; 221 | 222 | void fix(int correction) { 223 | start += correction; 224 | ref += correction; 225 | len -= correction; 226 | } 227 | 228 | int end() { 229 | return start + len; 230 | } 231 | } 232 | 233 | static void copyTo(Match m1, Match m2) { 234 | m2.len = m1.len; 235 | m2.start = m1.start; 236 | m2.ref = m1.ref; 237 | } 238 | 239 | } 240 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4Compressor.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteBuffer; 20 | import java.util.Arrays; 21 | 22 | /** 23 | * LZ4 compressor. 24 | *

25 | * Instances of this class are thread-safe. 26 | */ 27 | public abstract class LZ4Compressor { 28 | 29 | /** 30 | * Returns the maximum compressed length for an input of size length. 31 | * 32 | * @param length the input size in bytes 33 | * @return the maximum compressed length in bytes 34 | */ 35 | @SuppressWarnings("static-method") 36 | public final int maxCompressedLength(int length) { 37 | return LZ4Utils.maxCompressedLength(length); 38 | } 39 | 40 | /** 41 | * Compresses src[srcOff:srcOff+srcLen] into 42 | * dest[destOff:destOff+maxDestLen] and returns the compressed 43 | * length. 44 | * 45 | * This method will throw a {@link LZ4Exception} if this compressor is unable 46 | * to compress the input into less than maxDestLen bytes. To 47 | * prevent this exception to be thrown, you should make sure that 48 | * maxDestLen >= maxCompressedLength(srcLen). 49 | * 50 | * @param src the source data 51 | * @param srcOff the start offset in src 52 | * @param srcLen the number of bytes to compress 53 | * @param dest the destination buffer 54 | * @param destOff the start offset in dest 55 | * @param maxDestLen the maximum number of bytes to write in dest 56 | * @throws LZ4Exception if maxDestLen is too small 57 | * @return the compressed size 58 | */ 59 | public abstract int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen); 60 | 61 | /** 62 | * Compresses src[srcOff:srcOff+srcLen] into 63 | * dest[destOff:destOff+maxDestLen] and returns the compressed 64 | * length. 65 | * 66 | * This method will throw a {@link LZ4Exception} if this compressor is unable 67 | * to compress the input into less than maxDestLen bytes. To 68 | * prevent this exception to be thrown, you should make sure that 69 | * maxDestLen >= maxCompressedLength(srcLen). 70 | * 71 | * {@link ByteBuffer} positions remain unchanged. 72 | * 73 | * @param src the source data 74 | * @param srcOff the start offset in src 75 | * @param srcLen the number of bytes to compress 76 | * @param dest the destination buffer 77 | * @param destOff the start offset in dest 78 | * @param maxDestLen the maximum number of bytes to write in dest 79 | * @throws LZ4Exception if maxDestLen is too small 80 | * @return the compressed size 81 | */ 82 | public abstract int compress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen); 83 | 84 | /** 85 | * Convenience method, equivalent to calling 86 | * {@link #compress(byte[], int, int, byte[], int, int) compress(src, srcOff, srcLen, dest, destOff, dest.length - destOff)}. 87 | * 88 | * @param src the source data 89 | * @param srcOff the start offset in src 90 | * @param srcLen the number of bytes to compress 91 | * @param dest the destination buffer 92 | * @param destOff the start offset in dest 93 | * @throws LZ4Exception if dest is too small 94 | * @return the compressed size 95 | */ 96 | public final int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff) { 97 | return compress(src, srcOff, srcLen, dest, destOff, dest.length - destOff); 98 | } 99 | 100 | /** 101 | * Convenience method, equivalent to calling 102 | * {@link #compress(byte[], int, int, byte[], int) compress(src, 0, src.length, dest, 0)}. 103 | * 104 | * @param src the source data 105 | * @param dest the destination buffer 106 | * @throws LZ4Exception if dest is too small 107 | * @return the compressed size 108 | */ 109 | public final int compress(byte[] src, byte[] dest) { 110 | return compress(src, 0, src.length, dest, 0); 111 | } 112 | 113 | /** 114 | * Convenience method which returns src[srcOff:srcOff+srcLen] 115 | * compressed. 116 | *

Warning: this method has an 117 | * important overhead due to the fact that it needs to allocate a buffer to 118 | * compress into, and then needs to resize this buffer to the actual 119 | * compressed length.

120 | *

Here is how this method is implemented:

121 | *
122 |    * final int maxCompressedLength = maxCompressedLength(srcLen);
123 |    * final byte[] compressed = new byte[maxCompressedLength];
124 |    * final int compressedLength = compress(src, srcOff, srcLen, compressed, 0);
125 |    * return Arrays.copyOf(compressed, compressedLength);
126 |    * 
127 | * 128 | * @param src the source data 129 | * @param srcOff the start offset in src 130 | * @param srcLen the number of bytes to compress 131 | * @return the compressed data 132 | */ 133 | public final byte[] compress(byte[] src, int srcOff, int srcLen) { 134 | final int maxCompressedLength = maxCompressedLength(srcLen); 135 | final byte[] compressed = new byte[maxCompressedLength]; 136 | final int compressedLength = compress(src, srcOff, srcLen, compressed, 0); 137 | return Arrays.copyOf(compressed, compressedLength); 138 | } 139 | 140 | /** 141 | * Convenience method, equivalent to calling 142 | * {@link #compress(byte[], int, int) compress(src, 0, src.length)}. 143 | * 144 | * @param src the source data 145 | * @return the compressed data 146 | */ 147 | public final byte[] compress(byte[] src) { 148 | return compress(src, 0, src.length); 149 | } 150 | 151 | /** 152 | * Compresses src into dest. Calling this method 153 | * will update the positions of both {@link ByteBuffer}s. 154 | * 155 | * @param src the source data 156 | * @param dest the destination buffer 157 | * @throws LZ4Exception if dest is too small 158 | */ 159 | public final void compress(ByteBuffer src, ByteBuffer dest) { 160 | final int cpLen = compress(src, src.position(), src.remaining(), dest, dest.position(), dest.remaining()); 161 | src.position(src.limit()); 162 | dest.position(dest.position() + cpLen); 163 | } 164 | 165 | @Override 166 | public String toString() { 167 | return getClass().getSimpleName(); 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4Constants.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | enum LZ4Constants { 21 | ; 22 | 23 | static final int DEFAULT_COMPRESSION_LEVEL = 8+1; 24 | static final int MAX_COMPRESSION_LEVEL = 16+1; 25 | 26 | static final int MEMORY_USAGE = 14; 27 | static final int NOT_COMPRESSIBLE_DETECTION_LEVEL = 6; 28 | 29 | static final int MIN_MATCH = 4; 30 | 31 | static final int HASH_LOG = MEMORY_USAGE - 2; 32 | static final int HASH_TABLE_SIZE = 1 << HASH_LOG; 33 | 34 | static final int SKIP_STRENGTH = Math.max(NOT_COMPRESSIBLE_DETECTION_LEVEL, 2); 35 | static final int COPY_LENGTH = 8; 36 | static final int LAST_LITERALS = 5; 37 | static final int MF_LIMIT = COPY_LENGTH + MIN_MATCH; 38 | static final int MIN_LENGTH = MF_LIMIT + 1; 39 | 40 | static final int MAX_DISTANCE = 1 << 16; 41 | 42 | static final int ML_BITS = 4; 43 | static final int ML_MASK = (1 << ML_BITS) - 1; 44 | static final int RUN_BITS = 8 - ML_BITS; 45 | static final int RUN_MASK = (1 << RUN_BITS) - 1; 46 | 47 | static final int LZ4_64K_LIMIT = (1 << 16) + (MF_LIMIT - 1); 48 | static final int HASH_LOG_64K = HASH_LOG + 1; 49 | static final int HASH_TABLE_SIZE_64K = 1 << HASH_LOG_64K; 50 | 51 | static final int HASH_LOG_HC = 15; 52 | static final int HASH_TABLE_SIZE_HC = 1 << HASH_LOG_HC; 53 | static final int OPTIMAL_ML = ML_MASK - 1 + MIN_MATCH; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4Decompressor.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | /** 20 | * @deprecated Use {@link LZ4FastDecompressor} instead. 21 | */ 22 | @Deprecated 23 | public interface LZ4Decompressor { 24 | 25 | int decompress(byte[] src, int srcOff, byte[] dest, int destOff, int destLen); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4Exception.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | /** 20 | * LZ4 compression or decompression error. 21 | */ 22 | public class LZ4Exception extends RuntimeException { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | public LZ4Exception(String msg, Throwable t) { 27 | super(msg, t); 28 | } 29 | 30 | public LZ4Exception(String msg) { 31 | super(msg); 32 | } 33 | 34 | public LZ4Exception() { 35 | super(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4FastDecompressor.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /* 6 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /** 22 | * LZ4 decompressor that requires the size of the original input to be known. 23 | * Use {@link LZ4SafeDecompressor} if you only know the size of the 24 | * compressed stream. 25 | *

26 | * From lz4-java 1.6.0, it is deprecated to use a JNI-binding instance 27 | * of this class; i.e., an instasnce returned by 28 | * {@link LZ4Factory#fastDecompressor()} of {@link LZ4Factory#nativeInstance()}. 29 | * Please see {@link LZ4Factory#nativeInstance()} for details. 30 | *

31 | * Instances of this class are thread-safe. 32 | * 33 | * @see LZ4Factory#nativeInstance() 34 | */ 35 | public abstract class LZ4FastDecompressor implements LZ4Decompressor { 36 | 37 | /** Decompresses src[srcOff:] into dest[destOff:destOff+destLen] 38 | * and returns the number of bytes read from src. 39 | * destLen must be exactly the size of the decompressed data. 40 | * 41 | * @param src the compressed data 42 | * @param srcOff the start offset in src 43 | * @param dest the destination buffer to store the decompressed data 44 | * @param destOff the start offset in dest 45 | * @param destLen the exact size of the original input 46 | * @return the number of bytes read to restore the original input 47 | */ 48 | public abstract int decompress(byte[] src, int srcOff, byte[] dest, int destOff, int destLen); 49 | 50 | /** Decompresses src[srcOff:] into dest[destOff:destOff+destLen] 51 | * and returns the number of bytes read from src. 52 | * destLen must be exactly the size of the decompressed data. 53 | * The positions and limits of the {@link ByteBuffer}s remain unchanged. 54 | * 55 | * @param src the compressed data 56 | * @param srcOff the start offset in src 57 | * @param dest the destination buffer to store the decompressed data 58 | * @param destOff the start offset in dest 59 | * @param destLen the exact size of the original input 60 | * @return the number of bytes read to restore the original input 61 | */ 62 | public abstract int decompress(ByteBuffer src, int srcOff, ByteBuffer dest, int destOff, int destLen); 63 | 64 | /** 65 | * Convenience method, equivalent to calling 66 | * {@link #decompress(byte[], int, byte[], int, int) decompress(src, 0, dest, 0, destLen)}. 67 | * 68 | * @param src the compressed data 69 | * @param dest the destination buffer to store the decompressed data 70 | * @param destLen the exact size of the original input 71 | * @return the number of bytes read to restore the original input 72 | */ 73 | public final int decompress(byte[] src, byte[] dest, int destLen) { 74 | return decompress(src, 0, dest, 0, destLen); 75 | } 76 | 77 | /** 78 | * Convenience method, equivalent to calling 79 | * {@link #decompress(byte[], byte[], int) decompress(src, dest, dest.length)}. 80 | * 81 | * @param src the compressed data 82 | * @param dest the destination buffer to store the decompressed data 83 | * @return the number of bytes read to restore the original input 84 | */ 85 | public final int decompress(byte[] src, byte[] dest) { 86 | return decompress(src, dest, dest.length); 87 | } 88 | 89 | /** 90 | * Convenience method which returns src[srcOff:?] 91 | * decompressed. 92 | *

Warning: this method has an 93 | * important overhead due to the fact that it needs to allocate a buffer to 94 | * decompress into.

95 | *

Here is how this method is implemented:

96 | *
 97 |    * final byte[] decompressed = new byte[destLen];
 98 |    * decompress(src, srcOff, decompressed, 0, destLen);
 99 |    * return decompressed;
100 |    * 
101 | * 102 | * @param src the compressed data 103 | * @param srcOff the start offset in src 104 | * @param destLen the exact size of the original input 105 | * @return the decompressed data 106 | */ 107 | public final byte[] decompress(byte[] src, int srcOff, int destLen) { 108 | final byte[] decompressed = new byte[destLen]; 109 | decompress(src, srcOff, decompressed, 0, destLen); 110 | return decompressed; 111 | } 112 | 113 | /** 114 | * Convenience method, equivalent to calling 115 | * {@link #decompress(byte[], int, int) decompress(src, 0, destLen)}. 116 | * 117 | * @param src the compressed data 118 | * @param destLen the exact size of the original input 119 | * @return the decompressed data 120 | */ 121 | public final byte[] decompress(byte[] src, int destLen) { 122 | return decompress(src, 0, destLen); 123 | } 124 | 125 | /** 126 | * Decompresses src into dest. dest's 127 | * {@link ByteBuffer#remaining()} must be exactly the size of the decompressed 128 | * data. This method moves the positions of the buffers. 129 | * 130 | * @param src the compressed data 131 | * @param dest the destination buffer to store the decompressed data 132 | */ 133 | public final void decompress(ByteBuffer src, ByteBuffer dest) { 134 | final int read = decompress(src, src.position(), dest, dest.position(), dest.remaining()); 135 | dest.position(dest.limit()); 136 | src.position(src.position() + read); 137 | } 138 | 139 | @Override 140 | public String toString() { 141 | return getClass().getSimpleName(); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4HCJNICompressor.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.lz4.LZ4Constants.DEFAULT_COMPRESSION_LEVEL; 20 | 21 | import java.nio.ByteBuffer; 22 | 23 | import net.jpountz.util.ByteBufferUtils; 24 | import net.jpountz.util.SafeUtils; 25 | 26 | /** 27 | * High compression {@link LZ4Compressor}s implemented with JNI bindings to the 28 | * original C implementation of LZ4. 29 | */ 30 | final class LZ4HCJNICompressor extends LZ4Compressor { 31 | 32 | public static final LZ4HCJNICompressor INSTANCE = new LZ4HCJNICompressor(); 33 | private static LZ4Compressor SAFE_INSTANCE; 34 | 35 | private final int compressionLevel; 36 | 37 | LZ4HCJNICompressor() { this(DEFAULT_COMPRESSION_LEVEL); } 38 | LZ4HCJNICompressor(int compressionLevel) { 39 | this.compressionLevel = compressionLevel; 40 | } 41 | 42 | @Override 43 | public int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen) { 44 | SafeUtils.checkRange(src, srcOff, srcLen); 45 | SafeUtils.checkRange(dest, destOff, maxDestLen); 46 | final int result = LZ4JNI.LZ4_compressHC(src, null, srcOff, srcLen, dest, null, destOff, maxDestLen, compressionLevel); 47 | if (result <= 0) { 48 | throw new LZ4Exception(); 49 | } 50 | return result; 51 | } 52 | 53 | @Override 54 | public int compress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen) { 55 | ByteBufferUtils.checkNotReadOnly(dest); 56 | ByteBufferUtils.checkRange(src, srcOff, srcLen); 57 | ByteBufferUtils.checkRange(dest, destOff, maxDestLen); 58 | 59 | if ((src.hasArray() || src.isDirect()) && (dest.hasArray() || dest.isDirect())) { 60 | byte[] srcArr = null, destArr = null; 61 | ByteBuffer srcBuf = null, destBuf = null; 62 | if (src.hasArray()) { 63 | srcArr = src.array(); 64 | srcOff += src.arrayOffset(); 65 | } else { 66 | assert src.isDirect(); 67 | srcBuf = src; 68 | } 69 | if (dest.hasArray()) { 70 | destArr = dest.array(); 71 | destOff += dest.arrayOffset(); 72 | } else { 73 | assert dest.isDirect(); 74 | destBuf = dest; 75 | } 76 | 77 | final int result = LZ4JNI.LZ4_compressHC(srcArr, srcBuf, srcOff, srcLen, destArr, destBuf, destOff, maxDestLen, compressionLevel); 78 | if (result <= 0) { 79 | throw new LZ4Exception(); 80 | } 81 | return result; 82 | } else { 83 | LZ4Compressor safeInstance = SAFE_INSTANCE; 84 | if (safeInstance == null) { 85 | safeInstance = SAFE_INSTANCE = LZ4Factory.safeInstance().highCompressor(compressionLevel); 86 | } 87 | return safeInstance.compress(src, srcOff, srcLen, dest, destOff, maxDestLen); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4JNI.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | import net.jpountz.util.Native; 22 | 23 | 24 | /** 25 | * JNI bindings to the original C implementation of LZ4. 26 | */ 27 | enum LZ4JNI { 28 | ; 29 | 30 | static { 31 | Native.load(); 32 | init(); 33 | } 34 | 35 | static native void init(); 36 | static native int LZ4_compress_limitedOutput(byte[] srcArray, ByteBuffer srcBuffer, int srcOff, int srcLen, byte[] destArray, ByteBuffer destBuffer, int destOff, int maxDestLen); 37 | static native int LZ4_compressHC(byte[] srcArray, ByteBuffer srcBuffer, int srcOff, int srcLen, byte[] destArray, ByteBuffer destBuffer, int destOff, int maxDestLen, int compressionLevel); 38 | static native int LZ4_decompress_fast(byte[] srcArray, ByteBuffer srcBuffer, int srcOff, byte[] destArray, ByteBuffer destBuffer, int destOff, int destLen); 39 | static native int LZ4_decompress_safe(byte[] srcArray, ByteBuffer srcBuffer, int srcOff, int srcLen, byte[] destArray, ByteBuffer destBuffer, int destOff, int maxDestLen); 40 | static native int LZ4_compressBound(int len); 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4JNICompressor.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.util.ByteBufferUtils.checkNotReadOnly; 20 | import static net.jpountz.util.ByteBufferUtils.checkRange; 21 | import static net.jpountz.util.SafeUtils.checkRange; 22 | 23 | import java.nio.ByteBuffer; 24 | 25 | /** 26 | * Fast {@link LZ4FastCompressor}s implemented with JNI bindings to the original C 27 | * implementation of LZ4. 28 | */ 29 | final class LZ4JNICompressor extends LZ4Compressor { 30 | 31 | public static final LZ4Compressor INSTANCE = new LZ4JNICompressor(); 32 | private static LZ4Compressor SAFE_INSTANCE; 33 | 34 | @Override 35 | public int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen) { 36 | checkRange(src, srcOff, srcLen); 37 | checkRange(dest, destOff, maxDestLen); 38 | final int result = LZ4JNI.LZ4_compress_limitedOutput(src, null, srcOff, srcLen, dest, null, destOff, maxDestLen); 39 | if (result <= 0) { 40 | throw new LZ4Exception("maxDestLen is too small"); 41 | } 42 | return result; 43 | } 44 | 45 | @Override 46 | public int compress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen) { 47 | checkNotReadOnly(dest); 48 | checkRange(src, srcOff, srcLen); 49 | checkRange(dest, destOff, maxDestLen); 50 | 51 | if ((src.hasArray() || src.isDirect()) && (dest.hasArray() || dest.isDirect())) { 52 | byte[] srcArr = null, destArr = null; 53 | ByteBuffer srcBuf = null, destBuf = null; 54 | if (src.hasArray()) { 55 | srcArr = src.array(); 56 | srcOff += src.arrayOffset(); 57 | } else { 58 | assert src.isDirect(); 59 | srcBuf = src; 60 | } 61 | if (dest.hasArray()) { 62 | destArr = dest.array(); 63 | destOff += dest.arrayOffset(); 64 | } else { 65 | assert dest.isDirect(); 66 | destBuf = dest; 67 | } 68 | 69 | final int result = LZ4JNI.LZ4_compress_limitedOutput(srcArr, srcBuf, srcOff, srcLen, destArr, destBuf, destOff, maxDestLen); 70 | if (result <= 0) { 71 | throw new LZ4Exception("maxDestLen is too small"); 72 | } 73 | return result; 74 | } else { 75 | LZ4Compressor safeInstance = SAFE_INSTANCE; 76 | if (safeInstance == null) { 77 | safeInstance = SAFE_INSTANCE = LZ4Factory.safeInstance().fastCompressor(); 78 | } 79 | return safeInstance.compress(src, srcOff, srcLen, dest, destOff, maxDestLen); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4JNIFastDecompressor.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | import java.nio.ByteBuffer; 21 | 22 | import net.jpountz.util.ByteBufferUtils; 23 | import net.jpountz.util.SafeUtils; 24 | 25 | 26 | /** 27 | * {@link LZ4FastDecompressor} implemented with JNI bindings to the original C 28 | * implementation of LZ4. 29 | */ 30 | final class LZ4JNIFastDecompressor extends LZ4FastDecompressor { 31 | 32 | public static final LZ4JNIFastDecompressor INSTANCE = new LZ4JNIFastDecompressor(); 33 | private static LZ4FastDecompressor SAFE_INSTANCE; 34 | 35 | @Override 36 | public final int decompress(byte[] src, int srcOff, byte[] dest, int destOff, int destLen) { 37 | SafeUtils.checkRange(src, srcOff); 38 | SafeUtils.checkRange(dest, destOff, destLen); 39 | final int result = LZ4JNI.LZ4_decompress_fast(src, null, srcOff, dest, null, destOff, destLen); 40 | if (result < 0) { 41 | throw new LZ4Exception("Error decoding offset " + (srcOff - result) + " of input buffer"); 42 | } 43 | return result; 44 | } 45 | 46 | @Override 47 | public int decompress(ByteBuffer src, int srcOff, ByteBuffer dest, int destOff, int destLen) { 48 | ByteBufferUtils.checkNotReadOnly(dest); 49 | ByteBufferUtils.checkRange(src, srcOff); 50 | ByteBufferUtils.checkRange(dest, destOff, destLen); 51 | 52 | if ((src.hasArray() || src.isDirect()) && (dest.hasArray() || dest.isDirect())) { 53 | byte[] srcArr = null, destArr = null; 54 | ByteBuffer srcBuf = null, destBuf = null; 55 | if (src.hasArray()) { 56 | srcArr = src.array(); 57 | srcOff += src.arrayOffset(); 58 | } else { 59 | assert src.isDirect(); 60 | srcBuf = src; 61 | } 62 | if (dest.hasArray()) { 63 | destArr = dest.array(); 64 | destOff += dest.arrayOffset(); 65 | } else { 66 | assert dest.isDirect(); 67 | destBuf = dest; 68 | } 69 | 70 | final int result = LZ4JNI.LZ4_decompress_fast(srcArr, srcBuf, srcOff, destArr, destBuf, destOff, destLen); 71 | if (result < 0) { 72 | throw new LZ4Exception("Error decoding offset " + (srcOff - result) + " of input buffer"); 73 | } 74 | return result; 75 | } else { 76 | LZ4FastDecompressor safeInstance = SAFE_INSTANCE; 77 | if (safeInstance == null) { 78 | safeInstance = SAFE_INSTANCE = LZ4Factory.safeInstance().fastDecompressor(); 79 | } 80 | return safeInstance.decompress(src, srcOff, dest, destOff, destLen); 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4JNISafeDecompressor.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | import java.nio.ByteBuffer; 21 | 22 | import net.jpountz.util.ByteBufferUtils; 23 | import net.jpountz.util.SafeUtils; 24 | 25 | /** 26 | * {@link LZ4SafeDecompressor} implemented with JNI bindings to the original C 27 | * implementation of LZ4. 28 | */ 29 | final class LZ4JNISafeDecompressor extends LZ4SafeDecompressor { 30 | 31 | public static final LZ4JNISafeDecompressor INSTANCE = new LZ4JNISafeDecompressor(); 32 | private static LZ4SafeDecompressor SAFE_INSTANCE; 33 | 34 | @Override 35 | public final int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen) { 36 | SafeUtils.checkRange(src, srcOff, srcLen); 37 | SafeUtils.checkRange(dest, destOff, maxDestLen); 38 | final int result = LZ4JNI.LZ4_decompress_safe(src, null, srcOff, srcLen, dest, null, destOff, maxDestLen); 39 | if (result < 0) { 40 | throw new LZ4Exception("Error decoding offset " + (srcOff - result) + " of input buffer"); 41 | } 42 | return result; 43 | } 44 | 45 | @Override 46 | public int decompress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen) { 47 | ByteBufferUtils.checkNotReadOnly(dest); 48 | ByteBufferUtils.checkRange(src, srcOff, srcLen); 49 | ByteBufferUtils.checkRange(dest, destOff, maxDestLen); 50 | 51 | if ((src.hasArray() || src.isDirect()) && (dest.hasArray() || dest.isDirect())) { 52 | byte[] srcArr = null, destArr = null; 53 | ByteBuffer srcBuf = null, destBuf = null; 54 | if (src.hasArray()) { 55 | srcArr = src.array(); 56 | srcOff += src.arrayOffset(); 57 | } else { 58 | assert src.isDirect(); 59 | srcBuf = src; 60 | } 61 | if (dest.hasArray()) { 62 | destArr = dest.array(); 63 | destOff += dest.arrayOffset(); 64 | } else { 65 | assert dest.isDirect(); 66 | destBuf = dest; 67 | } 68 | 69 | final int result = LZ4JNI.LZ4_decompress_safe(srcArr, srcBuf, srcOff, srcLen, destArr, destBuf, destOff, maxDestLen); 70 | if (result < 0) { 71 | throw new LZ4Exception("Error decoding offset " + (srcOff - result) + " of input buffer"); 72 | } 73 | return result; 74 | } else { 75 | LZ4SafeDecompressor safeInstance = SAFE_INSTANCE; 76 | if (safeInstance == null) { 77 | safeInstance = SAFE_INSTANCE = LZ4Factory.safeInstance().safeDecompressor(); 78 | } 79 | return safeInstance.decompress(src, srcOff, srcLen, dest, destOff, maxDestLen); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4SafeDecompressor.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteBuffer; 20 | import java.util.Arrays; 21 | 22 | /** 23 | * LZ4 decompressor that requires the size of the compressed data to be known. 24 | *

25 | * Implementations of this class are usually a little slower than those of 26 | * {@link LZ4FastDecompressor} but do not require the size of the original data to 27 | * be known. 28 | */ 29 | public abstract class LZ4SafeDecompressor implements LZ4UnknownSizeDecompressor { 30 | 31 | /** 32 | * Decompresses src[srcOff:srcOff+srcLen] into 33 | * dest[destOff:destOff+maxDestLen] and returns the number of 34 | * decompressed bytes written into dest. 35 | * 36 | * @param src the compressed data 37 | * @param srcOff the start offset in src 38 | * @param srcLen the exact size of the compressed data 39 | * @param dest the destination buffer to store the decompressed data 40 | * @param destOff the start offset in dest 41 | * @param maxDestLen the maximum number of bytes to write in dest 42 | * @return the original input size 43 | * @throws LZ4Exception if maxDestLen is too small 44 | */ 45 | public abstract int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen); 46 | 47 | /** 48 | * Decompresses src[srcOff:srcOff+srcLen] into 49 | * dest[destOff:destOff+maxDestLen] and returns the number of 50 | * decompressed bytes written into dest. 51 | * The positions and limits of the {@link ByteBuffer}s remain unchanged. 52 | * 53 | * @param src the compressed data 54 | * @param srcOff the start offset in src 55 | * @param srcLen the exact size of the compressed data 56 | * @param dest the destination buffer to store the decompressed data 57 | * @param destOff the start offset in dest 58 | * @param maxDestLen the maximum number of bytes to write in dest 59 | * @return the original input size 60 | * @throws LZ4Exception if maxDestLen is too small 61 | */ 62 | public abstract int decompress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen); 63 | 64 | /** 65 | * Convenience method, equivalent to calling 66 | * {@link #decompress(byte[], int, int, byte[], int, int) decompress(src, srcOff, srcLen, dest, destOff, dest.length - destOff)}. 67 | * 68 | * @param src the compressed data 69 | * @param srcOff the start offset in src 70 | * @param srcLen the exact size of the compressed data 71 | * @param dest the destination buffer to store the decompressed data 72 | * @param destOff the start offset in dest 73 | * @return the original input size 74 | * @throws LZ4Exception if dest is too small 75 | */ 76 | public final int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff) { 77 | return decompress(src, srcOff, srcLen, dest, destOff, dest.length - destOff); 78 | } 79 | 80 | /** 81 | * Convenience method, equivalent to calling 82 | * {@link #decompress(byte[], int, int, byte[], int) decompress(src, 0, src.length, dest, 0)} 83 | * 84 | * @param src the compressed data 85 | * @param dest the destination buffer to store the decompressed data 86 | * @return the original input size 87 | * @throws LZ4Exception if dest is too small 88 | */ 89 | public final int decompress(byte[] src, byte[] dest) { 90 | return decompress(src, 0, src.length, dest, 0); 91 | } 92 | 93 | /** 94 | * Convenience method which returns src[srcOff:srcOff+srcLen] 95 | * decompressed. 96 | *

Warning: this method has an 97 | * important overhead due to the fact that it needs to allocate a buffer to 98 | * decompress into, and then needs to resize this buffer to the actual 99 | * decompressed length.

100 | *

Here is how this method is implemented:

101 | *
102 |    * byte[] decompressed = new byte[maxDestLen];
103 |    * final int decompressedLength = decompress(src, srcOff, srcLen, decompressed, 0, maxDestLen);
104 |    * if (decompressedLength != decompressed.length) {
105 |    *   decompressed = Arrays.copyOf(decompressed, decompressedLength);
106 |    * }
107 |    * return decompressed;
108 |    * 
109 | * 110 | * @param src the compressed data 111 | * @param srcOff the start offset in src 112 | * @param srcLen the exact size of the compressed data 113 | * @param maxDestLen the maximum number of bytes to write in dest 114 | * @return the decompressed data 115 | * @throws LZ4Exception if maxDestLen is too small 116 | */ 117 | public final byte[] decompress(byte[] src, int srcOff, int srcLen, int maxDestLen) { 118 | byte[] decompressed = new byte[maxDestLen]; 119 | final int decompressedLength = decompress(src, srcOff, srcLen, decompressed, 0, maxDestLen); 120 | if (decompressedLength != decompressed.length) { 121 | decompressed = Arrays.copyOf(decompressed, decompressedLength); 122 | } 123 | return decompressed; 124 | } 125 | 126 | /** 127 | * Convenience method, equivalent to calling 128 | * {@link #decompress(byte[], int, int, int) decompress(src, 0, src.length, maxDestLen)}. 129 | * 130 | * @param src the compressed data 131 | * @param maxDestLen the maximum number of bytes to write in dest 132 | * @return the decompressed data 133 | * @throws LZ4Exception if maxDestLen is too small 134 | */ 135 | public final byte[] decompress(byte[] src, int maxDestLen) { 136 | return decompress(src, 0, src.length, maxDestLen); 137 | } 138 | 139 | /** 140 | * Decompresses src into dest. src's 141 | * {@link ByteBuffer#remaining()} must be exactly the size of the compressed 142 | * data. This method moves the positions of the buffers. 143 | * @param src the compressed data 144 | * @param dest the destination buffer to store the decompressed data 145 | * @throws LZ4Exception if dest is too small 146 | */ 147 | public final void decompress(ByteBuffer src, ByteBuffer dest) { 148 | final int decompressed = decompress(src, src.position(), src.remaining(), dest, dest.position(), dest.remaining()); 149 | src.position(src.limit()); 150 | dest.position(dest.position() + decompressed); 151 | } 152 | 153 | @Override 154 | public String toString() { 155 | return getClass().getSimpleName(); 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4SafeUtils.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.lz4.LZ4Constants.LAST_LITERALS; 20 | import static net.jpountz.lz4.LZ4Constants.ML_BITS; 21 | import static net.jpountz.lz4.LZ4Constants.ML_MASK; 22 | import static net.jpountz.lz4.LZ4Constants.RUN_MASK; 23 | import net.jpountz.util.SafeUtils; 24 | 25 | enum LZ4SafeUtils { 26 | ; 27 | 28 | static int hash(byte[] buf, int i) { 29 | return LZ4Utils.hash(SafeUtils.readInt(buf, i)); 30 | } 31 | 32 | static int hash64k(byte[] buf, int i) { 33 | return LZ4Utils.hash64k(SafeUtils.readInt(buf, i)); 34 | } 35 | 36 | static boolean readIntEquals(byte[] buf, int i, int j) { 37 | return buf[i] == buf[j] && buf[i+1] == buf[j+1] && buf[i+2] == buf[j+2] && buf[i+3] == buf[j+3]; 38 | } 39 | 40 | static void safeIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchLen) { 41 | for (int i = 0; i < matchLen; ++i) { 42 | dest[dOff + i] = dest[matchOff + i]; 43 | } 44 | } 45 | 46 | static void wildIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchCopyEnd) { 47 | do { 48 | copy8Bytes(dest, matchOff, dest, dOff); 49 | matchOff += 8; 50 | dOff += 8; 51 | } while (dOff < matchCopyEnd); 52 | } 53 | 54 | static void copy8Bytes(byte[] src, int sOff, byte[] dest, int dOff) { 55 | for (int i = 0; i < 8; ++i) { 56 | dest[dOff + i] = src[sOff + i]; 57 | } 58 | } 59 | 60 | static int commonBytes(byte[] b, int o1, int o2, int limit) { 61 | int count = 0; 62 | while (o2 < limit && b[o1++] == b[o2++]) { 63 | ++count; 64 | } 65 | return count; 66 | } 67 | 68 | static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) { 69 | int count = 0; 70 | while (o1 > l1 && o2 > l2 && b[--o1] == b[--o2]) { 71 | ++count; 72 | } 73 | return count; 74 | } 75 | 76 | static void safeArraycopy(byte[] src, int sOff, byte[] dest, int dOff, int len) { 77 | System.arraycopy(src, sOff, dest, dOff, len); 78 | } 79 | 80 | static void wildArraycopy(byte[] src, int sOff, byte[] dest, int dOff, int len) { 81 | try { 82 | for (int i = 0; i < len; i += 8) { 83 | copy8Bytes(src, sOff + i, dest, dOff + i); 84 | } 85 | } catch (ArrayIndexOutOfBoundsException e) { 86 | throw new LZ4Exception("Malformed input at offset " + sOff); 87 | } 88 | } 89 | 90 | static int encodeSequence(byte[] src, int anchor, int matchOff, int matchRef, int matchLen, byte[] dest, int dOff, int destEnd) { 91 | final int runLen = matchOff - anchor; 92 | final int tokenOff = dOff++; 93 | 94 | if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { 95 | throw new LZ4Exception("maxDestLen is too small"); 96 | } 97 | 98 | int token; 99 | if (runLen >= RUN_MASK) { 100 | token = (byte) (RUN_MASK << ML_BITS); 101 | dOff = writeLen(runLen - RUN_MASK, dest, dOff); 102 | } else { 103 | token = runLen << ML_BITS; 104 | } 105 | 106 | // copy literals 107 | wildArraycopy(src, anchor, dest, dOff, runLen); 108 | dOff += runLen; 109 | 110 | // encode offset 111 | final int matchDec = matchOff - matchRef; 112 | dest[dOff++] = (byte) matchDec; 113 | dest[dOff++] = (byte) (matchDec >>> 8); 114 | 115 | // encode match len 116 | matchLen -= 4; 117 | if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { 118 | throw new LZ4Exception("maxDestLen is too small"); 119 | } 120 | if (matchLen >= ML_MASK) { 121 | token |= ML_MASK; 122 | dOff = writeLen(matchLen - RUN_MASK, dest, dOff); 123 | } else { 124 | token |= matchLen; 125 | } 126 | 127 | dest[tokenOff] = (byte) token; 128 | 129 | return dOff; 130 | } 131 | 132 | static int lastLiterals(byte[] src, int sOff, int srcLen, byte[] dest, int dOff, int destEnd) { 133 | final int runLen = srcLen; 134 | 135 | if (dOff + runLen + 1 + (runLen + 255 - RUN_MASK) / 255 > destEnd) { 136 | throw new LZ4Exception(); 137 | } 138 | 139 | if (runLen >= RUN_MASK) { 140 | dest[dOff++] = (byte) (RUN_MASK << ML_BITS); 141 | dOff = writeLen(runLen - RUN_MASK, dest, dOff); 142 | } else { 143 | dest[dOff++] = (byte) (runLen << ML_BITS); 144 | } 145 | // copy literals 146 | System.arraycopy(src, sOff, dest, dOff, runLen); 147 | dOff += runLen; 148 | 149 | return dOff; 150 | } 151 | 152 | static int writeLen(int len, byte[] dest, int dOff) { 153 | while (len >= 0xFF) { 154 | dest[dOff++] = (byte) 0xFF; 155 | len -= 0xFF; 156 | } 157 | dest[dOff++] = (byte) len; 158 | return dOff; 159 | } 160 | 161 | static class Match { 162 | int start, ref, len; 163 | 164 | void fix(int correction) { 165 | start += correction; 166 | ref += correction; 167 | len -= correction; 168 | } 169 | 170 | int end() { 171 | return start + len; 172 | } 173 | } 174 | 175 | static void copyTo(Match m1, Match m2) { 176 | m2.len = m1.len; 177 | m2.start = m1.start; 178 | m2.ref = m1.ref; 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4UnknownSizeDecompressor.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | /** 20 | * @deprecated Use {@link LZ4SafeDecompressor} instead. 21 | */ 22 | @Deprecated 23 | public interface LZ4UnknownSizeDecompressor { 24 | 25 | int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen); 26 | 27 | int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/LZ4Utils.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.lz4.LZ4Constants.HASH_LOG; 20 | import static net.jpountz.lz4.LZ4Constants.HASH_LOG_64K; 21 | import static net.jpountz.lz4.LZ4Constants.HASH_LOG_HC; 22 | import static net.jpountz.lz4.LZ4Constants.LAST_LITERALS; 23 | import static net.jpountz.lz4.LZ4Constants.MIN_MATCH; 24 | import static net.jpountz.lz4.LZ4Constants.ML_BITS; 25 | import static net.jpountz.lz4.LZ4Constants.ML_MASK; 26 | import static net.jpountz.lz4.LZ4Constants.RUN_MASK; 27 | import net.jpountz.util.SafeUtils; 28 | 29 | enum LZ4Utils { 30 | ; 31 | 32 | private static final int MAX_INPUT_SIZE = 0x7E000000; 33 | 34 | static int maxCompressedLength(int length) { 35 | if (length < 0) { 36 | throw new IllegalArgumentException("length must be >= 0, got " + length); 37 | } else if (length >= MAX_INPUT_SIZE) { 38 | throw new IllegalArgumentException("length must be < " + MAX_INPUT_SIZE); 39 | } 40 | return length + length / 255 + 16; 41 | } 42 | 43 | static int hash(int i) { 44 | return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG); 45 | } 46 | 47 | static int hash64k(int i) { 48 | return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG_64K); 49 | } 50 | 51 | static int hashHC(int i) { 52 | return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG_HC); 53 | } 54 | 55 | static class Match { 56 | int start, ref, len; 57 | 58 | void fix(int correction) { 59 | start += correction; 60 | ref += correction; 61 | len -= correction; 62 | } 63 | 64 | int end() { 65 | return start + len; 66 | } 67 | } 68 | 69 | static void copyTo(Match m1, Match m2) { 70 | m2.len = m1.len; 71 | m2.start = m1.start; 72 | m2.ref = m1.ref; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/java/net/jpountz/lz4/package.html: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 |

LZ4 compression. The entry point of the API is the 23 | {@link net.jpountz.lz4.LZ4Factory} class, which gives access to 24 | {@link net.jpountz.lz4.LZ4Compressor compressors} and 25 | {@link net.jpountz.lz4.LZ4SafeDecompressor decompressors}.

26 | 27 | 28 |

Sample usage:

29 | 30 |
31 |     LZ4Factory factory = LZ4Factory.fastestInstance();
32 | 
33 |     byte[] data = "12345345234572".getBytes("UTF-8");
34 |     final int decompressedLength = data.length;
35 | 
36 |     // compress data
37 |     LZ4Compressor compressor = factory.fastCompressor();
38 |     int maxCompressedLength = compressor.maxCompressedLength(decompressedLength);
39 |     byte[] compressed = new byte[maxCompressedLength];
40 |     int compressedLength = compressor.compress(data, 0, decompressedLength, compressed, 0, maxCompressedLength);
41 | 
42 |     // decompress data
43 |     // - method 1: when the decompressed length is known
44 |     LZ4FastDecompressor decompressor = factory.fastDecompressor();
45 |     byte[] restored = new byte[decompressedLength];
46 |     int compressedLength2 = decompressor.decompress(compressed, 0, restored, 0, decompressedLength);
47 |     // compressedLength == compressedLength2
48 | 
49 |     // - method 2: when the compressed length is known (a little slower)
50 |     // the destination buffer needs to be over-sized
51 |     LZ4SafeDecompressor decompressor2 = factory.safeDecompressor();
52 |     int decompressedLength2 = decompressor2.decompress(compressed, 0, compressedLength, restored, 0);
53 |     // decompressedLength == decompressedLength2
54 | 
55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/java/net/jpountz/util/ByteBufferUtils.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.util; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteBuffer; 20 | import java.nio.ByteOrder; 21 | import java.nio.ReadOnlyBufferException; 22 | 23 | public enum ByteBufferUtils { 24 | ; 25 | 26 | public static void checkRange(ByteBuffer buf, int off, int len) { 27 | SafeUtils.checkLength(len); 28 | if (len > 0) { 29 | checkRange(buf, off); 30 | checkRange(buf, off + len - 1); 31 | } 32 | } 33 | 34 | public static void checkRange(ByteBuffer buf, int off) { 35 | if (off < 0 || off >= buf.capacity()) { 36 | throw new ArrayIndexOutOfBoundsException(off); 37 | } 38 | } 39 | 40 | public static ByteBuffer inLittleEndianOrder(ByteBuffer buf) { 41 | if (buf.order().equals(ByteOrder.LITTLE_ENDIAN)) { 42 | return buf; 43 | } else { 44 | return buf.duplicate().order(ByteOrder.LITTLE_ENDIAN); 45 | } 46 | } 47 | 48 | public static ByteBuffer inNativeByteOrder(ByteBuffer buf) { 49 | if (buf.order().equals(Utils.NATIVE_BYTE_ORDER)) { 50 | return buf; 51 | } else { 52 | return buf.duplicate().order(Utils.NATIVE_BYTE_ORDER); 53 | } 54 | } 55 | 56 | public static byte readByte(ByteBuffer buf, int i) { 57 | return buf.get(i); 58 | } 59 | 60 | public static void writeInt(ByteBuffer buf, int i, int v) { 61 | assert buf.order() == Utils.NATIVE_BYTE_ORDER; 62 | buf.putInt(i, v); 63 | } 64 | 65 | public static int readInt(ByteBuffer buf, int i) { 66 | assert buf.order() == Utils.NATIVE_BYTE_ORDER; 67 | return buf.getInt(i); 68 | } 69 | 70 | public static int readIntLE(ByteBuffer buf, int i) { 71 | assert buf.order() == ByteOrder.LITTLE_ENDIAN; 72 | return buf.getInt(i); 73 | } 74 | 75 | public static void writeLong(ByteBuffer buf, int i, long v) { 76 | assert buf.order() == Utils.NATIVE_BYTE_ORDER; 77 | buf.putLong(i, v); 78 | } 79 | 80 | public static long readLong(ByteBuffer buf, int i) { 81 | assert buf.order() == Utils.NATIVE_BYTE_ORDER; 82 | return buf.getLong(i); 83 | } 84 | 85 | public static long readLongLE(ByteBuffer buf, int i) { 86 | assert buf.order() == ByteOrder.LITTLE_ENDIAN; 87 | return buf.getLong(i); 88 | } 89 | 90 | public static void writeByte(ByteBuffer dest, int off, int i) { 91 | dest.put(off, (byte) i); 92 | } 93 | 94 | public static void writeShortLE(ByteBuffer dest, int off, int i) { 95 | dest.put(off, (byte) i); 96 | dest.put(off + 1, (byte) (i >>> 8)); 97 | } 98 | 99 | public static void checkNotReadOnly(ByteBuffer buffer) { 100 | if (buffer.isReadOnly()) { 101 | throw new ReadOnlyBufferException(); 102 | } 103 | } 104 | 105 | public static int readShortLE(ByteBuffer buf, int i) { 106 | return (buf.get(i) & 0xFF) | ((buf.get(i+1) & 0xFF) << 8); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/java/net/jpountz/util/Native.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.util; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.io.File; 20 | import java.io.FileOutputStream; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.io.FilenameFilter; 24 | 25 | /** FOR INTERNAL USE ONLY */ 26 | public enum Native { 27 | ; 28 | 29 | private enum OS { 30 | // Even on Windows, the default compiler from cpptasks (gcc) uses .so as a shared lib extension 31 | WINDOWS("win32", "so"), LINUX("linux", "so"), MAC("darwin", "dylib"), SOLARIS("solaris", "so"); 32 | public final String name, libExtension; 33 | 34 | private OS(String name, String libExtension) { 35 | this.name = name; 36 | this.libExtension = libExtension; 37 | } 38 | } 39 | 40 | private static String arch() { 41 | return System.getProperty("os.arch"); 42 | } 43 | 44 | private static OS os() { 45 | String osName = System.getProperty("os.name"); 46 | if (osName.contains("Linux")) { 47 | return OS.LINUX; 48 | } else if (osName.contains("Mac")) { 49 | return OS.MAC; 50 | } else if (osName.contains("Windows")) { 51 | return OS.WINDOWS; 52 | } else if (osName.contains("Solaris") || osName.contains("SunOS")) { 53 | return OS.SOLARIS; 54 | } else { 55 | throw new UnsupportedOperationException("Unsupported operating system: " 56 | + osName); 57 | } 58 | } 59 | 60 | private static String resourceName() { 61 | OS os = os(); 62 | String packagePrefix = Native.class.getPackage().getName().replace('.', '/'); 63 | 64 | return "/" + packagePrefix + "/" + os.name + "/" + arch() + "/liblz4-java." + os.libExtension; 65 | } 66 | 67 | private static boolean loaded = false; 68 | 69 | public static synchronized boolean isLoaded() { 70 | return loaded; 71 | } 72 | 73 | private static void cleanupOldTempLibs() { 74 | String tempFolder = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath(); 75 | File dir = new File(tempFolder); 76 | 77 | File[] tempLibFiles = dir.listFiles(new FilenameFilter() { 78 | private final String searchPattern = "liblz4-java-"; 79 | public boolean accept(File dir, String name) { 80 | return name.startsWith(searchPattern) && !name.endsWith(".lck"); 81 | } 82 | }); 83 | if(tempLibFiles != null) { 84 | for(File tempLibFile : tempLibFiles) { 85 | File lckFile = new File(tempLibFile.getAbsolutePath() + ".lck"); 86 | if(!lckFile.exists()) { 87 | try { 88 | tempLibFile.delete(); 89 | } 90 | catch(SecurityException e) { 91 | System.err.println("Failed to delete old temp lib" + e.getMessage()); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | public static synchronized void load() { 99 | if (loaded) { 100 | return; 101 | } 102 | 103 | cleanupOldTempLibs(); 104 | 105 | // Try to load lz4-java (liblz4-java.so on Linux) from the java.library.path. 106 | try { 107 | System.loadLibrary("lz4-java"); 108 | loaded = true; 109 | return; 110 | } catch (UnsatisfiedLinkError ex) { 111 | // Doesn't exist, so proceed to loading bundled library. 112 | } 113 | 114 | String resourceName = resourceName(); 115 | InputStream is = Native.class.getResourceAsStream(resourceName); 116 | if (is == null) { 117 | throw new UnsupportedOperationException("Unsupported OS/arch, cannot find " + resourceName + ". Please try building from source."); 118 | } 119 | File tempLib = null; 120 | File tempLibLock = null; 121 | try { 122 | // Create the .lck file first to avoid a race condition 123 | // with other concurrently running Java processes using lz4-java. 124 | tempLibLock = File.createTempFile("liblz4-java-", "." + os().libExtension + ".lck"); 125 | tempLib = new File(tempLibLock.getAbsolutePath().replaceFirst(".lck$", "")); 126 | // copy to tempLib 127 | try (FileOutputStream out = new FileOutputStream(tempLib)) { 128 | byte[] buf = new byte[4096]; 129 | while (true) { 130 | int read = is.read(buf); 131 | if (read == -1) { 132 | break; 133 | } 134 | out.write(buf, 0, read); 135 | } 136 | } 137 | System.load(tempLib.getAbsolutePath()); 138 | loaded = true; 139 | } catch (IOException e) { 140 | throw new ExceptionInInitializerError("Cannot unpack liblz4-java: " + e); 141 | } finally { 142 | if (!loaded) { 143 | if (tempLib != null && tempLib.exists()) { 144 | if (!tempLib.delete()) { 145 | throw new ExceptionInInitializerError("Cannot unpack liblz4-java / cannot delete a temporary native library " + tempLib); 146 | } 147 | } 148 | if (tempLibLock != null && tempLibLock.exists()) { 149 | if (!tempLibLock.delete()) { 150 | throw new ExceptionInInitializerError("Cannot unpack liblz4-java / cannot delete a temporary lock file " + tempLibLock); 151 | } 152 | } 153 | } else { 154 | final String keepEnv = System.getenv("LZ4JAVA_KEEP_TEMP_JNI_LIB"); 155 | final String keepProp = System.getProperty("lz4java.jnilib.temp.keep"); 156 | if ((keepEnv == null || !keepEnv.equals("true")) && 157 | (keepProp == null || !keepProp.equals("true"))) 158 | tempLib.deleteOnExit(); 159 | tempLibLock.deleteOnExit(); 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/java/net/jpountz/util/SafeUtils.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.util; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteOrder; 20 | 21 | public enum SafeUtils { 22 | ; 23 | 24 | public static void checkRange(byte[] buf, int off) { 25 | if (off < 0 || off >= buf.length) { 26 | throw new ArrayIndexOutOfBoundsException(off); 27 | } 28 | } 29 | 30 | public static void checkRange(byte[] buf, int off, int len) { 31 | checkLength(len); 32 | if (len > 0) { 33 | checkRange(buf, off); 34 | checkRange(buf, off + len - 1); 35 | } 36 | } 37 | 38 | public static void checkLength(int len) { 39 | if (len < 0) { 40 | throw new IllegalArgumentException("lengths must be >= 0"); 41 | } 42 | } 43 | 44 | public static byte readByte(byte[] buf, int i) { 45 | return buf[i]; 46 | } 47 | 48 | public static int readIntBE(byte[] buf, int i) { 49 | return ((buf[i] & 0xFF) << 24) | ((buf[i+1] & 0xFF) << 16) | ((buf[i+2] & 0xFF) << 8) | (buf[i+3] & 0xFF); 50 | } 51 | 52 | public static int readIntLE(byte[] buf, int i) { 53 | return (buf[i] & 0xFF) | ((buf[i+1] & 0xFF) << 8) | ((buf[i+2] & 0xFF) << 16) | ((buf[i+3] & 0xFF) << 24); 54 | } 55 | 56 | public static int readInt(byte[] buf, int i) { 57 | if (Utils.NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { 58 | return readIntBE(buf, i); 59 | } else { 60 | return readIntLE(buf, i); 61 | } 62 | } 63 | 64 | public static long readLongLE(byte[] buf, int i) { 65 | return (buf[i] & 0xFFL) | ((buf[i+1] & 0xFFL) << 8) | ((buf[i+2] & 0xFFL) << 16) | ((buf[i+3] & 0xFFL) << 24) 66 | | ((buf[i+4] & 0xFFL) << 32) | ((buf[i+5] & 0xFFL) << 40) | ((buf[i+6] & 0xFFL) << 48) | ((buf[i+7] & 0xFFL) << 56); 67 | } 68 | 69 | public static void writeShortLE(byte[] buf, int off, int v) { 70 | buf[off++] = (byte) v; 71 | buf[off++] = (byte) (v >>> 8); 72 | } 73 | 74 | public static void writeInt(int[] buf, int off, int v) { 75 | buf[off] = v; 76 | } 77 | 78 | public static int readInt(int[] buf, int off) { 79 | return buf[off]; 80 | } 81 | 82 | public static void writeByte(byte[] dest, int off, int i) { 83 | dest[off] = (byte) i; 84 | } 85 | 86 | public static void writeShort(short[] buf, int off, int v) { 87 | buf[off] = (short) v; 88 | } 89 | 90 | public static int readShortLE(byte[] buf, int i) { 91 | return (buf[i] & 0xFF) | ((buf[i+1] & 0xFF) << 8); 92 | } 93 | 94 | public static int readShort(short[] buf, int off) { 95 | return buf[off] & 0xFFFF; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/java/net/jpountz/util/Utils.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.util; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteOrder; 20 | 21 | public enum Utils { 22 | ; 23 | 24 | public static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder(); 25 | 26 | private static final boolean unalignedAccessAllowed; 27 | static { 28 | String arch = System.getProperty("os.arch"); 29 | unalignedAccessAllowed = arch.equals("i386") || arch.equals("x86") 30 | || arch.equals("amd64") || arch.equals("x86_64") 31 | || arch.equals("aarch64") || arch.equals("ppc64le"); 32 | } 33 | 34 | public static boolean isUnalignedAccessAllowed() { 35 | return unalignedAccessAllowed; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/java/net/jpountz/util/package.html: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 |

Utility classes.

23 | 24 | 25 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/AbstractStreamingXXHash32Java.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.xxhash.XXHashConstants.PRIME1; 20 | import static net.jpountz.xxhash.XXHashConstants.PRIME2; 21 | 22 | abstract class AbstractStreamingXXHash32Java extends StreamingXXHash32 { 23 | 24 | int v1, v2, v3, v4, memSize; 25 | long totalLen; 26 | final byte[] memory; 27 | 28 | AbstractStreamingXXHash32Java(int seed) { 29 | super(seed); 30 | memory = new byte[16]; 31 | reset(); 32 | } 33 | 34 | @Override 35 | public void reset() { 36 | v1 = seed + PRIME1 + PRIME2; 37 | v2 = seed + PRIME2; 38 | v3 = seed + 0; 39 | v4 = seed - PRIME1; 40 | totalLen = 0; 41 | memSize = 0; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/AbstractStreamingXXHash64Java.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Linnaea Von Lavia and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.xxhash.XXHashConstants.PRIME64_1; 20 | import static net.jpountz.xxhash.XXHashConstants.PRIME64_2; 21 | 22 | abstract class AbstractStreamingXXHash64Java extends StreamingXXHash64 { 23 | 24 | int memSize; 25 | long v1, v2, v3, v4; 26 | long totalLen; 27 | final byte[] memory; 28 | 29 | AbstractStreamingXXHash64Java(long seed) { 30 | super(seed); 31 | memory = new byte[32]; 32 | reset(); 33 | } 34 | 35 | @Override 36 | public void reset() { 37 | v1 = seed + PRIME64_1 + PRIME64_2; 38 | v2 = seed + PRIME64_2; 39 | v3 = seed + 0; 40 | v4 = seed - PRIME64_1; 41 | totalLen = 0; 42 | memSize = 0; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/StreamingXXHash32.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | import java.util.zip.Checksum; 4 | import java.io.Closeable; 5 | 6 | /* 7 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | 23 | 24 | /** 25 | * Streaming interface for {@link XXHash32}. 26 | *

27 | * This API is compatible with the {@link XXHash32 block API} and the following 28 | * code samples are equivalent: 29 | *

 30 |  *   int hash(XXHashFactory xxhashFactory, byte[] buf, int off, int len, int seed) {
 31 |  *     return xxhashFactory.hash32().hash(buf, off, len, seed);
 32 |  *   }
 33 |  * 
34 | *
 35 |  *   int hash(XXHashFactory xxhashFactory, byte[] buf, int off, int len, int seed) {
 36 |  *     StreamingXXHash32 sh32 = xxhashFactory.newStreamingHash32(seed);
 37 |  *     sh32.update(buf, off, len);
 38 |  *     return sh32.getValue();
 39 |  *   }
 40 |  * 
41 | *

42 | * Instances of this class are not thread-safe. 43 | */ 44 | public abstract class StreamingXXHash32 implements Closeable { 45 | 46 | interface Factory { 47 | 48 | StreamingXXHash32 newStreamingHash(int seed); 49 | 50 | } 51 | 52 | final int seed; 53 | 54 | StreamingXXHash32(int seed) { 55 | this.seed = seed; 56 | } 57 | 58 | /** 59 | * Returns the value of the checksum. 60 | * 61 | * @return the checksum 62 | */ 63 | public abstract int getValue(); 64 | 65 | /** 66 | * Updates the value of the hash with buf[off:off+len]. 67 | * 68 | * @param buf the input data 69 | * @param off the start offset in buf 70 | * @param len the number of bytes to hash 71 | */ 72 | public abstract void update(byte[] buf, int off, int len); 73 | 74 | /** 75 | * Resets this instance to the state it had right after instantiation. The 76 | * seed remains unchanged. 77 | */ 78 | public abstract void reset(); 79 | 80 | /** 81 | * Releases any system resources associated with this instance. 82 | * It is not mandatory to call this method after using this instance 83 | * because the system resources are released anyway when this instance 84 | * is reclaimed by GC. 85 | */ 86 | @Override 87 | public void close() { 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | return getClass().getSimpleName() + "(seed=" + seed + ")"; 93 | } 94 | 95 | /** 96 | * Returns a {@link Checksum} view of this instance. Modifications to the view 97 | * will modify this instance too and vice-versa. 98 | * 99 | * @return the {@link Checksum} object representing this instance 100 | */ 101 | public final Checksum asChecksum() { 102 | return new Checksum() { 103 | 104 | @Override 105 | public long getValue() { 106 | return StreamingXXHash32.this.getValue() & 0xFFFFFFFL; 107 | } 108 | 109 | @Override 110 | public void reset() { 111 | StreamingXXHash32.this.reset(); 112 | } 113 | 114 | @Override 115 | public void update(int b) { 116 | StreamingXXHash32.this.update(new byte[] {(byte) b}, 0, 1); 117 | } 118 | 119 | @Override 120 | public void update(byte[] b, int off, int len) { 121 | StreamingXXHash32.this.update(b, off, len); 122 | } 123 | 124 | @Override 125 | public String toString() { 126 | return StreamingXXHash32.this.toString(); 127 | } 128 | 129 | }; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/StreamingXXHash32JNI.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | /** 20 | * Fast {@link StreamingXXHash32} implemented with JNI bindings. 21 | * The methods are synchronized to avoid a race condition 22 | * between freeing the native memory in finalize() and using it in 23 | * reset(), getValue(), and update(). Note that GC can call finalize() 24 | * after calling checkState() and before using XXHashJNI if the caller 25 | * does not retain a reference to this object. 26 | */ 27 | final class StreamingXXHash32JNI extends StreamingXXHash32 { 28 | 29 | static class Factory implements StreamingXXHash32.Factory { 30 | 31 | public static final StreamingXXHash32.Factory INSTANCE = new Factory(); 32 | 33 | @Override 34 | public StreamingXXHash32 newStreamingHash(int seed) { 35 | return new StreamingXXHash32JNI(seed); 36 | } 37 | 38 | } 39 | 40 | private long state; 41 | 42 | StreamingXXHash32JNI(int seed) { 43 | super(seed); 44 | state = XXHashJNI.XXH32_init(seed); 45 | } 46 | 47 | private void checkState() { 48 | if (state == 0) { 49 | throw new AssertionError("Already finalized"); 50 | } 51 | } 52 | 53 | @Override 54 | public synchronized void reset() { 55 | checkState(); 56 | XXHashJNI.XXH32_free(state); 57 | state = XXHashJNI.XXH32_init(seed); 58 | } 59 | 60 | @Override 61 | public synchronized int getValue() { 62 | checkState(); 63 | return XXHashJNI.XXH32_digest(state); 64 | } 65 | 66 | @Override 67 | public synchronized void update(byte[] bytes, int off, int len) { 68 | checkState(); 69 | XXHashJNI.XXH32_update(state, bytes, off, len); 70 | } 71 | 72 | @Override 73 | public synchronized void close() { 74 | if (state != 0) { 75 | super.close(); 76 | XXHashJNI.XXH32_free(state); 77 | state = 0; 78 | } 79 | } 80 | 81 | @Override 82 | protected synchronized void finalize() throws Throwable { 83 | super.finalize(); 84 | if (state != 0) { 85 | // free memory 86 | XXHashJNI.XXH32_free(state); 87 | state = 0; 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/StreamingXXHash64.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | import java.util.zip.Checksum; 4 | import java.io.Closeable; 5 | 6 | /* 7 | * Copyright 2020 Linnaea Von Lavia and the lz4-java contributors. 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | 23 | 24 | /** 25 | * Streaming interface for {@link XXHash64}. 26 | *

27 | * This API is compatible with the {@link XXHash64 block API} and the following 28 | * code samples are equivalent: 29 | *

 30 |  *   long hash(XXHashFactory xxhashFactory, byte[] buf, int off, int len, long seed) {
 31 |  *     return xxhashFactory.hash64().hash(buf, off, len, seed);
 32 |  *   }
 33 |  * 
34 | *
 35 |  *   long hash(XXHashFactory xxhashFactory, byte[] buf, int off, int len, long seed) {
 36 |  *     StreamingXXHash64 sh64 = xxhashFactory.newStreamingHash64(seed);
 37 |  *     sh64.update(buf, off, len);
 38 |  *     return sh64.getValue();
 39 |  *   }
 40 |  * 
41 | *

42 | * Instances of this class are not thread-safe. 43 | */ 44 | public abstract class StreamingXXHash64 implements Closeable { 45 | 46 | interface Factory { 47 | 48 | StreamingXXHash64 newStreamingHash(long seed); 49 | 50 | } 51 | 52 | final long seed; 53 | 54 | StreamingXXHash64(long seed) { 55 | this.seed = seed; 56 | } 57 | 58 | /** 59 | * Returns the value of the checksum. 60 | * 61 | * @return the checksum 62 | */ 63 | public abstract long getValue(); 64 | 65 | /** 66 | * Updates the value of the hash with buf[off:off+len]. 67 | * 68 | * @param buf the input data 69 | * @param off the start offset in buf 70 | * @param len the number of bytes to hash 71 | */ 72 | public abstract void update(byte[] buf, int off, int len); 73 | 74 | /** 75 | * Resets this instance to the state it had right after instantiation. The 76 | * seed remains unchanged. 77 | */ 78 | public abstract void reset(); 79 | 80 | /** 81 | * Releases any system resources associated with this instance. 82 | * It is not mandatory to call this method after using this instance 83 | * because the system resources are released anyway when this instance 84 | * is reclaimed by GC. 85 | */ 86 | @Override 87 | public void close() { 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | return getClass().getSimpleName() + "(seed=" + seed + ")"; 93 | } 94 | 95 | /** 96 | * Returns a {@link Checksum} view of this instance. Modifications to the view 97 | * will modify this instance too and vice-versa. 98 | * 99 | * @return the {@link Checksum} object representing this instance 100 | */ 101 | public final Checksum asChecksum() { 102 | return new Checksum() { 103 | 104 | @Override 105 | public long getValue() { 106 | return StreamingXXHash64.this.getValue(); 107 | } 108 | 109 | @Override 110 | public void reset() { 111 | StreamingXXHash64.this.reset(); 112 | } 113 | 114 | @Override 115 | public void update(int b) { 116 | StreamingXXHash64.this.update(new byte[] {(byte) b}, 0, 1); 117 | } 118 | 119 | @Override 120 | public void update(byte[] b, int off, int len) { 121 | StreamingXXHash64.this.update(b, off, len); 122 | } 123 | 124 | @Override 125 | public String toString() { 126 | return StreamingXXHash64.this.toString(); 127 | } 128 | 129 | }; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/StreamingXXHash64JNI.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Linnaea Von Lavia and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | /** 21 | * Fast {@link StreamingXXHash64} implemented with JNI bindings. 22 | * The methods are synchronized to avoid a race condition 23 | * between freeing the native memory in finalize() and using it in 24 | * reset(), getValue(), and update(). Note that GC can call finalize() 25 | * after calling checkState() and before using XXHashJNI if the caller 26 | * does not retain a reference to this object. 27 | */ 28 | final class StreamingXXHash64JNI extends StreamingXXHash64 { 29 | 30 | static class Factory implements StreamingXXHash64.Factory { 31 | 32 | public static final StreamingXXHash64.Factory INSTANCE = new Factory(); 33 | 34 | @Override 35 | public StreamingXXHash64 newStreamingHash(long seed) { 36 | return new StreamingXXHash64JNI(seed); 37 | } 38 | 39 | } 40 | 41 | private long state; 42 | 43 | StreamingXXHash64JNI(long seed) { 44 | super(seed); 45 | state = XXHashJNI.XXH64_init(seed); 46 | } 47 | 48 | private void checkState() { 49 | if (state == 0) { 50 | throw new AssertionError("Already finalized"); 51 | } 52 | } 53 | 54 | @Override 55 | public synchronized void reset() { 56 | checkState(); 57 | XXHashJNI.XXH64_free(state); 58 | state = XXHashJNI.XXH64_init(seed); 59 | } 60 | 61 | @Override 62 | public synchronized long getValue() { 63 | checkState(); 64 | return XXHashJNI.XXH64_digest(state); 65 | } 66 | 67 | @Override 68 | public synchronized void update(byte[] bytes, int off, int len) { 69 | checkState(); 70 | XXHashJNI.XXH64_update(state, bytes, off, len); 71 | } 72 | 73 | @Override 74 | public synchronized void close() { 75 | if (state != 0) { 76 | super.close(); 77 | XXHashJNI.XXH64_free(state); 78 | state = 0; 79 | } 80 | } 81 | 82 | 83 | @Override 84 | protected synchronized void finalize() throws Throwable { 85 | super.finalize(); 86 | if (state != 0) { 87 | // free memory 88 | XXHashJNI.XXH64_free(state); 89 | state = 0; 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/XXHash32.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /* 6 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /** 22 | * A 32-bits hash. 23 | *

24 | * Instances of this class are thread-safe. 25 | */ 26 | public abstract class XXHash32 { 27 | 28 | /** 29 | * Computes the 32-bits hash of buf[off:off+len] using seed 30 | * seed. 31 | * 32 | * @param buf the input data 33 | * @param off the start offset in buf 34 | * @param len the number of bytes to hash 35 | * @param seed the seed to use 36 | * @return the hash value 37 | */ 38 | public abstract int hash(byte[] buf, int off, int len, int seed); 39 | 40 | /** 41 | * Computes the hash of the given slice of the {@link ByteBuffer}. 42 | * {@link ByteBuffer#position() position} and {@link ByteBuffer#limit() limit} 43 | * are not modified. 44 | * 45 | * @param buf the input data 46 | * @param off the start offset in buf 47 | * @param len the number of bytes to hash 48 | * @param seed the seed to use 49 | * @return the hash value 50 | */ 51 | public abstract int hash(ByteBuffer buf, int off, int len, int seed); 52 | 53 | /** 54 | * Computes the hash of the given {@link ByteBuffer}. The 55 | * {@link ByteBuffer#position() position} is moved in order to reflect bytes 56 | * which have been read. 57 | * 58 | * @param buf the input data 59 | * @param seed the seed to use 60 | * @return the hash value 61 | */ 62 | public final int hash(ByteBuffer buf, int seed) { 63 | final int hash = hash(buf, buf.position(), buf.remaining(), seed); 64 | buf.position(buf.limit()); 65 | return hash; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return getClass().getSimpleName(); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/XXHash32JNI.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.util.ByteBufferUtils.checkRange; 20 | import static net.jpountz.util.SafeUtils.checkRange; 21 | 22 | import java.nio.ByteBuffer; 23 | 24 | final class XXHash32JNI extends XXHash32 { 25 | 26 | public static final XXHash32 INSTANCE = new XXHash32JNI(); 27 | private static XXHash32 SAFE_INSTANCE; 28 | 29 | @Override 30 | public int hash(byte[] buf, int off, int len, int seed) { 31 | checkRange(buf, off, len); 32 | return XXHashJNI.XXH32(buf, off, len, seed); 33 | } 34 | 35 | @Override 36 | public int hash(ByteBuffer buf, int off, int len, int seed) { 37 | if (buf.isDirect()) { 38 | checkRange(buf, off, len); 39 | return XXHashJNI.XXH32BB(buf, off, len, seed); 40 | } else if (buf.hasArray()) { 41 | return hash(buf.array(), off + buf.arrayOffset(), len, seed); 42 | } else { 43 | XXHash32 safeInstance = SAFE_INSTANCE; 44 | if (safeInstance == null) { 45 | safeInstance = SAFE_INSTANCE = XXHashFactory.safeInstance().hash32(); 46 | } 47 | return safeInstance.hash(buf, off, len, seed); 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/XXHash64.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /* 6 | * Copyright 2020 Linnaea Von Lavia and the lz4-java contributors. 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /** 22 | * A 64-bits hash. 23 | *

24 | * Instances of this class are thread-safe. 25 | */ 26 | public abstract class XXHash64 { 27 | 28 | /** 29 | * Computes the 64-bits hash of buf[off:off+len] using seed 30 | * seed. 31 | * 32 | * @param buf the input data 33 | * @param off the start offset in buf 34 | * @param len the number of bytes to hash 35 | * @param seed the seed to use 36 | * @return the hash value 37 | */ 38 | public abstract long hash(byte[] buf, int off, int len, long seed); 39 | 40 | /** 41 | * Computes the hash of the given slice of the {@link ByteBuffer}. 42 | * {@link ByteBuffer#position() position} and {@link ByteBuffer#limit() limit} 43 | * are not modified. 44 | * 45 | * @param buf the input data 46 | * @param off the start offset in buf 47 | * @param len the number of bytes to hash 48 | * @param seed the seed to use 49 | * @return the hash value 50 | */ 51 | public abstract long hash(ByteBuffer buf, int off, int len, long seed); 52 | 53 | /** 54 | * Computes the hash of the given {@link ByteBuffer}. The 55 | * {@link ByteBuffer#position() position} is moved in order to reflect bytes 56 | * which have been read. 57 | * 58 | * @param buf the input data 59 | * @param seed the seed to use 60 | * @return the hash value 61 | */ 62 | public final long hash(ByteBuffer buf, long seed) { 63 | final long hash = hash(buf, buf.position(), buf.remaining(), seed); 64 | buf.position(buf.limit()); 65 | return hash; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return getClass().getSimpleName(); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/XXHash64JNI.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Linnaea Von Lavia and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import static net.jpountz.util.ByteBufferUtils.checkRange; 20 | import static net.jpountz.util.SafeUtils.checkRange; 21 | 22 | import java.nio.ByteBuffer; 23 | 24 | final class XXHash64JNI extends XXHash64 { 25 | 26 | public static final XXHash64 INSTANCE = new XXHash64JNI(); 27 | private static XXHash64 SAFE_INSTANCE; 28 | 29 | @Override 30 | public long hash(byte[] buf, int off, int len, long seed) { 31 | checkRange(buf, off, len); 32 | return XXHashJNI.XXH64(buf, off, len, seed); 33 | } 34 | 35 | @Override 36 | public long hash(ByteBuffer buf, int off, int len, long seed) { 37 | if (buf.isDirect()) { 38 | checkRange(buf, off, len); 39 | return XXHashJNI.XXH64BB(buf, off, len, seed); 40 | } else if (buf.hasArray()) { 41 | return hash(buf.array(), off + buf.arrayOffset(), len, seed); 42 | } else { 43 | XXHash64 safeInstance = SAFE_INSTANCE; 44 | if (safeInstance == null) { 45 | safeInstance = SAFE_INSTANCE = XXHashFactory.safeInstance().hash64(); 46 | } 47 | return safeInstance.hash(buf, off, len, seed); 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/XXHashConstants.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | enum XXHashConstants { 20 | ; 21 | 22 | static final int PRIME1 = -1640531535; 23 | static final int PRIME2 = -2048144777; 24 | static final int PRIME3 = -1028477379; 25 | static final int PRIME4 = 668265263; 26 | static final int PRIME5 = 374761393; 27 | 28 | static final long PRIME64_1 = -7046029288634856825L; //11400714785074694791 29 | static final long PRIME64_2 = -4417276706812531889L; //14029467366897019727 30 | static final long PRIME64_3 = 1609587929392839161L; 31 | static final long PRIME64_4 = -8796714831421723037L; //9650029242287828579 32 | static final long PRIME64_5 = 2870177450012600261L; 33 | } 34 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/XXHashJNI.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | import net.jpountz.util.Native; 22 | 23 | enum XXHashJNI { 24 | ; 25 | 26 | static { 27 | Native.load(); 28 | init(); 29 | } 30 | 31 | private static native void init(); 32 | static native int XXH32(byte[] input, int offset, int len, int seed); 33 | static native int XXH32BB(ByteBuffer input, int offset, int len, int seed); 34 | static native long XXH32_init(int seed); 35 | static native void XXH32_update(long state, byte[] input, int offset, int len); 36 | static native int XXH32_digest(long state); 37 | static native void XXH32_free(long state); 38 | 39 | static native long XXH64(byte[] input, int offset, int len, long seed); 40 | static native long XXH64BB(ByteBuffer input, int offset, int len, long seed); 41 | static native long XXH64_init(long seed); 42 | static native void XXH64_update(long state, byte[] input, int offset, int len); 43 | static native long XXH64_digest(long state); 44 | static native void XXH64_free(long state); 45 | } 46 | -------------------------------------------------------------------------------- /src/java/net/jpountz/xxhash/package.html: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 |

xxhash hashing. This package supports both block hashing via 23 | {@link net.jpountz.xxhash.XXHash32} and streaming hashing via 24 | {@link net.jpountz.xxhash.StreamingXXHash32}. Have a look at 25 | {@link net.jpountz.xxhash.XXHashFactory} to know how to get instances of these 26 | interfaces.

27 | 28 |

Streaming hashing is a little slower but doesn't require to load the whole 29 | stream into memory.

30 | 31 |

Sample block usage:

32 | 33 |
34 |     XXHashFactory factory = XXHashFactory.fastestInstance();
35 | 
36 |     byte[] data = "12345345234572".getBytes("UTF-8");
37 | 
38 |     XXHash32 hash32 = factory.hash32();
39 |     int seed = 0x9747b28c; // used to initialize the hash value, use whatever
40 |                            // value you want, but always the same
41 |     int hash = hash32.hash(data, 0, data.length, seed);
42 | 
43 | 44 |

Sample streaming usage:

45 | 46 |
47 |     XXHashFactory factory = XXHashFactory.fastestInstance();
48 | 
49 |     byte[] data = "12345345234572".getBytes("UTF-8");
50 |     ByteArrayInputStream in = new ByteArrayInputStream(data);
51 | 
52 |     int seed = 0x9747b28c; // used to initialize the hash value, use whatever
53 |                            // value you want, but always the same
54 |     StreamingXXHash32 hash32 = factory.newStreamingHash32(seed);
55 |     byte[] buf = new byte[8]; // for real-world usage, use a larger buffer, like 8192 bytes
56 |     for (;;) {
57 |       int read = in.read(buf);
58 |       if (read == -1) {
59 |         break;
60 |       }
61 |       hash32.update(buf, 0, read);
62 |     }
63 |     int hash = hash32.getValue();
64 | 
65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/java/overview.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | lz4 / xxHash - Java API 19 | 20 | 21 | 22 |

LZ4 - Java is a Java port of the popular 23 | lz4 compression algorithms and 24 | xxHash hashing algorithm.

25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/jni/net_jpountz_lz4_LZ4JNI.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "lz4.h" 18 | #include "lz4hc.h" 19 | #include "net_jpountz_lz4_LZ4JNI.h" 20 | 21 | static jclass OutOfMemoryError; 22 | 23 | /* 24 | * Class: net_jpountz_lz4_LZ4 25 | * Method: init 26 | * Signature: ()V 27 | */ 28 | JNIEXPORT void JNICALL Java_net_jpountz_lz4_LZ4JNI_init 29 | (JNIEnv *env, jclass cls) { 30 | OutOfMemoryError = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); 31 | } 32 | 33 | static void throw_OOM(JNIEnv *env) { 34 | (*env)->ThrowNew(env, OutOfMemoryError, "Out of memory"); 35 | } 36 | 37 | /* 38 | * Class: net_jpountz_lz4_LZ4JNI 39 | * Method: LZ4_compress_limitedOutput 40 | * Signature: ([BLjava/nio/ByteBuffer;II[BLjava/nio/ByteBuffer;II)I 41 | * 42 | * Though LZ4_compress_limitedOutput is no longer called as it was deprecated, 43 | * keep the method name of LZ4_compress_limitedOutput for backward compatibility, 44 | * so that the old JNI bindings in src/resources can still be used. 45 | */ 46 | JNIEXPORT jint JNICALL Java_net_jpountz_lz4_LZ4JNI_LZ4_1compress_1limitedOutput 47 | (JNIEnv *env, jclass cls, jbyteArray srcArray, jobject srcBuffer, jint srcOff, jint srcLen, jbyteArray destArray, jobject destBuffer, jint destOff, jint maxDestLen) { 48 | 49 | char* in; 50 | char* out; 51 | jint compressed; 52 | 53 | if (srcArray != NULL) { 54 | in = (char*) (*env)->GetPrimitiveArrayCritical(env, srcArray, 0); 55 | } else { 56 | in = (char*) (*env)->GetDirectBufferAddress(env, srcBuffer); 57 | } 58 | 59 | if (in == NULL) { 60 | throw_OOM(env); 61 | return 0; 62 | } 63 | 64 | if (destArray != NULL) { 65 | out = (char*) (*env)->GetPrimitiveArrayCritical(env, destArray, 0); 66 | } else { 67 | out = (char*) (*env)->GetDirectBufferAddress(env, destBuffer); 68 | } 69 | 70 | if (out == NULL) { 71 | throw_OOM(env); 72 | return 0; 73 | } 74 | 75 | compressed = LZ4_compress_default(in + srcOff, out + destOff, srcLen, maxDestLen); 76 | 77 | if (srcArray != NULL) { 78 | (*env)->ReleasePrimitiveArrayCritical(env, srcArray, in, 0); 79 | } 80 | if (destArray != NULL) { 81 | (*env)->ReleasePrimitiveArrayCritical(env, destArray, out, 0); 82 | } 83 | 84 | return compressed; 85 | 86 | } 87 | 88 | /* 89 | * Class: net_jpountz_lz4_LZ4JNI 90 | * Method: LZ4_compressHC 91 | * Signature: ([BLjava/nio/ByteBuffer;II[BLjava/nio/ByteBuffer;III)I 92 | */ 93 | JNIEXPORT jint JNICALL Java_net_jpountz_lz4_LZ4JNI_LZ4_1compressHC 94 | (JNIEnv *env, jclass cls, jbyteArray srcArray, jobject srcBuffer, jint srcOff, jint srcLen, jbyteArray destArray, jobject destBuffer, jint destOff, jint maxDestLen, jint compressionLevel) { 95 | 96 | char* in; 97 | char* out; 98 | jint compressed; 99 | 100 | if (srcArray != NULL) { 101 | in = (char*) (*env)->GetPrimitiveArrayCritical(env, srcArray, 0); 102 | } else { 103 | in = (char*) (*env)->GetDirectBufferAddress(env, srcBuffer); 104 | } 105 | 106 | if (in == NULL) { 107 | throw_OOM(env); 108 | return 0; 109 | } 110 | 111 | if (destArray != NULL) { 112 | out = (char*) (*env)->GetPrimitiveArrayCritical(env, destArray, 0); 113 | } else { 114 | out = (char*) (*env)->GetDirectBufferAddress(env, destBuffer); 115 | } 116 | 117 | if (out == NULL) { 118 | throw_OOM(env); 119 | return 0; 120 | } 121 | 122 | compressed = LZ4_compress_HC(in + srcOff, out + destOff, srcLen, maxDestLen, compressionLevel); 123 | 124 | if (srcArray != NULL) { 125 | (*env)->ReleasePrimitiveArrayCritical(env, srcArray, in, 0); 126 | } 127 | if (destArray != NULL) { 128 | (*env)->ReleasePrimitiveArrayCritical(env, destArray, out, 0); 129 | } 130 | 131 | return compressed; 132 | 133 | } 134 | 135 | /* 136 | * Class: net_jpountz_lz4_LZ4JNI 137 | * Method: LZ4_decompress_fast 138 | * Signature: ([BLjava/nio/ByteBuffer;I[BLjava/nio/ByteBuffer;II)I 139 | */ 140 | JNIEXPORT jint JNICALL Java_net_jpountz_lz4_LZ4JNI_LZ4_1decompress_1fast 141 | (JNIEnv *env, jclass cls, jbyteArray srcArray, jobject srcBuffer, jint srcOff, jbyteArray destArray, jobject destBuffer, jint destOff, jint destLen) { 142 | 143 | char* in; 144 | char* out; 145 | jint compressed; 146 | 147 | if (srcArray != NULL) { 148 | in = (char*) (*env)->GetPrimitiveArrayCritical(env, srcArray, 0); 149 | } else { 150 | in = (char*) (*env)->GetDirectBufferAddress(env, srcBuffer); 151 | } 152 | 153 | if (in == NULL) { 154 | throw_OOM(env); 155 | return 0; 156 | } 157 | 158 | if (destArray != NULL) { 159 | out = (char*) (*env)->GetPrimitiveArrayCritical(env, destArray, 0); 160 | } else { 161 | out = (char*) (*env)->GetDirectBufferAddress(env, destBuffer); 162 | } 163 | 164 | if (out == NULL) { 165 | throw_OOM(env); 166 | return 0; 167 | } 168 | 169 | compressed = LZ4_decompress_fast(in + srcOff, out + destOff, destLen); 170 | 171 | if (srcArray != NULL) { 172 | (*env)->ReleasePrimitiveArrayCritical(env, srcArray, in, 0); 173 | } 174 | if (destArray != NULL) { 175 | (*env)->ReleasePrimitiveArrayCritical(env, destArray, out, 0); 176 | } 177 | 178 | return compressed; 179 | 180 | } 181 | 182 | /* 183 | * Class: net_jpountz_lz4_LZ4JNI 184 | * Method: LZ4_decompress_safe 185 | * Signature: ([BLjava/nio/ByteBuffer;II[BLjava/nio/ByteBuffer;II)I 186 | */ 187 | JNIEXPORT jint JNICALL Java_net_jpountz_lz4_LZ4JNI_LZ4_1decompress_1safe 188 | (JNIEnv *env, jclass cls, jbyteArray srcArray, jobject srcBuffer, jint srcOff, jint srcLen, jbyteArray destArray, jobject destBuffer, jint destOff, jint maxDestLen) { 189 | 190 | char* in; 191 | char* out; 192 | jint decompressed; 193 | 194 | if (srcArray != NULL) { 195 | in = (char*) (*env)->GetPrimitiveArrayCritical(env, srcArray, 0); 196 | } else { 197 | in = (char*) (*env)->GetDirectBufferAddress(env, srcBuffer); 198 | } 199 | 200 | if (in == NULL) { 201 | throw_OOM(env); 202 | return 0; 203 | } 204 | 205 | if (destArray != NULL) { 206 | out = (char*) (*env)->GetPrimitiveArrayCritical(env, destArray, 0); 207 | } else { 208 | out = (char*) (*env)->GetDirectBufferAddress(env, destBuffer); 209 | } 210 | 211 | if (out == NULL) { 212 | throw_OOM(env); 213 | return 0; 214 | } 215 | 216 | decompressed = LZ4_decompress_safe(in + srcOff, out + destOff, srcLen, maxDestLen); 217 | 218 | if (srcArray != NULL) { 219 | (*env)->ReleasePrimitiveArrayCritical(env, srcArray, in, 0); 220 | } 221 | if (destArray != NULL) { 222 | (*env)->ReleasePrimitiveArrayCritical(env, destArray, out, 0); 223 | } 224 | 225 | return decompressed; 226 | 227 | } 228 | 229 | /* 230 | * Class: net_jpountz_lz4_LZ4JNI 231 | * Method: LZ4_compressBound 232 | * Signature: (I)I 233 | */ 234 | JNIEXPORT jint JNICALL Java_net_jpountz_lz4_LZ4JNI_LZ4_1compressBound 235 | (JNIEnv *env, jclass cls, jint len) { 236 | 237 | return LZ4_compressBound(len); 238 | 239 | } 240 | -------------------------------------------------------------------------------- /src/jni/net_jpountz_xxhash_XXHashJNI.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "xxhash.h" 19 | #include "net_jpountz_xxhash_XXHashJNI.h" 20 | 21 | static jclass OutOfMemoryError; 22 | 23 | /* 24 | * Class: net_jpountz_xxhash_XXHashJNI 25 | * Method: init 26 | * Signature: ()V 27 | */ 28 | JNIEXPORT void JNICALL Java_net_jpountz_xxhash_XXHashJNI_init 29 | (JNIEnv *env, jclass cls) { 30 | OutOfMemoryError = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); 31 | } 32 | 33 | static void throw_OOM(JNIEnv *env) { 34 | (*env)->ThrowNew(env, OutOfMemoryError, "Out of memory"); 35 | } 36 | 37 | /* 38 | * Class: net_jpountz_xxhash_XXHashJNI 39 | * Method: XXH32 40 | * Signature: ([BIII)I 41 | */ 42 | JNIEXPORT jint JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH32 43 | (JNIEnv *env, jclass cls, jbyteArray buf, jint off, jint len, jint seed) { 44 | 45 | char* in; 46 | jint h32; 47 | 48 | in = (char*) (*env)->GetPrimitiveArrayCritical(env, buf, 0); 49 | if (in == NULL) { 50 | throw_OOM(env); 51 | return 0; 52 | } 53 | 54 | h32 = XXH32(in + off, len, seed); 55 | 56 | (*env)->ReleasePrimitiveArrayCritical(env, buf, in, 0); 57 | 58 | return h32; 59 | } 60 | 61 | /* 62 | * Class: net_jpountz_xxhash_XXHashJNI 63 | * Method: XXH32BB 64 | * Signature: (Ljava/nio/ByteBuffer;III)I 65 | */ 66 | JNIEXPORT jint JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH32BB 67 | (JNIEnv *env, jclass cls, jobject buf, jint off, jint len, jint seed) { 68 | 69 | char* in; 70 | jint h32; 71 | 72 | in = (char*) (*env)->GetDirectBufferAddress(env, buf); 73 | if (in == NULL) { 74 | throw_OOM(env); 75 | return 0; 76 | } 77 | 78 | h32 = XXH32(in + off, len, seed); 79 | 80 | return h32; 81 | 82 | } 83 | 84 | /* 85 | * Class: net_jpountz_xxhash_XXHashJNI 86 | * Method: XXH32_init 87 | * Signature: (I)J 88 | */ 89 | JNIEXPORT jlong JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH32_1init 90 | (JNIEnv *env, jclass cls, jint seed) { 91 | 92 | XXH32_state_t *state = XXH32_createState(); 93 | if (XXH32_reset(state, seed) != XXH_OK) { 94 | XXH32_freeState(state); 95 | throw_OOM(env); 96 | return 0; 97 | } 98 | 99 | return (jlong) state; 100 | 101 | } 102 | 103 | /* 104 | * Class: net_jpountz_xxhash_XXHashJNI 105 | * Method: XXH32_update 106 | * Signature: (J[BII)V 107 | */ 108 | JNIEXPORT void JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH32_1update 109 | (JNIEnv *env, jclass cls, jlong state, jbyteArray src, jint off, jint len) { 110 | 111 | char* in = (char*) (*env)->GetPrimitiveArrayCritical(env, src, 0); 112 | if (in == NULL) { 113 | throw_OOM(env); 114 | return; 115 | } 116 | 117 | XXH32_update((void*) state, in + off, len); 118 | 119 | (*env)->ReleasePrimitiveArrayCritical(env, src, in, 0); 120 | 121 | } 122 | 123 | /* 124 | * Class: net_jpountz_xxhash_XXHashJNI 125 | * Method: XXH32_digest 126 | * Signature: (J)I 127 | */ 128 | JNIEXPORT jint JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH32_1digest 129 | (JNIEnv *env, jclass cls, jlong state) { 130 | 131 | return XXH32_digest((XXH32_state_t*) state); 132 | 133 | } 134 | 135 | /* 136 | * Class: net_jpountz_xxhash_XXHashJNI 137 | * Method: XXH32_free 138 | * Signature: (J)V 139 | */ 140 | JNIEXPORT void JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH32_1free 141 | (JNIEnv *env, jclass cls, jlong state) { 142 | 143 | XXH32_freeState((XXH32_state_t*) state); 144 | 145 | } 146 | 147 | /* 148 | * Class: net_jpountz_xxhash_XXHashJNI 149 | * Method: XXH64 150 | * Signature: ([BIIJ)J 151 | */ 152 | JNIEXPORT jlong JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH64 153 | (JNIEnv *env, jclass cls, jbyteArray buf, jint off, jint len, jlong seed) { 154 | 155 | char* in; 156 | jlong h64; 157 | 158 | in = (char*) (*env)->GetPrimitiveArrayCritical(env, buf, 0); 159 | if (in == NULL) { 160 | throw_OOM(env); 161 | return 0; 162 | } 163 | 164 | h64 = XXH64(in + off, len, seed); 165 | 166 | (*env)->ReleasePrimitiveArrayCritical(env, buf, in, 0); 167 | 168 | return h64; 169 | } 170 | 171 | /* 172 | * Class: net_jpountz_xxhash_XXHashJNI 173 | * Method: XXH64BB 174 | * Signature: (Ljava/nio/ByteBuffer;IIJ)J 175 | */ 176 | JNIEXPORT jlong JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH64BB 177 | (JNIEnv *env, jclass cls, jobject buf, jint off, jint len, jlong seed) { 178 | 179 | char* in; 180 | jlong h64; 181 | 182 | in = (char*) (*env)->GetDirectBufferAddress(env, buf); 183 | if (in == NULL) { 184 | throw_OOM(env); 185 | return 0; 186 | } 187 | 188 | h64 = XXH64(in + off, len, seed); 189 | 190 | return h64; 191 | 192 | } 193 | 194 | /* 195 | * Class: net_jpountz_xxhash_XXHashJNI 196 | * Method: XXH64_init 197 | * Signature: (J)J 198 | */ 199 | JNIEXPORT jlong JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH64_1init 200 | (JNIEnv *env, jclass cls, jlong seed) { 201 | 202 | XXH64_state_t *state = XXH64_createState(); 203 | if (XXH64_reset(state, seed) != XXH_OK) { 204 | XXH64_freeState(state); 205 | throw_OOM(env); 206 | return 0; 207 | } 208 | 209 | return (jlong) state; 210 | 211 | } 212 | 213 | /* 214 | * Class: net_jpountz_xxhash_XXHashJNI 215 | * Method: XXH64_update 216 | * Signature: (J[BII)V 217 | */ 218 | JNIEXPORT void JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH64_1update 219 | (JNIEnv *env, jclass cls, jlong state, jbyteArray src, jint off, jint len) { 220 | 221 | char* in = (char*) (*env)->GetPrimitiveArrayCritical(env, src, 0); 222 | if (in == NULL) { 223 | throw_OOM(env); 224 | return; 225 | } 226 | 227 | XXH64_update((XXH64_state_t*) state, in + off, len); 228 | 229 | (*env)->ReleasePrimitiveArrayCritical(env, src, in, 0); 230 | 231 | } 232 | 233 | /* 234 | * Class: net_jpountz_xxhash_XXHashJNI 235 | * Method: XXH64_digest 236 | * Signature: (J)J 237 | */ 238 | JNIEXPORT jlong JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH64_1digest 239 | (JNIEnv *env, jclass cls, jlong state) { 240 | 241 | return XXH64_digest((XXH64_state_t*) state); 242 | 243 | } 244 | 245 | /* 246 | * Class: net_jpountz_xxhash_XXHashJNI 247 | * Method: XXH64_free 248 | * Signature: (J)V 249 | */ 250 | JNIEXPORT void JNICALL Java_net_jpountz_xxhash_XXHashJNI_XXH64_1free 251 | (JNIEnv *env, jclass cls, jlong state) { 252 | 253 | XXH64_freeState((XXH64_state_t*) state); 254 | 255 | } 256 | -------------------------------------------------------------------------------- /src/resources/net/jpountz/util/darwin/aarch64/liblz4-java.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/resources/net/jpountz/util/darwin/aarch64/liblz4-java.dylib -------------------------------------------------------------------------------- /src/resources/net/jpountz/util/darwin/x86_64/liblz4-java.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/resources/net/jpountz/util/darwin/x86_64/liblz4-java.dylib -------------------------------------------------------------------------------- /src/resources/net/jpountz/util/linux/aarch64/liblz4-java.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/resources/net/jpountz/util/linux/aarch64/liblz4-java.so -------------------------------------------------------------------------------- /src/resources/net/jpountz/util/linux/amd64/liblz4-java.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/resources/net/jpountz/util/linux/amd64/liblz4-java.so -------------------------------------------------------------------------------- /src/resources/net/jpountz/util/linux/i386/liblz4-java.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/resources/net/jpountz/util/linux/i386/liblz4-java.so -------------------------------------------------------------------------------- /src/resources/net/jpountz/util/linux/ppc64le/liblz4-java.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/resources/net/jpountz/util/linux/ppc64le/liblz4-java.so -------------------------------------------------------------------------------- /src/resources/net/jpountz/util/linux/s390x/liblz4-java.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/resources/net/jpountz/util/linux/s390x/liblz4-java.so -------------------------------------------------------------------------------- /src/resources/net/jpountz/util/win32/amd64/liblz4-java.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/resources/net/jpountz/util/win32/amd64/liblz4-java.so -------------------------------------------------------------------------------- /src/test-resources/calgary/README: -------------------------------------------------------------------------------- 1 | Data from the Calgary corpus (http://corpus.canterbury.ac.nz/descriptions/#calgary). 2 | -------------------------------------------------------------------------------- /src/test-resources/calgary/geo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/test-resources/calgary/geo -------------------------------------------------------------------------------- /src/test-resources/calgary/pic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lz4/lz4-java/cdb417f0bb0de1bf304831806b6693bd652748aa/src/test-resources/calgary/pic -------------------------------------------------------------------------------- /src/test/net/jpountz/example/LZ4Example.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.example; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.io.UnsupportedEncodingException; 20 | 21 | import net.jpountz.lz4.LZ4Compressor; 22 | import net.jpountz.lz4.LZ4Factory; 23 | import net.jpountz.lz4.LZ4FastDecompressor; 24 | import net.jpountz.lz4.LZ4SafeDecompressor; 25 | 26 | public class LZ4Example { 27 | 28 | public static void main(String[] args) throws Exception { 29 | example(); 30 | } 31 | 32 | private static void example() throws UnsupportedEncodingException { 33 | LZ4Factory factory = LZ4Factory.fastestInstance(); 34 | 35 | byte[] data = "12345345234572".getBytes("UTF-8"); 36 | final int decompressedLength = data.length; 37 | 38 | // compress data 39 | LZ4Compressor compressor = factory.fastCompressor(); 40 | int maxCompressedLength = compressor.maxCompressedLength(decompressedLength); 41 | byte[] compressed = new byte[maxCompressedLength]; 42 | int compressedLength = compressor.compress(data, 0, decompressedLength, compressed, 0, maxCompressedLength); 43 | 44 | // decompress data 45 | // - method 1: when the decompressed length is known 46 | LZ4FastDecompressor decompressor = factory.fastDecompressor(); 47 | byte[] restored = new byte[decompressedLength]; 48 | int compressedLength2 = decompressor.decompress(compressed, 0, restored, 0, decompressedLength); 49 | // compressedLength == compressedLength2 50 | 51 | // - method 2: when the compressed length is known (a little slower) 52 | // the destination buffer needs to be over-sized 53 | LZ4SafeDecompressor decompressor2 = factory.safeDecompressor(); 54 | int decompressedLength2 = decompressor2.decompress(compressed, 0, compressedLength, restored, 0); 55 | // decompressedLength == decompressedLength2 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/test/net/jpountz/example/XXHashExample.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.example; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.io.ByteArrayInputStream; 20 | import java.io.IOException; 21 | import java.io.UnsupportedEncodingException; 22 | 23 | import net.jpountz.xxhash.StreamingXXHash32; 24 | import net.jpountz.xxhash.XXHash32; 25 | import net.jpountz.xxhash.XXHashFactory; 26 | 27 | public class XXHashExample { 28 | 29 | public static void main(String[] args) throws Exception { 30 | blockExample(); 31 | streamingExample(); 32 | } 33 | 34 | private static void blockExample() throws UnsupportedEncodingException { 35 | XXHashFactory factory = XXHashFactory.fastestInstance(); 36 | 37 | byte[] data = "12345345234572".getBytes("UTF-8"); 38 | 39 | XXHash32 hash32 = factory.hash32(); 40 | int seed = 0x9747b28c; // used to initialize the hash value, use whatever 41 | // value you want, but always the same 42 | int hash = hash32.hash(data, 0, data.length, seed); 43 | System.out.println("Block hash: " + hash); 44 | } 45 | 46 | private static void streamingExample() throws IOException { 47 | XXHashFactory factory = XXHashFactory.fastestInstance(); 48 | 49 | byte[] data = "12345345234572".getBytes("UTF-8"); 50 | ByteArrayInputStream in = new ByteArrayInputStream(data); 51 | 52 | int seed = 0x9747b28c; // used to initialize the hash value, use whatever 53 | // value you want, but always the same 54 | StreamingXXHash32 hash32 = factory.newStreamingHash32(seed); 55 | byte[] buf = new byte[8]; // for real-world usage, use a larger buffer, like 8192 bytes 56 | for (;;) { 57 | int read = in.read(buf); 58 | if (read == -1) { 59 | break; 60 | } 61 | hash32.update(buf, 0, read); 62 | } 63 | int hash = hash32.getValue(); 64 | System.out.println("Streaming hash: " + hash); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/test/net/jpountz/lz4/Instances.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | 20 | enum Instances { 21 | ; 22 | 23 | static LZ4Compressor[] COMPRESSORS = new LZ4Compressor[] { 24 | LZ4Factory.nativeInstance().fastCompressor(), 25 | LZ4Factory.nativeInstance().highCompressor(), 26 | LZ4Factory.unsafeInstance().fastCompressor(), 27 | LZ4Factory.unsafeInstance().highCompressor(), 28 | LZ4Factory.safeInstance().fastCompressor(), 29 | LZ4Factory.safeInstance().highCompressor() 30 | }; 31 | 32 | static LZ4FastDecompressor[] FAST_DECOMPRESSORS = new LZ4FastDecompressor[] { 33 | LZ4Factory.nativeInstance().fastDecompressor(), 34 | LZ4Factory.unsafeInstance().fastDecompressor(), 35 | LZ4Factory.safeInstance().fastDecompressor() 36 | }; 37 | 38 | static LZ4SafeDecompressor[] SAFE_DECOMPRESSORS = new LZ4SafeDecompressor[] { 39 | LZ4Factory.nativeInstance().safeDecompressor(), 40 | LZ4Factory.unsafeInstance().safeDecompressor(), 41 | LZ4Factory.safeInstance().safeDecompressor() 42 | }; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/net/jpountz/lz4/LZ4FactoryTest.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.lz4; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import junit.framework.TestCase; 20 | 21 | public class LZ4FactoryTest extends TestCase { 22 | 23 | public void test() { 24 | assertEquals(LZ4JNICompressor.INSTANCE, LZ4Factory.nativeInstance().fastCompressor()); 25 | assertEquals(LZ4HCJNICompressor.INSTANCE, LZ4Factory.nativeInstance().highCompressor()); 26 | assertEquals(LZ4JavaUnsafeCompressor.INSTANCE, LZ4Factory.unsafeInstance().fastCompressor()); 27 | assertEquals(LZ4HCJavaUnsafeCompressor.INSTANCE, LZ4Factory.unsafeInstance().highCompressor()); 28 | assertEquals(LZ4JavaSafeCompressor.INSTANCE, LZ4Factory.safeInstance().fastCompressor()); 29 | assertEquals(LZ4HCJavaSafeCompressor.INSTANCE, LZ4Factory.safeInstance().highCompressor()); 30 | 31 | assertEquals(LZ4JNIFastDecompressor.INSTANCE, LZ4Factory.nativeInstance().fastDecompressor()); 32 | assertEquals(LZ4JavaUnsafeFastDecompressor.INSTANCE, LZ4Factory.unsafeInstance().fastDecompressor()); 33 | assertEquals(LZ4JavaSafeFastDecompressor.INSTANCE, LZ4Factory.safeInstance().fastDecompressor()); 34 | 35 | assertEquals(LZ4JNISafeDecompressor.INSTANCE, LZ4Factory.nativeInstance().safeDecompressor()); 36 | assertEquals(LZ4JavaUnsafeSafeDecompressor.INSTANCE, LZ4Factory.unsafeInstance().safeDecompressor()); 37 | assertEquals(LZ4JavaSafeSafeDecompressor.INSTANCE, LZ4Factory.safeInstance().safeDecompressor()); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/net/jpountz/xxhash/XXHash32Test.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Linnaea Von Lavia and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | import net.jpountz.lz4.AbstractLZ4Test; 22 | import net.jpountz.util.SafeUtils; 23 | 24 | import org.junit.Test; 25 | import static org.junit.Assert.*; 26 | 27 | import com.carrotsearch.randomizedtesting.annotations.Repeat; 28 | 29 | public class XXHash32Test extends AbstractLZ4Test { 30 | 31 | private static abstract class StreamingXXHash32Adapter extends XXHash32 { 32 | 33 | protected abstract StreamingXXHash32 streamingHash(int seed); 34 | 35 | @Override 36 | public int hash(byte[] buf, int off, int len, int seed) { 37 | SafeUtils.checkRange(buf, off, len); 38 | int originalOff = off; 39 | int remainingPasses = randomInt(5); 40 | StreamingXXHash32 h = streamingHash(seed); 41 | final int end = off + len; 42 | while (off < end) { 43 | final int l = randomIntBetween(off, end) - off; 44 | h.update(buf, off, l); 45 | off += l; 46 | if (remainingPasses > 0 && randomInt(5) == 0) { 47 | h.reset(); 48 | --remainingPasses; 49 | off = originalOff; 50 | } 51 | if (randomBoolean()) { 52 | h.getValue(); 53 | } 54 | } 55 | return h.getValue(); 56 | } 57 | 58 | @Override 59 | public int hash(ByteBuffer buf, int off, int len, int seed) { 60 | byte[] bytes = new byte[len]; 61 | int originalPosition = buf.position(); 62 | try { 63 | buf.position(off); 64 | buf.get(bytes, 0, len); 65 | return hash(bytes, 0, len, seed); 66 | } finally { 67 | buf.position(originalPosition); 68 | } 69 | } 70 | 71 | public String toString() { 72 | return streamingHash(0).toString(); 73 | } 74 | 75 | } 76 | 77 | private static XXHash32[] INSTANCES = new XXHash32[] { 78 | XXHashFactory.nativeInstance().hash32(), 79 | XXHashFactory.unsafeInstance().hash32(), 80 | XXHashFactory.safeInstance().hash32(), 81 | new StreamingXXHash32Adapter() { 82 | protected StreamingXXHash32 streamingHash(int seed) { 83 | return XXHashFactory.nativeInstance().newStreamingHash32(seed); 84 | } 85 | }, 86 | new StreamingXXHash32Adapter() { 87 | protected StreamingXXHash32 streamingHash(int seed) { 88 | return XXHashFactory.unsafeInstance().newStreamingHash32(seed); 89 | } 90 | }, 91 | new StreamingXXHash32Adapter() { 92 | protected StreamingXXHash32 streamingHash(int seed) { 93 | return XXHashFactory.safeInstance().newStreamingHash32(seed); 94 | } 95 | } 96 | }; 97 | 98 | @Test 99 | public void testEmpty() { 100 | final int seed = randomInt(); 101 | for (XXHash32 xxHash : INSTANCES) { 102 | xxHash.hash(new byte[0], 0, 0, seed); 103 | xxHash.hash(copyOf(new byte[0], 0, 0), 0, 0, seed); 104 | } 105 | } 106 | 107 | @Test 108 | @Repeat(iterations = 20) 109 | public void testAIOOBE() { 110 | final int seed = randomInt(); 111 | final int max = randomBoolean() ? 32 : 1000; 112 | final int bufLen = randomIntBetween(1, max); 113 | final byte[] buf = randomArray(bufLen, 256); 114 | final int off = randomInt(buf.length - 1); 115 | final int len = randomInt(buf.length - off); 116 | for (XXHash32 xxHash : INSTANCES) { 117 | xxHash.hash(buf, off, len, seed); 118 | xxHash.hash(copyOf(buf, off, len), off, len, seed); 119 | } 120 | } 121 | 122 | @Test 123 | @Repeat(iterations=40) 124 | public void testInstances() { 125 | final int maxLenLog = randomInt(20); 126 | final int bufLen = randomInt(1 << maxLenLog); 127 | byte[] buf = randomArray(bufLen, 256); 128 | final int seed = randomInt(); 129 | final int off = randomIntBetween(0, Math.max(0, bufLen - 1)); 130 | final int len = randomIntBetween(0, bufLen - off); 131 | 132 | final int ref = XXHashFactory.nativeInstance().hash32().hash(buf, off, len, seed); 133 | for (XXHash32 hash : INSTANCES) { 134 | final int h = hash.hash(buf, off, len, seed); 135 | assertEquals(hash.toString(), ref, h); 136 | final ByteBuffer copy = copyOf(buf, off, len); 137 | final int h2 = hash.hash(copy, off, len, seed); 138 | assertEquals(off, copy.position()); 139 | assertEquals(len, copy.remaining()); 140 | assertEquals(hash.toString(), ref, h2); 141 | } 142 | } 143 | 144 | @Test 145 | public void test4GB() { 146 | byte[] bytes = new byte[randomIntBetween(1 << 22, 1 << 26)]; 147 | for (int i = 0; i < bytes.length; ++i) { 148 | bytes[i] = randomByte(); 149 | } 150 | final int off = randomInt(5); 151 | final int len = randomIntBetween(bytes.length - off - 1024, bytes.length - off); 152 | long totalLen = 0; 153 | final int seed = randomInt(); 154 | StreamingXXHash32 hash1 = XXHashFactory.nativeInstance().newStreamingHash32(seed); 155 | StreamingXXHash32 hash2 = XXHashFactory.unsafeInstance().newStreamingHash32(seed); 156 | StreamingXXHash32 hash3 = XXHashFactory.safeInstance().newStreamingHash32(seed); 157 | while (totalLen < (1L << 33)) { 158 | hash1.update(bytes, off, len); 159 | hash2.update(bytes, off, len); 160 | hash3.update(bytes, off, len); 161 | assertEquals(hash2.toString() + " " + totalLen, hash1.getValue(), hash2.getValue()); 162 | assertEquals(hash3.toString() + " " + totalLen, hash1.getValue(), hash3.getValue()); 163 | totalLen += len; 164 | } 165 | } 166 | 167 | @Test 168 | public void testClose() { 169 | StreamingXXHash32 hash = XXHashFactory.nativeInstance().newStreamingHash32(randomInt()); 170 | hash.close(); 171 | hash.close(); 172 | try { 173 | hash.getValue(); 174 | assertTrue(hash.toString(), false); 175 | } catch (AssertionError e) { 176 | // OK 177 | } 178 | try { 179 | hash.update(null, 0, 0); 180 | assertTrue(hash.toString(), false); 181 | } catch (AssertionError e) { 182 | // OK 183 | } 184 | try { 185 | hash.reset(); 186 | assertTrue(hash.toString(), false); 187 | } catch (AssertionError e) { 188 | // OK 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/test/net/jpountz/xxhash/XXHash64Test.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Linnaea Von Lavia and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | import net.jpountz.lz4.AbstractLZ4Test; 22 | import net.jpountz.util.SafeUtils; 23 | 24 | import org.junit.Test; 25 | import static org.junit.Assert.*; 26 | 27 | import com.carrotsearch.randomizedtesting.annotations.Repeat; 28 | 29 | public class XXHash64Test extends AbstractLZ4Test { 30 | 31 | private static abstract class StreamingXXHash64Adapter extends XXHash64 { 32 | 33 | protected abstract StreamingXXHash64 streamingHash(long seed); 34 | 35 | @Override 36 | public long hash(byte[] buf, int off, int len, long seed) { 37 | SafeUtils.checkRange(buf, off, len); 38 | int originalOff = off; 39 | int remainingPasses = randomInt(5); 40 | StreamingXXHash64 h = streamingHash(seed); 41 | final int end = off + len; 42 | while (off < end) { 43 | final int l = randomIntBetween(off, end) - off; 44 | h.update(buf, off, l); 45 | off += l; 46 | if (remainingPasses > 0 && randomInt(5) == 0) { 47 | h.reset(); 48 | --remainingPasses; 49 | off = originalOff; 50 | } 51 | if (randomBoolean()) { 52 | h.getValue(); 53 | } 54 | } 55 | return h.getValue(); 56 | } 57 | 58 | @Override 59 | public long hash(ByteBuffer buf, int off, int len, long seed) { 60 | byte[] bytes = new byte[len]; 61 | int originalPosition = buf.position(); 62 | try { 63 | buf.position(off); 64 | buf.get(bytes, 0, len); 65 | return hash(bytes, 0, len, seed); 66 | } finally { 67 | buf.position(originalPosition); 68 | } 69 | } 70 | 71 | public String toString() { 72 | return streamingHash(0).toString(); 73 | } 74 | 75 | } 76 | 77 | private static XXHash64[] INSTANCES = new XXHash64[] { 78 | XXHashFactory.nativeInstance().hash64(), 79 | XXHashFactory.unsafeInstance().hash64(), 80 | XXHashFactory.safeInstance().hash64(), 81 | new StreamingXXHash64Adapter() { 82 | protected StreamingXXHash64 streamingHash(long seed) { 83 | return XXHashFactory.nativeInstance().newStreamingHash64(seed); 84 | } 85 | }, 86 | new StreamingXXHash64Adapter() { 87 | protected StreamingXXHash64 streamingHash(long seed) { 88 | return XXHashFactory.unsafeInstance().newStreamingHash64(seed); 89 | } 90 | }, 91 | new StreamingXXHash64Adapter() { 92 | protected StreamingXXHash64 streamingHash(long seed) { 93 | return XXHashFactory.safeInstance().newStreamingHash64(seed); 94 | } 95 | } 96 | }; 97 | 98 | @Test 99 | public void testEmpty() { 100 | final long seed = randomLong(); 101 | for (XXHash64 xxHash : INSTANCES) { 102 | xxHash.hash(new byte[0], 0, 0, seed); 103 | xxHash.hash(copyOf(new byte[0], 0, 0), 0, 0, seed); 104 | } 105 | } 106 | 107 | @Test 108 | @Repeat(iterations = 20) 109 | public void testAIOOBE() { 110 | final long seed = randomLong(); 111 | final int max = randomBoolean() ? 64 : 1000; 112 | final int bufLen = randomIntBetween(1, max); 113 | final byte[] buf = new byte[bufLen]; 114 | for (int i = 0; i < buf.length; ++i) { 115 | buf[i] = randomByte(); 116 | } 117 | final int off = randomInt(buf.length - 1); 118 | final int len = randomInt(buf.length - off); 119 | for (XXHash64 xxHash : INSTANCES) { 120 | xxHash.hash(buf, off, len, seed); 121 | } 122 | } 123 | 124 | @Test 125 | @Repeat(iterations=40) 126 | public void testInstances() { 127 | final int maxLenLog = randomInt(20); 128 | final int bufLen = randomInt(1 << maxLenLog); 129 | byte[] buf = new byte[bufLen]; 130 | for (int i = 0; i < bufLen; ++i) { 131 | buf[i] = randomByte(); 132 | } 133 | final long seed = randomLong(); 134 | final int off = randomIntBetween(0, Math.max(0, bufLen - 1)); 135 | final int len = randomIntBetween(0, bufLen - off); 136 | 137 | final long ref = XXHashFactory.nativeInstance().hash64().hash(buf, off, len, seed); 138 | for (XXHash64 hash : INSTANCES) { 139 | final long h = hash.hash(buf, off, len, seed); 140 | assertEquals(hash.toString(), ref, h); 141 | final ByteBuffer copy = copyOf(buf, off, len); 142 | final long h2 = hash.hash(copy, off, len, seed); 143 | assertEquals(off, copy.position()); 144 | assertEquals(len, copy.remaining()); 145 | assertEquals(hash.toString(), ref, h2); 146 | } 147 | } 148 | 149 | @Test 150 | public void test4GB() { 151 | byte[] bytes = new byte[randomIntBetween(1 << 22, 1 << 26)]; 152 | for (int i = 0; i < bytes.length; ++i) { 153 | bytes[i] = randomByte(); 154 | } 155 | final int off = randomInt(5); 156 | final int len = randomIntBetween(bytes.length - off - 1024, bytes.length - off); 157 | long totalLen = 0; 158 | final long seed = randomLong(); 159 | StreamingXXHash64 hash1 = XXHashFactory.nativeInstance().newStreamingHash64(seed); 160 | StreamingXXHash64 hash2 = XXHashFactory.unsafeInstance().newStreamingHash64(seed); 161 | StreamingXXHash64 hash3 = XXHashFactory.safeInstance().newStreamingHash64(seed); 162 | while (totalLen < (1L << 33)) { 163 | hash1.update(bytes, off, len); 164 | hash2.update(bytes, off, len); 165 | hash3.update(bytes, off, len); 166 | assertEquals(hash2.toString() + " " + totalLen, hash1.getValue(), hash2.getValue()); 167 | assertEquals(hash3.toString() + " " + totalLen, hash1.getValue(), hash3.getValue()); 168 | totalLen += len; 169 | } 170 | } 171 | 172 | @Test 173 | public void testClose() { 174 | StreamingXXHash64 hash = XXHashFactory.nativeInstance().newStreamingHash64(randomInt()); 175 | hash.close(); 176 | hash.close(); 177 | try { 178 | hash.getValue(); 179 | assertTrue(hash.toString(), false); 180 | } catch (AssertionError e) { 181 | // OK 182 | } 183 | try { 184 | hash.update(null, 0, 0); 185 | assertTrue(hash.toString(), false); 186 | } catch (AssertionError e) { 187 | // OK 188 | } 189 | try { 190 | hash.reset(); 191 | assertTrue(hash.toString(), false); 192 | } catch (AssertionError e) { 193 | // OK 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/test/net/jpountz/xxhash/XXHashFactoryTest.java: -------------------------------------------------------------------------------- 1 | package net.jpountz.xxhash; 2 | 3 | /* 4 | * Copyright 2020 Adrien Grand and the lz4-java contributors. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import junit.framework.TestCase; 20 | 21 | public class XXHashFactoryTest extends TestCase { 22 | 23 | public void test() { 24 | assertEquals(XXHash32JNI.INSTANCE, XXHashFactory.nativeInstance().hash32()); 25 | assertTrue(XXHashFactory.nativeInstance().newStreamingHash32(0) instanceof StreamingXXHash32JNI); 26 | assertEquals(XXHash32JavaUnsafe.INSTANCE, XXHashFactory.unsafeInstance().hash32()); 27 | assertTrue(XXHashFactory.unsafeInstance().newStreamingHash32(0) instanceof StreamingXXHash32JavaUnsafe); 28 | assertEquals(XXHash32JavaSafe.INSTANCE, XXHashFactory.safeInstance().hash32()); 29 | assertTrue(XXHashFactory.safeInstance().newStreamingHash32(0) instanceof StreamingXXHash32JavaSafe); 30 | assertEquals(XXHash64JNI.INSTANCE, XXHashFactory.nativeInstance().hash64()); 31 | assertTrue(XXHashFactory.nativeInstance().newStreamingHash64(0) instanceof StreamingXXHash64JNI); 32 | assertEquals(XXHash64JavaUnsafe.INSTANCE, XXHashFactory.unsafeInstance().hash64()); 33 | assertTrue(XXHashFactory.unsafeInstance().newStreamingHash64(0) instanceof StreamingXXHash64JavaUnsafe); 34 | assertEquals(XXHash64JavaSafe.INSTANCE, XXHashFactory.safeInstance().hash64()); 35 | assertTrue(XXHashFactory.safeInstance().newStreamingHash64(0) instanceof StreamingXXHash64JavaSafe); 36 | } 37 | 38 | } 39 | --------------------------------------------------------------------------------