Transpiled version (821L) is out of date.
1 | // extended copy of java.util.zip.ZipOutputStream |
2 | // that can store compressed files directly |
3 | |
4 | import static java.util.zip.ZipConstants64.*; |
5 | import static java.util.zip.ZipUtils.*; |
6 | import sun.nio.cs.UTF_8; |
7 | import sun.security.action.GetPropertyAction; |
8 | |
9 | sclass ZipOutputStreamWithRawCopying extends DeflaterOutputStream is ZipConstants { |
10 | |
11 | /** |
12 | * Whether to use ZIP64 for zip files with more than 64k entries. |
13 | * Until ZIP64 support in zip implementations is ubiquitous, this |
14 | * system property allows the creation of zip files which can be |
15 | * read by legacy zip implementations which tolerate "incorrect" |
16 | * total entry count fields, such as the ones in jdk6, and even |
17 | * some in jdk7. |
18 | */ |
19 | private static final boolean inhibitZip64 = |
20 | Boolean.parseBoolean( |
21 | GetPropertyAction.privilegedGetProperty("jdk.util.zip.inhibitZip64")); |
22 | |
23 | private static class XEntry { |
24 | final ZipEntry entry; |
25 | final long offset; |
26 | public XEntry(ZipEntry entry, long offset) { |
27 | this.entry = entry; |
28 | this.offset = offset; |
29 | } |
30 | } |
31 | |
32 | private XEntry current; |
33 | private Vector<XEntry> xentries = new Vector<>(); |
34 | private HashSet<String> names = new HashSet<>(); |
35 | private CRC32 crc = new CRC32(); |
36 | private long written = 0; |
37 | private long locoff = 0; |
38 | private byte[] comment; |
39 | private int method = DEFLATED; |
40 | private boolean finished; |
41 | |
42 | private boolean closed = false; |
43 | |
44 | private final ZipCoder zc; |
45 | |
46 | private static int version(ZipEntry e) throws ZipException { |
47 | return switch (e.method) { |
48 | case DEFLATED -> 20; |
49 | case STORED -> 10; |
50 | default -> throw new ZipException("unsupported compression method"); |
51 | }; |
52 | } |
53 | |
54 | /** |
55 | * Checks to make sure that this stream has not been closed. |
56 | */ |
57 | private void ensureOpen() throws IOException { |
58 | if (closed) { |
59 | throw new IOException("Stream closed"); |
60 | } |
61 | } |
62 | |
63 | /** |
64 | * Compression method for uncompressed (STORED) entries. |
65 | */ |
66 | public static final int STORED = ZipEntry.STORED; |
67 | |
68 | /** |
69 | * Compression method for compressed (DEFLATED) entries. |
70 | */ |
71 | public static final int DEFLATED = ZipEntry.DEFLATED; |
72 | |
73 | /** |
74 | * Creates a new ZIP output stream. |
75 | * |
76 | * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used |
77 | * to encode the entry names and comments. |
78 | * |
79 | * @param out the actual output stream |
80 | */ |
81 | public *(OutputStream out) { |
82 | this(out, UTF_8.INSTANCE); |
83 | } |
84 | |
85 | /** |
86 | * Creates a new ZIP output stream. |
87 | * |
88 | * @param out the actual output stream |
89 | * |
90 | * @param charset the {@linkplain java.nio.charset.Charset charset} |
91 | * to be used to encode the entry names and comments |
92 | * |
93 | * @since 1.7 |
94 | */ |
95 | public *(OutputStream out, Charset charset) { |
96 | super(out, out != null ? new Deflater(Deflater.DEFAULT_COMPRESSION, true) : null); |
97 | if (charset == null) |
98 | throw new NullPointerException("charset is null"); |
99 | this.zc = ZipCoder.get(charset); |
100 | usesDefaultDeflater = true; |
101 | } |
102 | |
103 | /** |
104 | * Sets the ZIP file comment. |
105 | * @param comment the comment string |
106 | * @throws IllegalArgumentException if the length of the specified |
107 | * ZIP file comment is greater than 0xFFFF bytes |
108 | */ |
109 | public void setComment(String comment) { |
110 | if (comment != null) { |
111 | this.comment = zc.getBytes(comment); |
112 | if (this.comment.length > 0xffff) |
113 | throw new IllegalArgumentException("ZIP file comment too long."); |
114 | } |
115 | } |
116 | |
117 | /** |
118 | * Sets the default compression method for subsequent entries. This |
119 | * default will be used whenever the compression method is not specified |
120 | * for an individual ZIP file entry, and is initially set to DEFLATED. |
121 | * @param method the default compression method |
122 | * @throws IllegalArgumentException if the specified compression method |
123 | * is invalid |
124 | */ |
125 | public void setMethod(int method) { |
126 | if (method != DEFLATED && method != STORED) { |
127 | throw new IllegalArgumentException("invalid compression method"); |
128 | } |
129 | this.method = method; |
130 | } |
131 | |
132 | /** |
133 | * Sets the compression level for subsequent entries which are DEFLATED. |
134 | * The default setting is DEFAULT_COMPRESSION. |
135 | * @param level the compression level (0-9) |
136 | * @throws IllegalArgumentException if the compression level is invalid |
137 | */ |
138 | public void setLevel(int level) { |
139 | def.setLevel(level); |
140 | } |
141 | |
142 | /** |
143 | * Begins writing a new ZIP file entry and positions the stream to the |
144 | * start of the entry data. Closes the current entry if still active. |
145 | * <p> |
146 | * The default compression method will be used if no compression method |
147 | * was specified for the entry. When writing a compressed (DEFLATED) |
148 | * entry, and the compressed size has not been explicitly set with the |
149 | * {@link ZipEntry#setCompressedSize(long)} method, then the compressed |
150 | * size will be set to the actual compressed size after deflation. |
151 | * <p> |
152 | * The current time will be used if the entry has no set modification time. |
153 | * |
154 | * @param e the ZIP entry to be written |
155 | * @throws ZipException if a ZIP format error has occurred |
156 | * @throws IOException if an I/O error has occurred |
157 | */ |
158 | public void putNextEntry(ZipEntry e) throws IOException { |
159 | ensureOpen(); |
160 | if (current != null) { |
161 | closeEntry(); // close previous entry |
162 | } |
163 | if (e.xdostime == -1) { |
164 | // by default, do NOT use extended timestamps in extra |
165 | // data, for now. |
166 | e.setTime(System.currentTimeMillis()); |
167 | } |
168 | if (e.method == -1) { |
169 | e.method = method; // use default method |
170 | } |
171 | // store size, compressed size, and crc-32 in LOC header |
172 | e.flag = 0; |
173 | switch (e.method) { |
174 | case DEFLATED: |
175 | // If not set, store size, compressed size, and crc-32 in data |
176 | // descriptor immediately following the compressed entry data. |
177 | // Ignore the compressed size of a ZipEntry if it was implcitely set |
178 | // while reading that ZipEntry from a ZipFile or ZipInputStream because |
179 | // we can't know the compression level of the source zip file/stream. |
180 | if (e.size == -1 || e.csize == -1 || e.crc == -1 || !e.csizeSet) { |
181 | e.flag = 8; |
182 | } |
183 | break; |
184 | case STORED: |
185 | // compressed size, uncompressed size, and crc-32 must all be |
186 | // set for entries using STORED compression method |
187 | if (e.size == -1) { |
188 | e.size = e.csize; |
189 | } else if (e.csize == -1) { |
190 | e.csize = e.size; |
191 | } else if (e.size != e.csize) { |
192 | throw new ZipException( |
193 | "STORED entry where compressed != uncompressed size"); |
194 | } |
195 | if (e.size == -1 || e.crc == -1) { |
196 | throw new ZipException( |
197 | "STORED entry missing size, compressed size, or crc-32"); |
198 | } |
199 | break; |
200 | default: |
201 | throw new ZipException("unsupported compression method"); |
202 | } |
203 | if (! names.add(e.name)) { |
204 | throw new ZipException("duplicate entry: " + e.name); |
205 | } |
206 | if (zc.isUTF8()) |
207 | e.flag |= USE_UTF8; |
208 | current = new XEntry(e, written); |
209 | xentries.add(current); |
210 | writeLOC(current); |
211 | } |
212 | |
213 | /** |
214 | * Closes the current ZIP entry and positions the stream for writing |
215 | * the next entry. |
216 | * @throws ZipException if a ZIP format error has occurred |
217 | * @throws IOException if an I/O error has occurred |
218 | */ |
219 | public void closeEntry() throws IOException { |
220 | ensureOpen(); |
221 | if (current != null) { |
222 | ZipEntry e = current.entry; |
223 | switch (e.method) { |
224 | case DEFLATED -> { |
225 | def.finish(); |
226 | while (!def.finished()) { |
227 | deflate(); |
228 | } |
229 | if ((e.flag & 8) == 0) { |
230 | // verify size, compressed size, and crc-32 settings |
231 | if (e.size != def.getBytesRead()) { |
232 | throw new ZipException( |
233 | "invalid entry size (expected " + e.size + |
234 | " but got " + def.getBytesRead() + " bytes)"); |
235 | } |
236 | if (e.csize != def.getBytesWritten()) { |
237 | throw new ZipException( |
238 | "invalid entry compressed size (expected " + |
239 | e.csize + " but got " + def.getBytesWritten() + " bytes)"); |
240 | } |
241 | if (e.crc != crc.getValue()) { |
242 | throw new ZipException( |
243 | "invalid entry CRC-32 (expected 0x" + |
244 | Long.toHexString(e.crc) + " but got 0x" + |
245 | Long.toHexString(crc.getValue()) + ")"); |
246 | } |
247 | } else { |
248 | e.size = def.getBytesRead(); |
249 | e.csize = def.getBytesWritten(); |
250 | e.crc = crc.getValue(); |
251 | writeEXT(e); |
252 | } |
253 | def.reset(); |
254 | written += e.csize; |
255 | } |
256 | case STORED -> { |
257 | // we already know that both e.size and e.csize are the same |
258 | if (e.size != written - locoff) { |
259 | throw new ZipException( |
260 | "invalid entry size (expected " + e.size + |
261 | " but got " + (written - locoff) + " bytes)"); |
262 | } |
263 | if (e.crc != crc.getValue()) { |
264 | throw new ZipException( |
265 | "invalid entry crc-32 (expected 0x" + |
266 | Long.toHexString(e.crc) + " but got 0x" + |
267 | Long.toHexString(crc.getValue()) + ")"); |
268 | } |
269 | } |
270 | default -> throw new ZipException("invalid compression method"); |
271 | } |
272 | crc.reset(); |
273 | current = null; |
274 | } |
275 | } |
276 | |
277 | /** |
278 | * Writes an array of bytes to the current ZIP entry data. This method |
279 | * will block until all the bytes are written. |
280 | * @param b the data to be written |
281 | * @param off the start offset in the data |
282 | * @param len the number of bytes that are written |
283 | * @throws ZipException if a ZIP file error has occurred |
284 | * @throws IOException if an I/O error has occurred |
285 | */ |
286 | public synchronized void write(byte[] b, int off, int len) |
287 | throws IOException |
288 | { |
289 | ensureOpen(); |
290 | if (off < 0 || len < 0 || off > b.length - len) { |
291 | throw new IndexOutOfBoundsException(); |
292 | } else if (len == 0) { |
293 | return; |
294 | } |
295 | |
296 | if (current == null) { |
297 | throw new ZipException("no current ZIP entry"); |
298 | } |
299 | ZipEntry entry = current.entry; |
300 | switch (entry.method) { |
301 | case DEFLATED -> super.write(b, off, len); |
302 | case STORED -> { |
303 | written += len; |
304 | if (written - locoff > entry.size) { |
305 | throw new ZipException( |
306 | "attempt to write past end of STORED entry"); |
307 | } |
308 | out.write(b, off, len); |
309 | } |
310 | default -> throw new ZipException("invalid compression method"); |
311 | } |
312 | crc.update(b, off, len); |
313 | } |
314 | |
315 | /** |
316 | * Finishes writing the contents of the ZIP output stream without closing |
317 | * the underlying stream. Use this method when applying multiple filters |
318 | * in succession to the same output stream. |
319 | * @throws ZipException if a ZIP file error has occurred |
320 | * @throws IOException if an I/O exception has occurred |
321 | */ |
322 | public void finish() throws IOException { |
323 | ensureOpen(); |
324 | if (finished) { |
325 | return; |
326 | } |
327 | if (current != null) { |
328 | closeEntry(); |
329 | } |
330 | // write central directory |
331 | long off = written; |
332 | for (XEntry xentry : xentries) |
333 | writeCEN(xentry); |
334 | writeEND(off, written - off); |
335 | finished = true; |
336 | } |
337 | |
338 | /** |
339 | * Closes the ZIP output stream as well as the stream being filtered. |
340 | * @throws ZipException if a ZIP file error has occurred |
341 | * @throws IOException if an I/O error has occurred |
342 | */ |
343 | public void close() throws IOException { |
344 | if (!closed) { |
345 | super.close(); |
346 | closed = true; |
347 | } |
348 | } |
349 | |
350 | /* |
351 | * Writes local file (LOC) header for specified entry. |
352 | */ |
353 | private void writeLOC(XEntry xentry) throws IOException { |
354 | ZipEntry e = xentry.entry; |
355 | int flag = e.flag; |
356 | boolean hasZip64 = false; |
357 | int elen = getExtraLen(e.extra); |
358 | |
359 | writeInt(LOCSIG); // LOC header signature |
360 | if ((flag & 8) == 8) { |
361 | writeShort(version(e)); // version needed to extract |
362 | writeShort(flag); // general purpose bit flag |
363 | writeShort(e.method); // compression method |
364 | writeInt(e.xdostime); // last modification time |
365 | // store size, uncompressed size, and crc-32 in data descriptor |
366 | // immediately following compressed entry data |
367 | writeInt(0); |
368 | writeInt(0); |
369 | writeInt(0); |
370 | } else { |
371 | if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) { |
372 | hasZip64 = true; |
373 | writeShort(45); // ver 4.5 for zip64 |
374 | } else { |
375 | writeShort(version(e)); // version needed to extract |
376 | } |
377 | writeShort(flag); // general purpose bit flag |
378 | writeShort(e.method); // compression method |
379 | writeInt(e.xdostime); // last modification time |
380 | writeInt(e.crc); // crc-32 |
381 | if (hasZip64) { |
382 | writeInt(ZIP64_MAGICVAL); |
383 | writeInt(ZIP64_MAGICVAL); |
384 | elen += 20; //headid(2) + size(2) + size(8) + csize(8) |
385 | } else { |
386 | writeInt(e.csize); // compressed size |
387 | writeInt(e.size); // uncompressed size |
388 | } |
389 | } |
390 | byte[] nameBytes = zc.getBytes(e.name); |
391 | writeShort(nameBytes.length); |
392 | |
393 | int elenEXTT = 0; // info-zip extended timestamp |
394 | int flagEXTT = 0; |
395 | long umtime = -1; |
396 | long uatime = -1; |
397 | long uctime = -1; |
398 | if (e.mtime != null) { |
399 | elenEXTT += 4; |
400 | flagEXTT |= EXTT_FLAG_LMT; |
401 | umtime = fileTimeToUnixTime(e.mtime); |
402 | } |
403 | if (e.atime != null) { |
404 | elenEXTT += 4; |
405 | flagEXTT |= EXTT_FLAG_LAT; |
406 | uatime = fileTimeToUnixTime(e.atime); |
407 | } |
408 | if (e.ctime != null) { |
409 | elenEXTT += 4; |
410 | flagEXTT |= EXTT_FLAT_CT; |
411 | uctime = fileTimeToUnixTime(e.ctime); |
412 | } |
413 | if (flagEXTT != 0) { |
414 | // to use ntfs time if any m/a/ctime is beyond unixtime upper bound |
415 | if (umtime > UPPER_UNIXTIME_BOUND || |
416 | uatime > UPPER_UNIXTIME_BOUND || |
417 | uctime > UPPER_UNIXTIME_BOUND) { |
418 | elen += 36; // NTFS time, total 36 bytes |
419 | } else { |
420 | elen += (elenEXTT + 5); // headid(2) + size(2) + flag(1) + data |
421 | } |
422 | } |
423 | writeShort(elen); |
424 | writeBytes(nameBytes, 0, nameBytes.length); |
425 | if (hasZip64) { |
426 | writeShort(ZIP64_EXTID); |
427 | writeShort(16); |
428 | writeLong(e.size); |
429 | writeLong(e.csize); |
430 | } |
431 | if (flagEXTT != 0) { |
432 | if (umtime > UPPER_UNIXTIME_BOUND || |
433 | uatime > UPPER_UNIXTIME_BOUND || |
434 | uctime > UPPER_UNIXTIME_BOUND) { |
435 | writeShort(EXTID_NTFS); // id |
436 | writeShort(32); // data size |
437 | writeInt(0); // reserved |
438 | writeShort(0x0001); // NTFS attr tag |
439 | writeShort(24); |
440 | writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE |
441 | : fileTimeToWinTime(e.mtime)); |
442 | writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE |
443 | : fileTimeToWinTime(e.atime)); |
444 | writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE |
445 | : fileTimeToWinTime(e.ctime)); |
446 | } else { |
447 | writeShort(EXTID_EXTT); |
448 | writeShort(elenEXTT + 1); // flag + data |
449 | writeByte(flagEXTT); |
450 | if (e.mtime != null) |
451 | writeInt(umtime); |
452 | if (e.atime != null) |
453 | writeInt(uatime); |
454 | if (e.ctime != null) |
455 | writeInt(uctime); |
456 | } |
457 | } |
458 | writeExtra(e.extra); |
459 | locoff = written; |
460 | } |
461 | |
462 | /* |
463 | * Writes extra data descriptor (EXT) for specified entry. |
464 | */ |
465 | private void writeEXT(ZipEntry e) throws IOException { |
466 | writeInt(EXTSIG); // EXT header signature |
467 | writeInt(e.crc); // crc-32 |
468 | if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) { |
469 | writeLong(e.csize); |
470 | writeLong(e.size); |
471 | } else { |
472 | writeInt(e.csize); // compressed size |
473 | writeInt(e.size); // uncompressed size |
474 | } |
475 | } |
476 | |
477 | /** |
478 | * Adds information about compatibility of file attribute information |
479 | * to a version value. |
480 | */ |
481 | private int versionMadeBy(ZipEntry e, int version) { |
482 | return (e.extraAttributes < 0) ? version : |
483 | VERSION_MADE_BY_BASE_UNIX | (version & 0xff); |
484 | } |
485 | |
486 | /* |
487 | * Write central directory (CEN) header for specified entry. |
488 | * REMIND: add support for file attributes |
489 | */ |
490 | private void writeCEN(XEntry xentry) throws IOException { |
491 | ZipEntry e = xentry.entry; |
492 | int flag = e.flag; |
493 | int version = version(e); |
494 | long csize = e.csize; |
495 | long size = e.size; |
496 | long offset = xentry.offset; |
497 | int elenZIP64 = 0; |
498 | boolean hasZip64 = false; |
499 | |
500 | if (e.csize >= ZIP64_MAGICVAL) { |
501 | csize = ZIP64_MAGICVAL; |
502 | elenZIP64 += 8; // csize(8) |
503 | hasZip64 = true; |
504 | } |
505 | if (e.size >= ZIP64_MAGICVAL) { |
506 | size = ZIP64_MAGICVAL; // size(8) |
507 | elenZIP64 += 8; |
508 | hasZip64 = true; |
509 | } |
510 | if (xentry.offset >= ZIP64_MAGICVAL) { |
511 | offset = ZIP64_MAGICVAL; |
512 | elenZIP64 += 8; // offset(8) |
513 | hasZip64 = true; |
514 | } |
515 | writeInt(CENSIG); // CEN header signature |
516 | if (hasZip64) { |
517 | writeShort(versionMadeBy(e,45)); // ver 4.5 for zip64 |
518 | writeShort(45); |
519 | } else { |
520 | writeShort(versionMadeBy(e, version)); // version made by |
521 | writeShort(version); // version needed to extract |
522 | } |
523 | writeShort(flag); // general purpose bit flag |
524 | writeShort(e.method); // compression method |
525 | writeInt(e.xdostime); // last modification time |
526 | writeInt(e.crc); // crc-32 |
527 | writeInt(csize); // compressed size |
528 | writeInt(size); // uncompressed size |
529 | byte[] nameBytes = zc.getBytes(e.name); |
530 | writeShort(nameBytes.length); |
531 | |
532 | int elen = getExtraLen(e.extra); |
533 | if (hasZip64) { |
534 | elen += (elenZIP64 + 4);// + headid(2) + datasize(2) |
535 | } |
536 | // cen info-zip extended timestamp only outputs mtime |
537 | // but set the flag for a/ctime, if present in loc |
538 | int flagEXTT = 0; |
539 | long umtime = -1; |
540 | long uatime = -1; |
541 | long uctime = -1; |
542 | if (e.mtime != null) { |
543 | flagEXTT |= EXTT_FLAG_LMT; |
544 | umtime = fileTimeToUnixTime(e.mtime); |
545 | } |
546 | if (e.atime != null) { |
547 | flagEXTT |= EXTT_FLAG_LAT; |
548 | uatime = fileTimeToUnixTime(e.atime); |
549 | } |
550 | if (e.ctime != null) { |
551 | flagEXTT |= EXTT_FLAT_CT; |
552 | uctime = fileTimeToUnixTime(e.ctime); |
553 | } |
554 | if (flagEXTT != 0) { |
555 | // to use ntfs time if any m/a/ctime is beyond unixtime upper bound |
556 | if (umtime > UPPER_UNIXTIME_BOUND || |
557 | uatime > UPPER_UNIXTIME_BOUND || |
558 | uctime > UPPER_UNIXTIME_BOUND) { |
559 | elen += 36; // NTFS time total 36 bytes |
560 | } else { |
561 | elen += 5; // headid(2) + sz(2) + flag(1) |
562 | if (e.mtime != null) |
563 | elen += 4; // + mtime (4) |
564 | } |
565 | } |
566 | writeShort(elen); |
567 | byte[] commentBytes; |
568 | if (e.comment != null) { |
569 | commentBytes = zc.getBytes(e.comment); |
570 | writeShort(Math.min(commentBytes.length, 0xffff)); |
571 | } else { |
572 | commentBytes = null; |
573 | writeShort(0); |
574 | } |
575 | writeShort(0); // starting disk number |
576 | writeShort(0); // internal file attributes (unused) |
577 | // extra file attributes, used for storing posix permissions etc. |
578 | writeInt(e.extraAttributes > 0 ? e.extraAttributes << 16 : 0); |
579 | writeInt(offset); // relative offset of local header |
580 | writeBytes(nameBytes, 0, nameBytes.length); |
581 | |
582 | // take care of EXTID_ZIP64 and EXTID_EXTT |
583 | if (hasZip64) { |
584 | writeShort(ZIP64_EXTID);// Zip64 extra |
585 | writeShort(elenZIP64); |
586 | if (size == ZIP64_MAGICVAL) |
587 | writeLong(e.size); |
588 | if (csize == ZIP64_MAGICVAL) |
589 | writeLong(e.csize); |
590 | if (offset == ZIP64_MAGICVAL) |
591 | writeLong(xentry.offset); |
592 | } |
593 | if (flagEXTT != 0) { |
594 | if (umtime > UPPER_UNIXTIME_BOUND || |
595 | uatime > UPPER_UNIXTIME_BOUND || |
596 | uctime > UPPER_UNIXTIME_BOUND) { |
597 | writeShort(EXTID_NTFS); // id |
598 | writeShort(32); // data size |
599 | writeInt(0); // reserved |
600 | writeShort(0x0001); // NTFS attr tag |
601 | writeShort(24); |
602 | writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE |
603 | : fileTimeToWinTime(e.mtime)); |
604 | writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE |
605 | : fileTimeToWinTime(e.atime)); |
606 | writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE |
607 | : fileTimeToWinTime(e.ctime)); |
608 | } else { |
609 | writeShort(EXTID_EXTT); |
610 | if (e.mtime != null) { |
611 | writeShort(5); // flag + mtime |
612 | writeByte(flagEXTT); |
613 | writeInt(umtime); |
614 | } else { |
615 | writeShort(1); // flag only |
616 | writeByte(flagEXTT); |
617 | } |
618 | } |
619 | } |
620 | writeExtra(e.extra); |
621 | if (commentBytes != null) { |
622 | writeBytes(commentBytes, 0, Math.min(commentBytes.length, 0xffff)); |
623 | } |
624 | } |
625 | |
626 | /* |
627 | * Writes end of central directory (END) header. |
628 | */ |
629 | private void writeEND(long off, long len) throws IOException { |
630 | boolean hasZip64 = false; |
631 | long xlen = len; |
632 | long xoff = off; |
633 | if (xlen >= ZIP64_MAGICVAL) { |
634 | xlen = ZIP64_MAGICVAL; |
635 | hasZip64 = true; |
636 | } |
637 | if (xoff >= ZIP64_MAGICVAL) { |
638 | xoff = ZIP64_MAGICVAL; |
639 | hasZip64 = true; |
640 | } |
641 | int count = xentries.size(); |
642 | if (count >= ZIP64_MAGICCOUNT) { |
643 | hasZip64 |= !inhibitZip64; |
644 | if (hasZip64) { |
645 | count = ZIP64_MAGICCOUNT; |
646 | } |
647 | } |
648 | if (hasZip64) { |
649 | long off64 = written; |
650 | //zip64 end of central directory record |
651 | writeInt(ZIP64_ENDSIG); // zip64 END record signature |
652 | writeLong(ZIP64_ENDHDR - 12); // size of zip64 end |
653 | writeShort(45); // version made by |
654 | writeShort(45); // version needed to extract |
655 | writeInt(0); // number of this disk |
656 | writeInt(0); // central directory start disk |
657 | writeLong(xentries.size()); // number of directory entires on disk |
658 | writeLong(xentries.size()); // number of directory entires |
659 | writeLong(len); // length of central directory |
660 | writeLong(off); // offset of central directory |
661 | |
662 | //zip64 end of central directory locator |
663 | writeInt(ZIP64_LOCSIG); // zip64 END locator signature |
664 | writeInt(0); // zip64 END start disk |
665 | writeLong(off64); // offset of zip64 END |
666 | writeInt(1); // total number of disks (?) |
667 | } |
668 | writeInt(ENDSIG); // END record signature |
669 | writeShort(0); // number of this disk |
670 | writeShort(0); // central directory start disk |
671 | writeShort(count); // number of directory entries on disk |
672 | writeShort(count); // total number of directory entries |
673 | writeInt(xlen); // length of central directory |
674 | writeInt(xoff); // offset of central directory |
675 | if (comment != null) { // zip file comment |
676 | writeShort(comment.length); |
677 | writeBytes(comment, 0, comment.length); |
678 | } else { |
679 | writeShort(0); |
680 | } |
681 | } |
682 | |
683 | /* |
684 | * Returns the length of extra data without EXTT and ZIP64. |
685 | */ |
686 | private int getExtraLen(byte[] extra) { |
687 | if (extra == null) |
688 | return 0; |
689 | int skipped = 0; |
690 | int len = extra.length; |
691 | int off = 0; |
692 | while (off + 4 <= len) { |
693 | int tag = get16(extra, off); |
694 | int sz = get16(extra, off + 2); |
695 | if (sz < 0 || (off + 4 + sz) > len) { |
696 | break; |
697 | } |
698 | if (tag == EXTID_EXTT || tag == EXTID_ZIP64) { |
699 | skipped += (sz + 4); |
700 | } |
701 | off += (sz + 4); |
702 | } |
703 | return len - skipped; |
704 | } |
705 | |
706 | /* |
707 | * Writes extra data without EXTT and ZIP64. |
708 | * |
709 | * Extra timestamp and ZIP64 data is handled/output separately |
710 | * in writeLOC and writeCEN. |
711 | */ |
712 | private void writeExtra(byte[] extra) throws IOException { |
713 | if (extra != null) { |
714 | int len = extra.length; |
715 | int off = 0; |
716 | while (off + 4 <= len) { |
717 | int tag = get16(extra, off); |
718 | int sz = get16(extra, off + 2); |
719 | if (sz < 0 || (off + 4 + sz) > len) { |
720 | writeBytes(extra, off, len - off); |
721 | return; |
722 | } |
723 | if (tag != EXTID_EXTT && tag != EXTID_ZIP64) { |
724 | writeBytes(extra, off, sz + 4); |
725 | } |
726 | off += (sz + 4); |
727 | } |
728 | if (off < len) { |
729 | writeBytes(extra, off, len - off); |
730 | } |
731 | } |
732 | } |
733 | |
734 | /* |
735 | * Writes a 8-bit byte to the output stream. |
736 | */ |
737 | private void writeByte(int v) throws IOException { |
738 | OutputStream out = this.out; |
739 | out.write(v & 0xff); |
740 | written += 1; |
741 | } |
742 | |
743 | /* |
744 | * Writes a 16-bit short to the output stream in little-endian byte order. |
745 | */ |
746 | private void writeShort(int v) throws IOException { |
747 | OutputStream out = this.out; |
748 | out.write((v >>> 0) & 0xff); |
749 | out.write((v >>> 8) & 0xff); |
750 | written += 2; |
751 | } |
752 | |
753 | /* |
754 | * Writes a 32-bit int to the output stream in little-endian byte order. |
755 | */ |
756 | private void writeInt(long v) throws IOException { |
757 | OutputStream out = this.out; |
758 | out.write((int)((v >>> 0) & 0xff)); |
759 | out.write((int)((v >>> 8) & 0xff)); |
760 | out.write((int)((v >>> 16) & 0xff)); |
761 | out.write((int)((v >>> 24) & 0xff)); |
762 | written += 4; |
763 | } |
764 | |
765 | /* |
766 | * Writes a 64-bit int to the output stream in little-endian byte order. |
767 | */ |
768 | private void writeLong(long v) throws IOException { |
769 | OutputStream out = this.out; |
770 | out.write((int)((v >>> 0) & 0xff)); |
771 | out.write((int)((v >>> 8) & 0xff)); |
772 | out.write((int)((v >>> 16) & 0xff)); |
773 | out.write((int)((v >>> 24) & 0xff)); |
774 | out.write((int)((v >>> 32) & 0xff)); |
775 | out.write((int)((v >>> 40) & 0xff)); |
776 | out.write((int)((v >>> 48) & 0xff)); |
777 | out.write((int)((v >>> 56) & 0xff)); |
778 | written += 8; |
779 | } |
780 | |
781 | /* |
782 | * Writes an array of bytes to the output stream. |
783 | */ |
784 | private void writeBytes(byte[] b, int off, int len) throws IOException { |
785 | super.out.write(b, off, len); |
786 | written += len; |
787 | } |
788 | } |
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1034350 |
Snippet name: | ZipOutputStreamWithRawCopying [abandoned] - uncompilable, uses too many JDK internals. will use zip4j instead |
Eternal ID of this version: | #1034350/4 |
Text MD5: | 34fec5df077d85d06d9f106ed04aa6b8 |
Author: | stefan |
Category: | javax / stefan's os |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-02-03 04:41:47 |
Source code size: | 30162 bytes / 788 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 149 / 226 |
Version history: | 3 change(s) |
Referenced in: | [show references] |