Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump minimum required JDK to 22, migrate API to MemorySegment #28

Merged
merged 9 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading