Skip to content

Commit 5595347

Browse files
committed
Support tracking prefix data in front of archives
1 parent 6046eb9 commit 5595347

File tree

5 files changed

+51
-9
lines changed

5 files changed

+51
-9
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>software.coley</groupId>
88
<artifactId>lljzip</artifactId>
9-
<version>2.4.1</version>
9+
<version>2.5.0</version>
1010

1111
<name>LL Java ZIP</name>
1212
<description>Lower level ZIP support for Java</description>

src/main/java/software/coley/lljzip/format/model/ZipArchive.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package software.coley.lljzip.format.model;
22

3+
import software.coley.lljzip.format.read.ZipReader;
34
import software.coley.lljzip.format.transform.ZipPartMapper;
45
import software.coley.lljzip.util.OffsetComparator;
56

67
import javax.annotation.Nonnull;
78
import javax.annotation.Nullable;
89
import java.io.Closeable;
910
import java.io.IOException;
11+
import java.lang.foreign.MemorySegment;
1012
import java.util.*;
1113
import java.util.function.Predicate;
1214
import java.util.stream.Collectors;
@@ -19,6 +21,7 @@
1921
public class ZipArchive implements AutoCloseable, Iterable<ZipPart> {
2022
private final List<ZipPart> parts = new ArrayList<>();
2123
private final Closeable closableBackingResource;
24+
private MemorySegment prefixData;
2225

2326
/**
2427
* New zip archive without any backing resource.
@@ -37,6 +40,28 @@ public ZipArchive(@Nonnull Closeable closableBackingResource) {
3740
this.closableBackingResource = closableBackingResource;
3841
}
3942

43+
/**
44+
* If the {@link ZipReader} used to read this archive supports tracking such information this will return
45+
* any data not contained in the archive that was found at the front of the input content.
46+
* <p/>
47+
* Some applications like Jar2Exe will prepend a loader executable in front of the jar, in which case this content
48+
* would be that executable. It can be any kind of arbitrary data though.
49+
*
50+
* @return Data at the front of the archive.
51+
*/
52+
@Nullable
53+
public MemorySegment getPrefixData() {
54+
return prefixData;
55+
}
56+
57+
/**
58+
* @param prefixData
59+
* Data at the front of the archive.
60+
*/
61+
public void setPrefixData(@Nullable MemorySegment prefixData) {
62+
this.prefixData = prefixData;
63+
}
64+
4065
/**
4166
* @param mapper
4267
* Part mapper to manipulate contained zip parts.

src/main/java/software/coley/lljzip/format/read/ForwardScanZipReader.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
import javax.annotation.Nonnull;
1414
import java.io.IOException;
1515
import java.lang.foreign.MemorySegment;
16-
import java.util.HashSet;
17-
import java.util.Set;
16+
import java.util.*;
1817

1918
/**
2019
* The standard read strategy that should work with standard zip archives.
@@ -67,7 +66,7 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO
6766

6867
// Read local files
6968
// - Set to prevent duplicate file header entries for the same offset
70-
Set<Long> offsets = new HashSet<>();
69+
NavigableSet<Long> offsets = new TreeSet<>();
7170
for (CentralDirectoryFileHeader directory : zip.getCentralDirectories()) {
7271
long offset = zipStart + directory.getRelativeOffsetOfLocalHeader();
7372
if (!offsets.contains(offset) && MemorySegmentUtil.readQuad(data, offset) == ZipPatterns.LOCAL_FILE_HEADER_QUAD) {
@@ -83,6 +82,12 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO
8382
}
8483
}
8584

85+
// Record any data appearing at the front of the file not associated with the ZIP file contents.
86+
if (!offsets.isEmpty()) {
87+
long firstOffset = offsets.first();
88+
zip.setPrefixData(data.asSlice(0, firstOffset));
89+
}
90+
8691
// Sort based on order
8792
zip.sortParts(new OffsetComparator());
8893
}

src/main/java/software/coley/lljzip/format/read/JvmZipReader.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public JvmZipReader() {
4040
*
4141
* @param skipRevisitedCenToLocalLinks
4242
* Flag to skip creating duplicate {@link LocalFileHeader} entries if multiple
43-
* {@link CentralDirectoryFileHeader} point to the same location.
43+
* {@link CentralDirectoryFileHeader} point to the same location.
4444
*/
4545
public JvmZipReader(boolean skipRevisitedCenToLocalLinks) {
4646
this(new JvmZipPartAllocator(), skipRevisitedCenToLocalLinks);
@@ -53,7 +53,7 @@ public JvmZipReader(boolean skipRevisitedCenToLocalLinks) {
5353
* Allocator to use.
5454
* @param skipRevisitedCenToLocalLinks
5555
* Flag to skip creating duplicate {@link LocalFileHeader} entries if multiple
56-
* {@link CentralDirectoryFileHeader} point to the same location.
56+
* {@link CentralDirectoryFileHeader} point to the same location.
5757
*/
5858
public JvmZipReader(@Nonnull ZipPartAllocator allocator, boolean skipRevisitedCenToLocalLinks) {
5959
super(allocator);
@@ -203,6 +203,12 @@ else if (MemorySegmentUtil.readQuad(data, jvmBaseFileOffset) == ZipPatterns.CENT
203203
}
204204
}
205205

206+
// Record any data appearing at the front of the file not associated with the ZIP file contents.
207+
if (!entryOffsets.isEmpty()) {
208+
long firstOffset = entryOffsets.first();
209+
zip.setPrefixData(data.asSlice(0, firstOffset));
210+
}
211+
206212
// Sort based on order
207213
zip.sortParts(new OffsetComparator());
208214
}

src/main/java/software/coley/lljzip/format/read/NaiveLocalFileZipReader.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,18 @@ public NaiveLocalFileZipReader(@Nonnull ZipPartAllocator allocator) {
3434

3535
@Override
3636
public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IOException {
37-
long localFileOffset = -1;
38-
while ((localFileOffset = MemorySegmentUtil.indexOfQuad(data, localFileOffset + 1, ZipPatterns.LOCAL_FILE_HEADER_QUAD)) >= 0) {
37+
long localFileOffset = MemorySegmentUtil.indexOfQuad(data, 0, ZipPatterns.LOCAL_FILE_HEADER_QUAD);
38+
if (localFileOffset < 0) return;
39+
if (localFileOffset > 0) {
40+
// The first offset containing archive data is not at the first byte.
41+
// Record whatever content is at the front.
42+
zip.setPrefixData(data.asSlice(0, localFileOffset));
43+
}
44+
do {
3945
LocalFileHeader file = newLocalFileHeader();
4046
file.read(data, localFileOffset);
4147
zip.addPart(file);
4248
postProcessLocalFileHeader(file);
43-
}
49+
} while ((localFileOffset = MemorySegmentUtil.indexOfQuad(data, localFileOffset + 1, ZipPatterns.LOCAL_FILE_HEADER_QUAD)) >= 0);
4450
}
4551
}

0 commit comments

Comments
 (0)