source: josm/trunk/src/org/tukaani/xz/BlockOutputStream.java@ 15948

Last change on this file since 15948 was 13350, checked in by stoecker, 7 years ago

see #15816 - add XZ support

File size: 4.2 KB
Line 
1/*
2 * BlockOutputStream
3 *
4 * Author: Lasse Collin <lasse.collin@tukaani.org>
5 *
6 * This file has been put into the public domain.
7 * You can do whatever you want with this file.
8 */
9
10package org.tukaani.xz;
11
12import java.io.OutputStream;
13import java.io.ByteArrayOutputStream;
14import java.io.IOException;
15import org.tukaani.xz.common.EncoderUtil;
16import org.tukaani.xz.check.Check;
17
18class BlockOutputStream extends FinishableOutputStream {
19 private final OutputStream out;
20 private final CountingOutputStream outCounted;
21 private FinishableOutputStream filterChain;
22 private final Check check;
23
24 private final int headerSize;
25 private final long compressedSizeLimit;
26 private long uncompressedSize = 0;
27
28 private final byte[] tempBuf = new byte[1];
29
30 public BlockOutputStream(OutputStream out, FilterEncoder[] filters,
31 Check check, ArrayCache arrayCache)
32 throws IOException {
33 this.out = out;
34 this.check = check;
35
36 // Initialize the filter chain.
37 outCounted = new CountingOutputStream(out);
38 filterChain = outCounted;
39 for (int i = filters.length - 1; i >= 0; --i)
40 filterChain = filters[i].getOutputStream(filterChain, arrayCache);
41
42 // Prepare to encode the Block Header field.
43 ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
44
45 // Write a dummy Block Header Size field. The real value is written
46 // once everything else except CRC32 has been written.
47 bufStream.write(0x00);
48
49 // Write Block Flags. Storing Compressed Size or Uncompressed Size
50 // isn't supported for now.
51 bufStream.write(filters.length - 1);
52
53 // List of Filter Flags
54 for (int i = 0; i < filters.length; ++i) {
55 EncoderUtil.encodeVLI(bufStream, filters[i].getFilterID());
56 byte[] filterProps = filters[i].getFilterProps();
57 EncoderUtil.encodeVLI(bufStream, filterProps.length);
58 bufStream.write(filterProps);
59 }
60
61 // Header Padding
62 while ((bufStream.size() & 3) != 0)
63 bufStream.write(0x00);
64
65 byte[] buf = bufStream.toByteArray();
66
67 // Total size of the Block Header: Take the size of the CRC32 field
68 // into account.
69 headerSize = buf.length + 4;
70
71 // This is just a sanity check.
72 if (headerSize > EncoderUtil.BLOCK_HEADER_SIZE_MAX)
73 throw new UnsupportedOptionsException();
74
75 // Block Header Size
76 buf[0] = (byte)(buf.length / 4);
77
78 // Write the Block Header field to the output stream.
79 out.write(buf);
80 EncoderUtil.writeCRC32(out, buf);
81
82 // Calculate the maximum allowed size of the Compressed Data field.
83 // It is hard to exceed it so this is mostly to be pedantic.
84 compressedSizeLimit = (EncoderUtil.VLI_MAX & ~3)
85 - headerSize - check.getSize();
86 }
87
88 public void write(int b) throws IOException {
89 tempBuf[0] = (byte)b;
90 write(tempBuf, 0, 1);
91 }
92
93 public void write(byte[] buf, int off, int len) throws IOException {
94 filterChain.write(buf, off, len);
95 check.update(buf, off, len);
96 uncompressedSize += len;
97 validate();
98 }
99
100 public void flush() throws IOException {
101 filterChain.flush();
102 validate();
103 }
104
105 public void finish() throws IOException {
106 // Finish the Compressed Data field.
107 filterChain.finish();
108 validate();
109
110 // Block Padding
111 for (long i = outCounted.getSize(); (i & 3) != 0; ++i)
112 out.write(0x00);
113
114 // Check
115 out.write(check.finish());
116 }
117
118 private void validate() throws IOException {
119 long compressedSize = outCounted.getSize();
120
121 // It is very hard to trigger this exception.
122 // This is just to be pedantic.
123 if (compressedSize < 0 || compressedSize > compressedSizeLimit
124 || uncompressedSize < 0)
125 throw new XZIOException("XZ Stream has grown too big");
126 }
127
128 public long getUnpaddedSize() {
129 return headerSize + outCounted.getSize() + check.getSize();
130 }
131
132 public long getUncompressedSize() {
133 return uncompressedSize;
134 }
135}
Note: See TracBrowser for help on using the repository browser.