Skip to content

Commit 93f5065

Browse files
committed
Allow toggling of skipping N-to-1 CEN-to-LOC entry mapping in JvmZipReader, defaulting to true
1 parent d601ec2 commit 93f5065

File tree

3 files changed

+91
-19
lines changed

3 files changed

+91
-19
lines changed

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

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,38 @@
2626
*/
2727
public class JvmZipReader extends AbstractZipReader {
2828
private static final Logger logger = LoggerFactory.getLogger(JvmZipReader.class);
29+
private final boolean skipRevisitedCenToLocalLinks;
2930

3031
/**
3132
* New reader with jvm allocator.
3233
*/
3334
public JvmZipReader() {
34-
this(new JvmZipPartAllocator());
35+
this(new JvmZipPartAllocator(), true);
36+
}
37+
38+
/**
39+
* New reader with jvm allocator.
40+
*
41+
* @param skipRevisitedCenToLocalLinks
42+
* Flag to skip creating duplicate {@link LocalFileHeader} entries if multiple
43+
* {@link CentralDirectoryFileHeader} point to the same location.
44+
*/
45+
public JvmZipReader(boolean skipRevisitedCenToLocalLinks) {
46+
this(new JvmZipPartAllocator(), skipRevisitedCenToLocalLinks);
3547
}
3648

3749
/**
3850
* New reader with given allocator.
3951
*
4052
* @param allocator
4153
* Allocator to use.
54+
* @param skipRevisitedCenToLocalLinks
55+
* Flag to skip creating duplicate {@link LocalFileHeader} entries if multiple
56+
* {@link CentralDirectoryFileHeader} point to the same location.
4257
*/
43-
public JvmZipReader(@Nonnull ZipPartAllocator allocator) {
58+
public JvmZipReader(@Nonnull ZipPartAllocator allocator, boolean skipRevisitedCenToLocalLinks) {
4459
super(allocator);
60+
this.skipRevisitedCenToLocalLinks = skipRevisitedCenToLocalLinks;
4561
}
4662

4763
@Override
@@ -159,24 +175,31 @@ else if (data.getInt(jvmBaseFileOffset) == ZipPatterns.CENTRAL_DIRECTORY_FILE_HE
159175
for (CentralDirectoryFileHeader directory : zip.getCentralDirectories()) {
160176
long relative = directory.getRelativeOffsetOfLocalHeader();
161177
long offset = jvmBaseFileOffset + relative;
162-
if (!offsets.contains(offset) && data.getInt(offset) == ZipPatterns.LOCAL_FILE_HEADER_QUAD) {
163-
try {
164-
LocalFileHeader file = newLocalFileHeader();
165-
if (file instanceof JvmLocalFileHeader) {
166-
((JvmLocalFileHeader) file).setOffsets(entryOffsets);
167-
}
168-
file.read(data, offset);
169-
directory.link(file);
170-
file.link(directory);
171-
file.adoptLinkedCentralDirectoryValues();
172-
zip.addPart(file);
173-
postProcessLocalFileHeader(file);
174-
offsets.add(offset);
175-
} catch (Exception ex) {
176-
logger.warn("Failed to read 'local file header' at offset[{}]", offset, ex);
177-
}
178-
} else {
178+
boolean isNewOffset = offsets.add(offset);
179+
if (!isNewOffset) {
180+
logger.warn("Central-Directory-File-Header's offset[{}] was already visited", offset);
181+
if (skipRevisitedCenToLocalLinks)
182+
continue;
183+
}
184+
185+
if (data.getInt(offset) != ZipPatterns.LOCAL_FILE_HEADER_QUAD) {
179186
logger.warn("Central-Directory-File-Header's offset[{}] to Local-File-Header does not match the Local-File-Header magic!", offset);
187+
continue;
188+
}
189+
190+
try {
191+
LocalFileHeader file = newLocalFileHeader();
192+
if (file instanceof JvmLocalFileHeader) {
193+
((JvmLocalFileHeader) file).setOffsets(entryOffsets);
194+
}
195+
file.read(data, offset);
196+
directory.link(file);
197+
file.link(directory);
198+
file.adoptLinkedCentralDirectoryValues();
199+
zip.addPart(file);
200+
postProcessLocalFileHeader(file);
201+
} catch (Exception ex) {
202+
logger.warn("Failed to read 'local file header' at offset[{}]", offset, ex);
180203
}
181204
}
182205

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package software.coley.lljzip;
2+
3+
import org.junit.jupiter.params.ParameterizedTest;
4+
import org.junit.jupiter.params.provider.ValueSource;
5+
import software.coley.lljzip.format.model.AbstractZipFileHeader;
6+
import software.coley.lljzip.format.model.ZipArchive;
7+
import software.coley.lljzip.format.read.JvmZipReader;
8+
9+
import java.nio.file.Path;
10+
import java.nio.file.Paths;
11+
import java.util.Set;
12+
import java.util.TreeSet;
13+
import java.util.stream.Collectors;
14+
import java.util.zip.ZipFile;
15+
16+
import static org.junit.jupiter.api.Assertions.*;
17+
18+
/**
19+
* Some edge case tests where {@link JvmZipReader} needs to match a few quirky cases from {@link ZipFile}.
20+
*
21+
* @author Matt Coley
22+
*/
23+
public class JvmVsZipFileEqualityEdgeCaseTests {
24+
25+
@ParameterizedTest
26+
@ValueSource(strings = {
27+
"resource-pack-trick-header-N-to-1-cen-to-loc-mapping.zip",
28+
})
29+
public void test(String name) {
30+
Path path = Paths.get("src/test/resources/" + name);
31+
32+
try {
33+
ZipArchive zipJvm = ZipIO.read(path, new JvmZipReader(false));
34+
ZipArchive zipAdapting = ZipIO.readAdaptingIO(path);
35+
int sizeDel = zipAdapting.getLocalFiles().size();
36+
int sizeJvm = zipJvm.getLocalFiles().size();
37+
Set<String> namesAdapting = zipAdapting.getLocalFiles().stream().map(AbstractZipFileHeader::getFileNameAsString).collect(Collectors.toCollection(TreeSet::new));
38+
Set<String> namesJvm = zipJvm.getLocalFiles().stream().map(AbstractZipFileHeader::getFileNameAsString).collect(Collectors.toCollection(TreeSet::new));
39+
Set<String> namesDifference = new TreeSet<>(namesAdapting);
40+
namesDifference.removeAll(namesJvm);
41+
42+
// Both files should have the same number of local file headers
43+
assertEquals(0, namesDifference.size());
44+
assertEquals(sizeDel, sizeJvm);
45+
} catch (Exception ex) {
46+
ex.printStackTrace();
47+
}
48+
}
49+
}
Binary file not shown.

0 commit comments

Comments
 (0)