Skip to content

Commit 44cf9b3

Browse files
authored
Merge pull request #22 from NeRdTheNed/new-tests
Bonus zip tests
2 parents 2b6beef + 2eb50a3 commit 44cf9b3

23 files changed

+165
-79
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.1.0</version>
9+
<version>2.1.1</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/CentralDirectoryFileHeader.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,25 +86,25 @@ public CentralDirectoryFileHeader copy() {
8686
@Override
8787
public void read(@Nonnull ByteData data, long offset) {
8888
super.read(data, offset);
89-
versionMadeBy = ByteDataUtil.readLazyWord(data, offset, 4);
90-
versionNeededToExtract = ByteDataUtil.readLazyWord(data, offset, 6);
91-
generalPurposeBitFlag = ByteDataUtil.readLazyWord(data, offset, 8);
92-
compressionMethod = ByteDataUtil.readLazyWord(data, offset, 10);
93-
lastModFileTime = ByteDataUtil.readLazyWord(data, offset, 12);
94-
lastModFileDate = ByteDataUtil.readLazyWord(data, offset, 14);
95-
crc32 = ByteDataUtil.readLazyQuad(data, offset, 16);
96-
compressedSize = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 20);
97-
uncompressedSize = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 24);
98-
fileNameLength = ByteDataUtil.readLazyWord(data, offset, 28);
99-
extraFieldLength = ByteDataUtil.readLazyWord(data, offset, 30);
100-
fileCommentLength = ByteDataUtil.readLazyWord(data, offset, 32);
101-
diskNumberStart = ByteDataUtil.readLazyWord(data, offset, 34);
102-
internalFileAttributes = ByteDataUtil.readLazyWord(data, offset, 36);
103-
externalFileAttributes = ByteDataUtil.readLazyQuad(data, offset, 38);
104-
relativeOffsetOfLocalHeader = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 42);
105-
fileName = ByteDataUtil.readLazySlice(data, offset, new LazyInt(() -> 46), fileNameLength);
106-
extraField = ByteDataUtil.readLazySlice(data, offset, fileNameLength.add(46), extraFieldLength);
107-
fileComment = ByteDataUtil.readLazySlice(data, offset, fileNameLength.add(46).add(extraFieldLength), fileCommentLength);
89+
versionMadeBy = ByteDataUtil.readLazyWord(data, offset, 4).withId("versionMadeBy");
90+
versionNeededToExtract = ByteDataUtil.readLazyWord(data, offset, 6).withId("versionNeededToExtract");
91+
generalPurposeBitFlag = ByteDataUtil.readLazyWord(data, offset, 8).withId("generalPurposeBitFlag");
92+
compressionMethod = ByteDataUtil.readLazyWord(data, offset, 10).withId("compressionMethod");
93+
lastModFileTime = ByteDataUtil.readLazyWord(data, offset, 12).withId("lastModFileTime");
94+
lastModFileDate = ByteDataUtil.readLazyWord(data, offset, 14).withId("lastModFileDate");
95+
crc32 = ByteDataUtil.readLazyQuad(data, offset, 16).withId("crc32");
96+
compressedSize = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 20).withId("compressedSize");
97+
uncompressedSize = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 24).withId("uncompressedSize");
98+
fileNameLength = ByteDataUtil.readLazyWord(data, offset, 28).withId("fileNameLength");
99+
extraFieldLength = ByteDataUtil.readLazyWord(data, offset, 30).withId("extraFieldLength");
100+
fileCommentLength = ByteDataUtil.readLazyWord(data, offset, 32).withId("fileCommentLength");
101+
diskNumberStart = ByteDataUtil.readLazyWord(data, offset, 34).withId("diskNumberStart");
102+
internalFileAttributes = ByteDataUtil.readLazyWord(data, offset, 36).withId("internalFileAttributes");
103+
externalFileAttributes = ByteDataUtil.readLazyQuad(data, offset, 38).withId("externalFileAttributes");
104+
relativeOffsetOfLocalHeader = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 42).withId("relativeOffsetOfLocalHeader");
105+
fileName = ByteDataUtil.readLazySlice(data, offset, new LazyInt(() -> 46), fileNameLength).withId("fileName");
106+
extraField = ByteDataUtil.readLazySlice(data, offset, fileNameLength.add(46), extraFieldLength).withId("extraField");
107+
fileComment = ByteDataUtil.readLazySlice(data, offset, fileNameLength.add(46).add(extraFieldLength), fileCommentLength).withId("fileComment");
108108
}
109109

110110
@Override

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ public void read(@Nonnull ByteData data, long offset) {
7070
// Update the file data ranges
7171
this.relativeDataOffsetStart = relativeDataOffsetStart;
7272
this.relativeDataOffsetEnd = relativeDataOffsetEnd;
73-
fileDataLength = new LazyLong(() -> relativeDataOffsetEnd - relativeDataOffsetStart);
73+
fileDataLength = new LazyLong(() -> relativeDataOffsetEnd - relativeDataOffsetStart).withId("fileDataLength");
74+
7475
fileData = ByteDataUtil.readLazyLongSlice(data, offset,
75-
new LazyLong(() -> relativeDataOffsetStart), fileDataLength);
76+
new LazyLong(() -> relativeDataOffsetStart), fileDataLength).withId("fileData");
7677

7778
// Update sizes where possible
7879
long size = fileDataLength.get();
@@ -89,20 +90,32 @@ public void read(@Nonnull ByteData data, long offset) {
8990
}
9091

9192
@Override
92-
public void link(CentralDirectoryFileHeader directoryFileHeader) {
93-
super.link(directoryFileHeader);
93+
public void adoptLinkedCentralDirectoryValues() {
94+
CentralDirectoryFileHeader directoryFileHeader = linkedDirectoryFileHeader;
95+
if (directoryFileHeader == null)
96+
return;
9497

9598
// JVM trusts central directory file header contents over local
9699
// - Using fields as this maintains the lazy model
97-
compressionMethod = directoryFileHeader.compressionMethod;
98-
compressedSize = directoryFileHeader.compressedSize;
99-
uncompressedSize = directoryFileHeader.uncompressedSize;
100-
fileName = directoryFileHeader.fileName;
101-
generalPurposeBitFlag = directoryFileHeader.generalPurposeBitFlag;
102-
crc32 = directoryFileHeader.crc32;
103-
lastModFileDate = directoryFileHeader.lastModFileDate;
104-
lastModFileTime = directoryFileHeader.lastModFileTime;
100+
versionNeededToExtract = linkedDirectoryFileHeader.versionNeededToExtract;
101+
generalPurposeBitFlag = linkedDirectoryFileHeader.generalPurposeBitFlag;
102+
compressionMethod = linkedDirectoryFileHeader.compressionMethod;
103+
lastModFileTime = linkedDirectoryFileHeader.lastModFileTime;
104+
lastModFileDate = linkedDirectoryFileHeader.lastModFileDate;
105+
crc32 = linkedDirectoryFileHeader.crc32;
106+
fileNameLength = linkedDirectoryFileHeader.fileNameLength;
107+
fileName = linkedDirectoryFileHeader.fileName;
108+
extraField = linkedDirectoryFileHeader.extraField;
105109

110+
// The sizes are not used by the JVM parser.
111+
// It just says 'go until the next header'.
112+
//
113+
// compressedSize = directoryFileHeader.compressedSize;
114+
// uncompressedSize = directoryFileHeader.uncompressedSize;
115+
//
116+
// That being said, we want a fallback if no data is found.
117+
// This may occur if something with offset detection fails.
118+
//
106119
// Update file data with new compressed/uncompressed size if it was not able to be found previously
107120
// with only the local data available.
108121
if (!foundData) {
@@ -120,8 +133,7 @@ public void link(CentralDirectoryFileHeader directoryFileHeader) {
120133
if (fileDataLength < relativeDataOffsetEnd) {
121134
fileData = ByteDataUtil.readLazyLongSlice(data, offset,
122135
new LazyLong(() -> relativeDataOffsetStart - offset),
123-
new LazyLong(() -> fileDataLength));
124-
data = null;
136+
new LazyLong(() -> fileDataLength)).withId("fileData");
125137
}
126138
}
127139
}

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

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
*/
4040
public class LocalFileHeader extends AbstractZipFileHeader {
4141
protected static final int MIN_FIXED_SIZE = 30;
42-
private transient CentralDirectoryFileHeader linkedDirectoryFileHeader;
42+
protected transient CentralDirectoryFileHeader linkedDirectoryFileHeader;
4343

4444
// LocalFileHeader spec (plus common elements between this and central file)
4545
protected LazyByteData fileData;
@@ -76,18 +76,18 @@ public LocalFileHeader copy() {
7676
@Override
7777
public void read(@Nonnull ByteData data, long offset) {
7878
super.read(data, offset);
79-
versionNeededToExtract = ByteDataUtil.readLazyWord(data, offset, 4);
80-
generalPurposeBitFlag = ByteDataUtil.readLazyWord(data, offset, 6);
81-
compressionMethod = ByteDataUtil.readLazyWord(data, offset, 8);
82-
lastModFileTime = ByteDataUtil.readLazyWord(data, offset, 10);
83-
lastModFileDate = ByteDataUtil.readLazyWord(data, offset, 12);
84-
crc32 = ByteDataUtil.readLazyQuad(data, offset, 14);
85-
compressedSize = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 18);
86-
uncompressedSize = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 22);
87-
fileNameLength = ByteDataUtil.readLazyWord(data, offset, 26);
88-
extraFieldLength = ByteDataUtil.readLazyWord(data, offset, 28);
89-
fileName = ByteDataUtil.readLazySlice(data, offset, new LazyInt(() -> MIN_FIXED_SIZE), fileNameLength);
90-
extraField = ByteDataUtil.readLazySlice(data, offset, fileNameLength.add(MIN_FIXED_SIZE), extraFieldLength);
79+
versionNeededToExtract = ByteDataUtil.readLazyWord(data, offset, 4).withId("versionNeededToExtract");
80+
generalPurposeBitFlag = ByteDataUtil.readLazyWord(data, offset, 6).withId("generalPurposeBitFlag");
81+
compressionMethod = ByteDataUtil.readLazyWord(data, offset, 8).withId("compressionMethod");
82+
lastModFileTime = ByteDataUtil.readLazyWord(data, offset, 10).withId("lastModFileTime");
83+
lastModFileDate = ByteDataUtil.readLazyWord(data, offset, 12).withId("lastModFileDate");
84+
crc32 = ByteDataUtil.readLazyQuad(data, offset, 14).withId("crc32");
85+
compressedSize = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 18).withId("compressedSize");
86+
uncompressedSize = ByteDataUtil.readLazyMaskedLongQuad(data, offset, 22).withId("uncompressedSize");
87+
fileNameLength = ByteDataUtil.readLazyWord(data, offset, 26).withId("fileNameLength");
88+
extraFieldLength = ByteDataUtil.readLazyWord(data, offset, 28).withId("extraFieldLength");
89+
fileName = ByteDataUtil.readLazySlice(data, offset, new LazyInt(() -> MIN_FIXED_SIZE), fileNameLength).withId("fileName");
90+
extraField = ByteDataUtil.readLazySlice(data, offset, fileNameLength.add(MIN_FIXED_SIZE), extraFieldLength).withId("extraField");
9191
fileDataLength = new LazyLong(() -> {
9292
long fileDataLength;
9393
if (compressionMethod.get() == STORED) {
@@ -96,8 +96,9 @@ public void read(@Nonnull ByteData data, long offset) {
9696
fileDataLength = compressedSize.get();
9797
}
9898
return fileDataLength;
99-
});
100-
fileData = ByteDataUtil.readLazyLongSlice(data, offset, fileNameLength.add(extraFieldLength).add(MIN_FIXED_SIZE), fileDataLength);
99+
}).withId("fileDataLength");
100+
fileData = ByteDataUtil.readLazyLongSlice(data, offset,
101+
fileNameLength.add(extraFieldLength).add(MIN_FIXED_SIZE), fileDataLength).withId("fileData");
101102
}
102103

103104
/**
@@ -132,18 +133,22 @@ public boolean hasDifferentValuesThanCentralDirectoryHeader() {
132133
* which you may want to adopt.
133134
*/
134135
public void adoptLinkedCentralDirectoryValues() {
135-
if (data != null && linkedDirectoryFileHeader != null) {
136-
setVersionNeededToExtract(linkedDirectoryFileHeader.getVersionNeededToExtract());
137-
setGeneralPurposeBitFlag(linkedDirectoryFileHeader.getGeneralPurposeBitFlag());
138-
setCompressionMethod(linkedDirectoryFileHeader.getCompressionMethod());
139-
setLastModFileTime(linkedDirectoryFileHeader.getLastModFileTime());
140-
setLastModFileDate(linkedDirectoryFileHeader.getLastModFileDate());
141-
setCrc32(linkedDirectoryFileHeader.getCrc32());
142-
setCompressedSize(linkedDirectoryFileHeader.getCompressedSize());
143-
setUncompressedSize(linkedDirectoryFileHeader.getUncompressedSize());
144-
setFileNameLength(linkedDirectoryFileHeader.getFileNameLength());
145-
fileName = ByteDataUtil.readLazySlice(data, offset, new LazyInt(() -> MIN_FIXED_SIZE), fileNameLength);
146-
extraField = ByteDataUtil.readLazySlice(data, offset, fileNameLength.add(MIN_FIXED_SIZE), extraFieldLength);
136+
if (linkedDirectoryFileHeader != null) {
137+
versionNeededToExtract = linkedDirectoryFileHeader.versionNeededToExtract;
138+
generalPurposeBitFlag = linkedDirectoryFileHeader.generalPurposeBitFlag;
139+
compressionMethod = linkedDirectoryFileHeader.compressionMethod;
140+
lastModFileTime = linkedDirectoryFileHeader.lastModFileTime;
141+
lastModFileDate = linkedDirectoryFileHeader.lastModFileDate;
142+
crc32 = linkedDirectoryFileHeader.crc32;
143+
compressedSize = linkedDirectoryFileHeader.compressedSize;
144+
uncompressedSize = linkedDirectoryFileHeader.uncompressedSize;
145+
fileNameLength = linkedDirectoryFileHeader.fileNameLength;
146+
fileName = linkedDirectoryFileHeader.fileName;
147+
extraField = linkedDirectoryFileHeader.extraField;
148+
// We're using the same slices/data locations from the central directory.
149+
// If we wanted to use local data but with updated offsets from the central directory it would look like this:
150+
// fileName = ByteDataUtil.readLazySlice(data, offset, new LazyInt(() -> MIN_FIXED_SIZE), fileNameLength).withId("fileName");
151+
// extraField = ByteDataUtil.readLazySlice(data, offset, fileNameLength.add(MIN_FIXED_SIZE), extraFieldLength).withId("extraField");
147152
fileDataLength = new LazyLong(() -> {
148153
long fileDataLength;
149154
if (compressionMethod.get() == STORED) {
@@ -152,8 +157,9 @@ public void adoptLinkedCentralDirectoryValues() {
152157
fileDataLength = compressedSize.get();
153158
}
154159
return fileDataLength;
155-
});
156-
fileData = ByteDataUtil.readLazyLongSlice(data, offset, fileNameLength.add(extraFieldLength).add(30), fileDataLength);
160+
}).withId("fileDataLength");
161+
if (data != null)
162+
fileData = ByteDataUtil.readLazyLongSlice(data, offset, fileNameLength.add(extraFieldLength).add(MIN_FIXED_SIZE), fileDataLength).withId("fileData");
157163
}
158164
}
159165

@@ -163,7 +169,7 @@ public void adoptLinkedCentralDirectoryValues() {
163169
* @param endOffset New file data length.
164170
*/
165171
public void setFileDataEndOffset(long endOffset) {
166-
long fileDataStartOffset = offset + fileNameLength.add(extraFieldLength).add(30).get();
172+
long fileDataStartOffset = offset + fileNameLength.add(extraFieldLength).add(MIN_FIXED_SIZE).get();
167173
long length = endOffset - fileDataStartOffset;
168174
setFileDataLength(length);
169175
}
@@ -173,15 +179,15 @@ public void setFileDataEndOffset(long endOffset) {
173179
*/
174180
public void setFileDataLength(long newLength) {
175181
fileDataLength.set(newLength);
176-
fileData = ByteDataUtil.readLazyLongSlice(data, offset, fileNameLength.add(extraFieldLength).add(30), newLength);
182+
fileData = ByteDataUtil.readLazyLongSlice(data, offset, fileNameLength.add(extraFieldLength).add(MIN_FIXED_SIZE), newLength).withId("fileData");
177183
}
178184

179185
/**
180186
* @param newLength New file data length.
181187
*/
182188
public void setFileDataLength(@Nonnull LazyLong newLength) {
183189
fileDataLength = newLength;
184-
fileData = ByteDataUtil.readLazyLongSlice(data, offset, fileNameLength.add(extraFieldLength).add(30), newLength);
190+
fileData = ByteDataUtil.readLazyLongSlice(data, offset, fileNameLength.add(extraFieldLength).add(MIN_FIXED_SIZE), newLength).withId("fileData");
185191
}
186192

187193
@Override

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ else if (data.getInt(jvmBaseFileOffset) == ZipPatterns.CENTRAL_DIRECTORY_FILE_HE
144144
// Add the earliest central directory offset, which serves as the upper bound to search against for the
145145
// last local file header entry's file data contents.
146146
entryOffsets.add(earliestCdfh);
147+
// Add the end of central directory
148+
entryOffsets.add(endOfCentralDirectoryOffset);
147149

148150
// Create the local file entries
149151
for (CentralDirectoryFileHeader directory : zip.getCentralDirectories()) {
@@ -152,12 +154,12 @@ else if (data.getInt(jvmBaseFileOffset) == ZipPatterns.CENTRAL_DIRECTORY_FILE_HE
152154
if (!offsets.contains(offset) && data.getInt(offset) == ZipPatterns.LOCAL_FILE_HEADER_QUAD) {
153155
try {
154156
LocalFileHeader file = newLocalFileHeader();
155-
directory.link(file);
156-
file.link(directory);
157157
if (file instanceof JvmLocalFileHeader) {
158158
((JvmLocalFileHeader) file).setOffsets(entryOffsets);
159159
}
160160
file.read(data, offset);
161+
directory.link(file);
162+
file.link(directory);
161163
file.adoptLinkedCentralDirectoryValues();
162164
zip.addPart(file);
163165
postProcessLocalFileHeader(file);

src/main/java/software/coley/lljzip/util/UnsafeMappedFile.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ private long validate(long position) {
150150
}
151151
position += address;
152152
if (position > end) {
153-
throw new IllegalArgumentException(Long.toString(position));
153+
long diff = position - end;
154+
throw new IllegalArgumentException("positon beyond max bounds: " + position + " > " + end + " diff: " + diff);
154155
}
155156
return position;
156157
}

src/main/java/software/coley/lljzip/util/lazy/Lazy.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,23 @@
1111
public abstract class Lazy<S> {
1212
protected final S lookup;
1313
protected boolean set;
14+
protected String id = "";
1415

1516
public Lazy(@Nonnull S lookup) {
1617
this.lookup = lookup;
1718
}
19+
20+
/**
21+
* @param id
22+
* Value id.
23+
* @param <L>
24+
* Self type.
25+
*
26+
* @return Self.
27+
*/
28+
@SuppressWarnings("unchecked")
29+
public <L extends Lazy<S>> L withId(String id) {
30+
this.id = id;
31+
return (L) this;
32+
}
1833
}

src/main/java/software/coley/lljzip/util/lazy/LazyByteData.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public LazyByteData(@Nonnull Supplier<ByteData> lookup) {
2626
@Nonnull
2727
public LazyByteData copy() {
2828
LazyByteData copy = new LazyByteData(lookup);
29+
copy.id = id;
2930
if (set) copy.set(value);
3031
return copy;
3132
}
@@ -53,7 +54,7 @@ public ByteData get() {
5354

5455
@Override
5556
public String toString() {
56-
return "data[" + get().length() + "]";
57+
return id + " data[" + get().length() + "]";
5758
}
5859

5960
@Override

src/main/java/software/coley/lljzip/util/lazy/LazyInt.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public LazyInt(@Nonnull IntSupplier lookup) {
2323
@Nonnull
2424
public LazyInt copy() {
2525
LazyInt copy = new LazyInt(lookup);
26+
copy.id = id;
2627
if (set) copy.set(value);
2728
return copy;
2829
}
@@ -69,7 +70,7 @@ public int get() {
6970

7071
@Override
7172
public String toString() {
72-
return Integer.toString(get());
73+
return id + " " + get();
7374
}
7475

7576
@Override

src/main/java/software/coley/lljzip/util/lazy/LazyLong.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public LazyLong(@Nonnull LongSupplier lookup) {
2323
@Nonnull
2424
public LazyLong copy() {
2525
LazyLong copy = new LazyLong(lookup);
26+
copy.id = id;
2627
if (set) copy.set(value);
2728
return copy;
2829
}
@@ -49,7 +50,7 @@ public long get() {
4950

5051
@Override
5152
public String toString() {
52-
return Long.toString(get());
53+
return id + " " + get();
5354
}
5455

5556
@Override

0 commit comments

Comments
 (0)