Skip to content

Commit

Permalink
Merge pull request #28 from xxDark/jdk22
Browse files Browse the repository at this point in the history
Bump minimum required JDK to 22, migrate API to MemorySegment
  • Loading branch information
Col-E authored Apr 13, 2024
2 parents b2555f6 + 99db173 commit 822cb24
Show file tree
Hide file tree
Showing 43 changed files with 437 additions and 1,191 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
java-version: [ 17 ]
java-version: [ 22 ]
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
Expand All @@ -33,7 +33,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
java-version: 22
check-latest: true
# The project version extract NEEDS to have the maven wrapper already downloaded.
# So we have a dummy step here just to initialize it.
Expand Down Expand Up @@ -90,7 +90,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
java-version: 22
# The project version extract NEEDS to have the maven wrapper already downloaded.
# So we have a dummy step here just to initialize it.
- name: Download Maven wrapper
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ But that's not all it does. That's just what that one comment says. Some other f

## Additional features

- Reads ZIP files using `Unsafe` backed mapped files.
- Using `FileChannel.map` yields `MappedByteBuffer` which uses `int` values, limiting files up to about 2GB
- Our `UnsafeMappedFile` implementation uses `long` which far exceeds the GB file size range
- Reads ZIP files using `MemorySegment` backed mapped files.
- Highly configurable, offering 3 ZIP reading strategies out of the box _(See `ZipIO` for convenience calls)_
- Std / Forward scanning: Scans for `EndOfCentralDirectory` from the front of the file, like many other tools
- Naive: Scans only for `LocalFileHeader` values from the front of the file, the fastest implementation, but obviously naive
Expand Down
2 changes: 1 addition & 1 deletion jreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ project:
inceptionYear: 2022
stereotype: none
java:
version: 11
version: 22
groupId: software.coley
artifactId: lljzip

Expand Down
10 changes: 3 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>software.coley</groupId>
<artifactId>lljzip</artifactId>
<version>2.3.0</version>
<version>2.4.0</version>

<name>LL Java ZIP</name>
<description>Lower level ZIP support for Java</description>
Expand Down Expand Up @@ -97,13 +97,9 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- When attempting to compile on higher Java versions, you may encounter
some issues with Unsafe. In Intellij unset the 'use -release' under the
javac compiler page and it'll happily compile once again
-->
<version>3.13.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<release>22</release>
</configuration>
</plugin>
<plugin>
Expand Down
42 changes: 29 additions & 13 deletions src/main/java/software/coley/lljzip/ZipIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
import software.coley.lljzip.format.model.EndOfCentralDirectory;
import software.coley.lljzip.format.model.ZipArchive;
import software.coley.lljzip.format.read.*;
import software.coley.lljzip.util.BufferData;
import software.coley.lljzip.util.ByteData;
import software.coley.lljzip.util.FileMapUtil;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipFile;
Expand All @@ -21,7 +21,7 @@
* <li>For regular ZIP files use {@link ForwardScanZipReader}.</li>
* <li>For ZIP files without {@link CentralDirectoryFileHeader} or {@link EndOfCentralDirectory} items, use {@link NaiveLocalFileZipReader}</li>
* </ul>
* You can fully control zip parsing via {@link #read(ByteData, ZipReader)} by passing a customized reader implementation.
* You can fully control zip parsing via {@link #read(MemorySegment, ZipReader)} by passing a customized reader implementation.
*
* @author Matt Coley
*/
Expand All @@ -37,7 +37,7 @@ public class ZipIO {
* @throws IOException
* When the archive bytes cannot be read from, usually indicating a malformed zip.
*/
public static ZipArchive readStandard(ByteData data) throws IOException {
public static ZipArchive readStandard(MemorySegment data) throws IOException {
return read(data, new ForwardScanZipReader());
}

Expand Down Expand Up @@ -82,7 +82,7 @@ public static ZipArchive readStandard(Path data) throws IOException {
* @throws IOException
* When the archive bytes cannot be read from, usually indicating a malformed zip.
*/
public static ZipArchive readNaive(ByteData data) throws IOException {
public static ZipArchive readNaive(MemorySegment data) throws IOException {
return read(data, new NaiveLocalFileZipReader());
}

Expand Down Expand Up @@ -128,7 +128,7 @@ public static ZipArchive readNaive(Path data) throws IOException {
* @throws IOException
* When the archive bytes cannot be read from, usually indicating a malformed zip.
*/
public static ZipArchive readJvm(ByteData data) throws IOException {
public static ZipArchive readJvm(MemorySegment data) throws IOException {
return read(data, new JvmZipReader());
}

Expand Down Expand Up @@ -172,7 +172,7 @@ public static ZipArchive readJvm(Path path) throws IOException {
*
* @return Archive from path.
*
* @throws IOException
* @throws IOException When the archive cannot be read.
*/
public static ZipArchive readAdaptingIO(Path path) throws IOException {
ZipArchive archive = new ZipArchive();
Expand All @@ -194,7 +194,7 @@ public static ZipArchive readAdaptingIO(Path path) throws IOException {
public static ZipArchive read(byte[] data, ZipReader strategy) throws IOException {
if (data == null)
throw new IOException("Data is null!");
return read(BufferData.wrap(data), strategy);
return read(MemorySegment.ofArray(data), strategy);
}

/**
Expand All @@ -213,7 +213,23 @@ public static ZipArchive read(Path path, ZipReader strategy) throws IOException
throw new IOException("Data is null!");
if (!Files.isRegularFile(path))
throw new FileNotFoundException(path.toString());
return read(FileMapUtil.map(path), strategy);
FileChannel fc = FileChannel.open(path);
try {
long size = fc.size();
// The fixed size elements of a CDFH is 22 bytes (plus the variable size bits which can be 0)
// - Even if we only want to read local/central file entries, those are even larger at a minimum
if (size < 22)
throw new IOException("Not enough bytes to read Central-Directory-File-Header, minimum=22");

ZipArchive zip = new ZipArchive(fc);
strategy.read(zip, fc.map(FileChannel.MapMode.READ_ONLY, 0L, size, Arena.ofAuto()));
fc = null;
return zip;
} finally {
if (fc != null) {
fc.close();
}
}
}

/**
Expand All @@ -227,17 +243,17 @@ public static ZipArchive read(Path path, ZipReader strategy) throws IOException
* @throws IOException
* When the archive bytes cannot be read from, usually indicating a malformed zip.
*/
public static ZipArchive read(ByteData data, ZipReader strategy) throws IOException {
public static ZipArchive read(MemorySegment data, ZipReader strategy) throws IOException {
if (data == null)
throw new IOException("Data is null!");

// The fixed size elements of a CDFH is 22 bytes (plus the variable size bits which can be 0)
// - Even if we only want to read local/central file entries, those are even larger at a minimum
if (data.length() < 22)
if (data.byteSize() < 22)
throw new IOException("Not enough bytes to read Central-Directory-File-Header, minimum=22");

// Create instance
ZipArchive zip = new ZipArchive(data);
ZipArchive zip = new ZipArchive();
strategy.read(zip, data);
return zip;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/software/coley/lljzip/format/ZipPatterns.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import software.coley.lljzip.format.model.CentralDirectoryFileHeader;
import software.coley.lljzip.format.model.EndOfCentralDirectory;
import software.coley.lljzip.format.model.LocalFileHeader;
import software.coley.lljzip.util.ByteDataUtil;
import software.coley.lljzip.util.MemorySegmentUtil;

/**
* Patterns for usage in {@link ByteDataUtil} methods.
* Patterns for usage in {@link MemorySegmentUtil} methods.
*
* @author Matt Coley
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package software.coley.lljzip.format.compression;

import software.coley.lljzip.format.model.LocalFileHeader;
import software.coley.lljzip.util.ByteData;

import java.io.IOException;
import java.lang.foreign.MemorySegment;

/**
* Outlines decompression of {@link LocalFileHeader#getFileData()}.
Expand All @@ -22,5 +22,5 @@ public interface Decompressor {
* @throws IOException
* Decompression failure.
*/
ByteData decompress(LocalFileHeader header, ByteData bytes) throws IOException;
MemorySegment decompress(LocalFileHeader header, MemorySegment bytes) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package software.coley.lljzip.format.compression;

import software.coley.lljzip.format.model.LocalFileHeader;
import software.coley.lljzip.util.ByteData;
import software.coley.lljzip.util.FastWrapOutputStream;

import java.io.IOException;
import java.lang.foreign.MemorySegment;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.ZipException;
Expand All @@ -22,23 +22,24 @@ private DeflateDecompressor() {
}

@Override
public ByteData decompress(LocalFileHeader header, ByteData data) throws IOException {
public MemorySegment decompress(LocalFileHeader header, MemorySegment data) throws IOException {
if (header.getCompressionMethod() != ZipCompressions.DEFLATED)
throw new IOException("LocalFileHeader contents not using 'Deflated'!");
Inflater inflater = new Inflater(true);
FastWrapOutputStream out = new FastWrapOutputStream();
try {
byte[] output = new byte[1024];
byte[] buffer = new byte[1024];
MemorySegment bufferSegment = MemorySegment.ofArray(buffer);
long position = 0L;
long length = data.length();
long length = data.byteSize();
do {
if (inflater.needsInput()) {
int remaining = (int) Math.min(buffer.length, length);
if (remaining == 0) {
break;
}
data.get(position, buffer, 0, remaining);
MemorySegment.copy(data, position, bufferSegment, 0, remaining);
length -= remaining;
position += remaining;
inflater.setInput(buffer, 0, remaining);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package software.coley.lljzip.format.compression;

import software.coley.lljzip.format.model.LocalFileHeader;
import software.coley.lljzip.util.ByteData;
import software.coley.lljzip.util.FastWrapOutputStream;
import software.coley.lljzip.util.InflaterHackery;

import java.io.IOException;
import java.lang.foreign.MemorySegment;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.zip.DataFormatException;
Expand Down Expand Up @@ -38,7 +38,7 @@ private UnsafeDeflateDecompressor() {
}

@Override
public ByteData decompress(LocalFileHeader header, ByteData data) throws IOException {
public MemorySegment decompress(LocalFileHeader header, MemorySegment data) throws IOException {
if (header.getCompressionMethod() != ZipCompressions.DEFLATED)
throw new IOException("LocalFileHeader contents not using 'Deflated'!");
FastWrapOutputStream out = new FastWrapOutputStream();
Expand All @@ -55,16 +55,17 @@ public ByteData decompress(LocalFileHeader header, ByteData data) throws IOExcep
try {
byte[] output = entry.decompress;
byte[] buffer = entry.buffer;
MemorySegment bufferSegment = MemorySegment.ofArray(buffer);
Inflater inflater = entry.inflater;
long position = 0L;
long length = data.length();
long length = data.byteSize();
int remaining = 0;
boolean needsInput = true;
do {
if (needsInput) {
remaining = (int) Math.min(buffer.length, length);
if (remaining != 0) {
data.get(position, buffer, 0, remaining);
MemorySegment.copy(data, position, bufferSegment, 0, remaining);
length -= remaining;
position += remaining;
inflater.setInput(buffer, 0, remaining);
Expand Down
Loading

0 comments on commit 822cb24

Please sign in to comment.