Skip to content

Commit 4d38c8a

Browse files
committed
Switch to byte buffers
1 parent 3de5f13 commit 4d38c8a

22 files changed

+624
-318
lines changed

src/main/java/software/coley/llzip/ZipCompressions.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import software.coley.llzip.strategy.DeflateDecompressor;
55

66
import java.io.IOException;
7+
import java.nio.ByteBuffer;
78

89
/**
910
* Constants for {@link LocalFileHeader#getCompressionMethod()}.
@@ -202,7 +203,7 @@ static String getName(int method) {
202203
* @throws IOException
203204
* When the decompression failed.
204205
*/
205-
static byte[] decompress(LocalFileHeader header) throws IOException {
206+
static ByteBuffer decompress(LocalFileHeader header) throws IOException {
206207
int method = header.getCompressionMethod();
207208
switch (method) {
208209
case STRORED:

src/main/java/software/coley/llzip/ZipIO.java

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,36 @@
44
import software.coley.llzip.strategy.JvmZipReaderStrategy;
55
import software.coley.llzip.strategy.ZipReaderStrategy;
66

7+
import java.io.FileNotFoundException;
78
import java.io.IOException;
9+
import java.nio.ByteBuffer;
10+
import java.nio.ByteOrder;
11+
import java.nio.channels.FileChannel;
12+
import java.nio.file.Files;
13+
import java.nio.file.Path;
14+
import java.nio.file.StandardOpenOption;
815

916
/**
1017
* IO wrappers for reading {@link ZipArchive} contents.
1118
*
1219
* @author Matt Coley
1320
*/
1421
public class ZipIO {
22+
/**
23+
* Creates an archive using the {@link DefaultZipReaderStrategy}.
24+
*
25+
* @param data
26+
* Zip bytes.
27+
*
28+
* @return Archive from bytes.
29+
*
30+
* @throws IOException
31+
* When the archive bytes cannot be read from, usually indicating a malformed zip.
32+
*/
33+
public static ZipArchive readStandard(ByteBuffer data) throws IOException {
34+
return read(data, new DefaultZipReaderStrategy());
35+
}
36+
1537
/**
1638
* Creates an archive using the {@link DefaultZipReaderStrategy}.
1739
*
@@ -26,6 +48,36 @@ public class ZipIO {
2648
public static ZipArchive readStandard(byte[] data) throws IOException {
2749
return read(data, new DefaultZipReaderStrategy());
2850
}
51+
/**
52+
* Creates an archive using the {@link DefaultZipReaderStrategy}.
53+
*
54+
* @param data
55+
* Zip path.
56+
*
57+
* @return Archive from bytes.
58+
*
59+
* @throws IOException
60+
* When the archive bytes cannot be read from, usually indicating a malformed zip.
61+
*/
62+
public static ZipArchive readStandard(Path data) throws IOException {
63+
return read(data, new DefaultZipReaderStrategy());
64+
}
65+
66+
/**
67+
* Creates an archive using the {@link JvmZipReaderStrategy} which handles some edge cases not usually
68+
* expected from zip files.
69+
*
70+
* @param data
71+
* Zip bytes.
72+
*
73+
* @return Archive from bytes.
74+
*
75+
* @throws IOException
76+
* When the archive bytes cannot be read from, usually indicating a malformed zip.
77+
*/
78+
public static ZipArchive readJvm(ByteBuffer data) throws IOException {
79+
return read(data, new JvmZipReaderStrategy());
80+
}
2981

3082
/**
3183
* Creates an archive using the {@link JvmZipReaderStrategy} which handles some edge cases not usually
@@ -43,6 +95,22 @@ public static ZipArchive readJvm(byte[] data) throws IOException {
4395
return read(data, new JvmZipReaderStrategy());
4496
}
4597

98+
/**
99+
* Creates an archive using the {@link JvmZipReaderStrategy} which handles some edge cases not usually
100+
* expected from zip files.
101+
*
102+
* @param path
103+
* Zip path.
104+
*
105+
* @return Archive from bytes.
106+
*
107+
* @throws IOException
108+
* When the archive bytes cannot be read from, usually indicating a malformed zip.
109+
*/
110+
public static ZipArchive readJvm(Path path) throws IOException {
111+
return read(path, new JvmZipReaderStrategy());
112+
}
113+
46114
/**
47115
* @param data
48116
* Zip bytes.
@@ -54,15 +122,55 @@ public static ZipArchive readJvm(byte[] data) throws IOException {
54122
* @throws IOException
55123
* When the archive bytes cannot be read from, usually indicating a malformed zip.
56124
*/
57-
public static ZipArchive read(byte[] data, ZipReaderStrategy strategy) throws IOException {
125+
public static ZipArchive read(ByteBuffer data, ZipReaderStrategy strategy) throws IOException {
58126
if (data == null)
59127
throw new IOException("Data is null!");
60128
// The fixed size elements of a CDFH is 22 bytes (plus the variable size bits which can be 0)
61-
if (data.length < 22)
129+
if (data.remaining() < 22)
62130
throw new IOException("Not enough bytes to read Central-Directory-File-Header, minimum=22");
63131
// Create instance
64132
ZipArchive zip = new ZipArchive();
65133
strategy.read(zip, data);
66134
return zip;
67135
}
136+
137+
/**
138+
* @param data
139+
* Zip bytes.
140+
* @param strategy
141+
* Zip reader implementation.
142+
*
143+
* @return Archive from bytes.
144+
*
145+
* @throws IOException
146+
* When the archive bytes cannot be read from, usually indicating a malformed zip.
147+
*/
148+
public static ZipArchive read(byte[] data, ZipReaderStrategy strategy) throws IOException {
149+
if (data == null)
150+
throw new IOException("Data is null!");
151+
return read(ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN), strategy);
152+
}
153+
154+
/**
155+
* @param path
156+
* Zip path.
157+
* @param strategy
158+
* Zip reader implementation.
159+
*
160+
* @return Archive from bytes.
161+
*
162+
* @throws IOException
163+
* When the archive bytes cannot be read from, usually indicating a malformed zip.
164+
*/
165+
public static ZipArchive read(Path path, ZipReaderStrategy strategy) throws IOException {
166+
if (path == null)
167+
throw new IOException("Data is null!");
168+
if (!Files.isRegularFile(path))
169+
throw new FileNotFoundException(path.toString());
170+
ByteBuffer buffer;
171+
try (FileChannel fc = FileChannel.open(path, StandardOpenOption.READ)) {
172+
buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0L, fc.size());
173+
}
174+
return read(buffer.order(ByteOrder.LITTLE_ENDIAN), strategy);
175+
}
68176
}

src/main/java/software/coley/llzip/ZipPatterns.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import software.coley.llzip.part.CentralDirectoryFileHeader;
44
import software.coley.llzip.part.EndOfCentralDirectory;
55
import software.coley.llzip.part.LocalFileHeader;
6-
import software.coley.llzip.util.Array;
6+
import software.coley.llzip.util.Buffers;
77

88
/**
9-
* Patterns for usage in {@link Array} methods.
9+
* Patterns for usage in {@link Buffers} methods.
1010
*
1111
* @author Matt Coley
1212
*/

src/main/java/software/coley/llzip/part/CentralDirectoryFileHeader.java

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import software.coley.llzip.ZipCompressions;
44
import software.coley.llzip.strategy.Decompressor;
5-
import software.coley.llzip.util.Array;
5+
import software.coley.llzip.util.Buffers;
66

7-
import java.util.Arrays;
7+
import java.nio.ByteBuffer;
88
import java.util.Objects;
99

1010
/**
@@ -32,37 +32,40 @@ public class CentralDirectoryFileHeader implements ZipPart, ZipRead {
3232
private int internalFileAttributes;
3333
private int externalFileAttributes;
3434
private int relativeOffsetOfLocalHeader;
35-
private String fileName;
36-
private byte[] extraField;
37-
private String fileComment;
35+
private ByteBuffer fileName;
36+
private ByteBuffer extraField;
37+
private ByteBuffer fileComment;
38+
39+
private transient String fileNameCache;
40+
private transient String fileCommentCache;
3841

3942
@Override
40-
public void read(byte[] data, int offset) {
43+
public void read(ByteBuffer data, int offset) {
4144
this.offset = offset;
42-
versionMadeBy = Array.readWord(data, offset + 4);
43-
versionNeededToExtract = Array.readWord(data, offset + 6);
44-
generalPurposeBitFlag = Array.readWord(data, offset + 8);
45-
compressionMethod = Array.readWord(data, offset + 10);
46-
lastModFileTime = Array.readWord(data, offset + 12);
47-
lastModFileDate = Array.readWord(data, offset + 14);
48-
crc32 = Array.readQuad(data, offset + 16);
49-
compressedSize = Array.readQuad(data, offset + 20);
50-
uncompressedSize = Array.readQuad(data, offset + 24);
51-
fileNameLength = Array.readWord(data, offset + 28);
52-
extraFieldLength = Array.readWord(data, offset + 30);
53-
fileCommentLength = Array.readWord(data, offset + 32);
54-
diskNumberStart = Array.readWord(data, offset + 34);
55-
internalFileAttributes = Array.readWord(data, offset + 36);
56-
externalFileAttributes = Array.readQuad(data, offset + 38);
57-
relativeOffsetOfLocalHeader = Array.readQuad(data, offset + 42);
58-
fileName = Array.readString(data, offset + 46, fileNameLength);
59-
extraField = Array.readArray(data, offset + 46 + fileNameLength, extraFieldLength);
60-
fileComment = Array.readString(data, offset + 46 + fileNameLength + extraFieldLength, fileCommentLength);
45+
versionMadeBy = Buffers.readWord(data, offset + 4);
46+
versionNeededToExtract = Buffers.readWord(data, offset + 6);
47+
generalPurposeBitFlag = Buffers.readWord(data, offset + 8);
48+
compressionMethod = Buffers.readWord(data, offset + 10);
49+
lastModFileTime = Buffers.readWord(data, offset + 12);
50+
lastModFileDate = Buffers.readWord(data, offset + 14);
51+
crc32 = Buffers.readQuad(data, offset + 16);
52+
compressedSize = Buffers.readQuad(data, offset + 20);
53+
uncompressedSize = Buffers.readQuad(data, offset + 24);
54+
fileNameLength = Buffers.readWord(data, offset + 28);
55+
extraFieldLength = Buffers.readWord(data, offset + 30);
56+
fileCommentLength = Buffers.readWord(data, offset + 32);
57+
diskNumberStart = Buffers.readWord(data, offset + 34);
58+
internalFileAttributes = Buffers.readWord(data, offset + 36);
59+
externalFileAttributes = Buffers.readQuad(data, offset + 38);
60+
relativeOffsetOfLocalHeader = Buffers.readQuad(data, offset + 42);
61+
fileName = Buffers.slice(data, offset + 46, fileNameLength);
62+
extraField = Buffers.slice(data, offset + 46 + fileNameLength, extraFieldLength);
63+
fileComment = Buffers.slice(data, offset + 46 + fileNameLength + extraFieldLength, fileCommentLength);
6164
}
6265

6366
@Override
6467
public int length() {
65-
return 46 + fileName.length() + extraField.length + fileComment.length();
68+
return 46 + Buffers.length(fileName) + Buffers.length(extraField) + Buffers.length(fileComment);
6669
}
6770

6871
@Override
@@ -363,49 +366,74 @@ public void setRelativeOffsetOfLocalHeader(int relativeOffsetOfLocalHeader) {
363366
*
364367
* @return File name.
365368
*/
366-
public String getFileName() {
369+
public ByteBuffer getFileName() {
367370
return fileName;
368371
}
369372

370373
/**
371374
* @param fileName
372375
* File name.
373376
*/
374-
public void setFileName(String fileName) {
377+
public void setFileName(ByteBuffer fileName) {
375378
this.fileName = fileName;
376379
}
377380

381+
/**
382+
* Should match {@link CentralDirectoryFileHeader#getFileName()} but is not a strict requirement.
383+
* If they do not match, the central directory file name should be trusted instead.
384+
*
385+
* @return File name.
386+
*/
387+
public String getFileNameAsString() {
388+
String fileNameCache = this.fileNameCache;
389+
if (fileNameCache == null) {
390+
return this.fileNameCache = Buffers.toString(fileName);
391+
}
392+
return fileNameCache;
393+
}
394+
378395
/**
379396
* @return May be used for extra compression information,
380397
* depending on the {@link #getCompressionMethod() compression method} used.
381398
*/
382-
public byte[] getExtraField() {
399+
public ByteBuffer getExtraField() {
383400
return extraField;
384401
}
385402

386403
/**
387404
* @param extraField
388405
* Extra field bytes.
389406
*/
390-
public void setExtraField(byte[] extraField) {
407+
public void setExtraField(ByteBuffer extraField) {
391408
this.extraField = extraField;
392409
}
393410

394411
/**
395412
* @return File comment.
396413
*/
397-
public String getFileComment() {
414+
public ByteBuffer getFileComment() {
398415
return fileComment;
399416
}
400417

401418
/**
402419
* @param fileComment
403420
* File comment.
404421
*/
405-
public void setFileComment(String fileComment) {
422+
public void setFileComment(ByteBuffer fileComment) {
406423
this.fileComment = fileComment;
407424
}
408425

426+
/**
427+
* @return File comment.
428+
*/
429+
public String getFileCommentAsString() {
430+
String fileCommentCache = this.fileCommentCache;
431+
if (fileCommentCache == null) {
432+
return this.fileCommentCache = Buffers.toString(fileComment);
433+
}
434+
return fileCommentCache;
435+
}
436+
409437
@Override
410438
public String toString() {
411439
return "CentralDirectoryFileHeader{" +
@@ -426,7 +454,7 @@ public String toString() {
426454
", externalFileAttributes=" + externalFileAttributes +
427455
", relativeOffsetOfLocalHeader=" + relativeOffsetOfLocalHeader +
428456
", fileName='" + fileName + '\'' +
429-
", extraField=" + Arrays.toString(extraField) +
457+
", extraField=" + Buffers.toString(extraField) +
430458
", fileComment='" + fileComment + '\'' +
431459
'}';
432460
}
@@ -455,7 +483,7 @@ public boolean equals(Object o) {
455483
relativeOffsetOfLocalHeader == that.relativeOffsetOfLocalHeader &&
456484
Objects.equals(linkedFileHeader, that.linkedFileHeader) &&
457485
fileName.equals(that.fileName) &&
458-
Arrays.equals(extraField, that.extraField) &&
486+
Buffers.equals(extraField, that.extraField) &&
459487
fileComment.equals(that.fileComment);
460488
}
461489

@@ -465,7 +493,7 @@ public int hashCode() {
465493
compressionMethod, lastModFileTime, lastModFileDate, crc32, compressedSize, uncompressedSize,
466494
fileNameLength, extraFieldLength, fileCommentLength, diskNumberStart, internalFileAttributes,
467495
externalFileAttributes, relativeOffsetOfLocalHeader, fileName, fileComment);
468-
result = 31 * result + Arrays.hashCode(extraField);
496+
result = 31 * result + extraField.hashCode();
469497
return result;
470498
}
471499
}

0 commit comments

Comments
 (0)