rev 50498 : 8199871: Deprecate pack200 and unpack200 tools
Reviewed-by:
1 /*
2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27 package com.sun.java.util.jar.pack;
28
29 import java.io.BufferedInputStream;
30 import java.io.File;
31 import java.io.FileInputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.nio.ByteBuffer;
35 import java.util.jar.JarOutputStream;
36 import java.util.jar.Pack200;
37 import java.util.zip.CRC32;
38 import java.util.zip.Deflater;
39 import java.util.zip.ZipEntry;
40 import java.util.zip.ZipOutputStream;
41
42 @SuppressWarnings({"removal"})
43 class NativeUnpack {
44 // Pointer to the native unpacker obj
45 private long unpackerPtr;
46
47 // Input stream.
48 private BufferedInputStream in;
49
50 private static synchronized native void initIDs();
51
52 // Starts processing at the indicated position in the buffer.
53 // If the buffer is null, the readInputFn callback is used to get bytes.
54 // Returns (s<<32|f), the number of following segments and files.
55 private synchronized native long start(ByteBuffer buf, long offset);
56
57 // Returns true if there's another, and fills in the parts.
58 private synchronized native boolean getNextFile(Object[] parts);
59
60 private synchronized native ByteBuffer getUnusedInput();
61
62 // Resets the engine and frees all resources.
63 // Returns total number of bytes consumed by the engine.
64 private synchronized native long finish();
65
66 // Setting state in the unpacker.
67 protected synchronized native boolean setOption(String opt, String value);
68 protected synchronized native String getOption(String opt);
69
70 private int _verbose;
71
72 // State for progress bar:
73 private long _byteCount; // bytes read in current segment
74 private int _segCount; // number of segs scanned
75 private int _fileCount; // number of files written
76 private long _estByteLimit; // estimate of eventual total
77 private int _estSegLimit; // ditto
78 private int _estFileLimit; // ditto
79 private int _prevPercent = -1; // for monotonicity
80
81 private final CRC32 _crc32 = new CRC32();
82 private byte[] _buf = new byte[1<<14];
83
84 private UnpackerImpl _p200;
85 private PropMap _props;
86
87 static {
88 // If loading from stand alone build uncomment this.
89 // System.loadLibrary("unpack");
90 java.security.AccessController.doPrivileged(
91 new java.security.PrivilegedAction<>() {
92 public Void run() {
93 System.loadLibrary("unpack");
94 return null;
95 }
96 });
97 initIDs();
98 }
99
100 NativeUnpack(UnpackerImpl p200) {
101 super();
102 _p200 = p200;
103 _props = p200.props;
104 p200._nunp = this;
105 }
106
107 // for JNI callbacks
108 private static Object currentInstance() {
109 UnpackerImpl p200 = (UnpackerImpl) Utils.getTLGlobals();
110 return (p200 == null)? null: p200._nunp;
111 }
112
113 private synchronized long getUnpackerPtr() {
114 return unpackerPtr;
115 }
116
117 // Callback from the unpacker engine to get more data.
118 private long readInputFn(ByteBuffer pbuf, long minlen) throws IOException {
119 if (in == null) return 0; // nothing is readable
120 long maxlen = pbuf.capacity() - pbuf.position();
121 assert(minlen <= maxlen); // don't talk nonsense
122 long numread = 0;
123 int steps = 0;
124 while (numread < minlen) {
125 steps++;
126 // read available input, up to buf.length or maxlen
127 int readlen = _buf.length;
128 if (readlen > (maxlen - numread))
129 readlen = (int)(maxlen - numread);
130 int nr = in.read(_buf, 0, readlen);
131 if (nr <= 0) break;
132 numread += nr;
133 assert(numread <= maxlen);
134 // %%% get rid of this extra copy by using nio?
135 pbuf.put(_buf, 0, nr);
136 }
137 if (_verbose > 1)
138 Utils.log.fine("readInputFn("+minlen+","+maxlen+") => "+numread+" steps="+steps);
139 if (maxlen > 100) {
140 _estByteLimit = _byteCount + maxlen;
141 } else {
142 _estByteLimit = (_byteCount + numread) * 20;
143 }
144 _byteCount += numread;
145 updateProgress();
146 return numread;
147 }
148
149 private void updateProgress() {
150 // Progress is a combination of segment reading and file writing.
151 final double READ_WT = 0.33;
152 final double WRITE_WT = 0.67;
153 double readProgress = _segCount;
154 if (_estByteLimit > 0 && _byteCount > 0)
155 readProgress += (double)_byteCount / _estByteLimit;
156 double writeProgress = _fileCount;
157 double scaledProgress
158 = READ_WT * readProgress / Math.max(_estSegLimit,1)
159 + WRITE_WT * writeProgress / Math.max(_estFileLimit,1);
160 int percent = (int) Math.round(100*scaledProgress);
161 if (percent > 100) percent = 100;
162 if (percent > _prevPercent) {
163 _prevPercent = percent;
164 _props.setInteger(Pack200.Unpacker.PROGRESS, percent);
165 if (_verbose > 0)
166 Utils.log.info("progress = "+percent);
167 }
168 }
169
170 private void copyInOption(String opt) {
171 String val = _props.getProperty(opt);
172 if (_verbose > 0)
173 Utils.log.info("set "+opt+"="+val);
174 if (val != null) {
175 boolean set = setOption(opt, val);
176 if (!set)
177 Utils.log.warning("Invalid option "+opt+"="+val);
178 }
179 }
180
181 void run(InputStream inRaw, JarOutputStream jstream,
182 ByteBuffer presetInput) throws IOException {
183 BufferedInputStream in0 = new BufferedInputStream(inRaw);
184 this.in = in0; // for readInputFn to see
185 _verbose = _props.getInteger(Utils.DEBUG_VERBOSE);
186 // Fix for BugId: 4902477, -unpack.modification.time = 1059010598000
187 // TODO eliminate and fix in unpack.cpp
188
189 final int modtime = Pack200.Packer.KEEP.equals(_props.getProperty(Utils.UNPACK_MODIFICATION_TIME, "0")) ?
190 Constants.NO_MODTIME : _props.getTime(Utils.UNPACK_MODIFICATION_TIME);
191
192 copyInOption(Utils.DEBUG_VERBOSE);
193 copyInOption(Pack200.Unpacker.DEFLATE_HINT);
194 if (modtime == Constants.NO_MODTIME) // Don't pass KEEP && NOW
195 copyInOption(Utils.UNPACK_MODIFICATION_TIME);
196 updateProgress(); // reset progress bar
197 for (;;) {
198 // Read the packed bits.
199 long counts = start(presetInput, 0);
200 _byteCount = _estByteLimit = 0; // reset partial scan counts
201 ++_segCount; // just finished scanning a whole segment...
202 int nextSeg = (int)( counts >>> 32 );
203 int nextFile = (int)( counts >>> 0 );
204
205 // Estimate eventual total number of segments and files.
206 _estSegLimit = _segCount + nextSeg;
207 double filesAfterThisSeg = _fileCount + nextFile;
208 _estFileLimit = (int)( (filesAfterThisSeg *
209 _estSegLimit) / _segCount );
210
211 // Write the files.
212 int[] intParts = { 0,0, 0, 0 };
213 // intParts = {size.hi/lo, mod, defl}
214 Object[] parts = { intParts, null, null, null };
215 // parts = { {intParts}, name, data0/1 }
216 while (getNextFile(parts)) {
217 //BandStructure.printArrayTo(System.out, intParts, 0, parts.length);
218 String name = (String) parts[1];
219 long size = ( (long)intParts[0] << 32)
220 + (((long)intParts[1] << 32) >>> 32);
221
222 long mtime = (modtime != Constants.NO_MODTIME ) ?
223 modtime : intParts[2] ;
224 boolean deflateHint = (intParts[3] != 0);
225 ByteBuffer data0 = (ByteBuffer) parts[2];
226 ByteBuffer data1 = (ByteBuffer) parts[3];
227 writeEntry(jstream, name, mtime, size, deflateHint,
228 data0, data1);
229 ++_fileCount;
230 updateProgress();
231 }
232 presetInput = getUnusedInput();
233 long consumed = finish();
234 if (_verbose > 0)
235 Utils.log.info("bytes consumed = "+consumed);
236 if (presetInput == null &&
237 !Utils.isPackMagic(Utils.readMagic(in0))) {
238 break;
239 }
240 if (_verbose > 0 ) {
241 if (presetInput != null)
242 Utils.log.info("unused input = "+presetInput);
243 }
244 }
245 }
246
247 void run(InputStream in, JarOutputStream jstream) throws IOException {
248 run(in, jstream, null);
249 }
250
251 void run(File inFile, JarOutputStream jstream) throws IOException {
252 // %%% maybe memory-map the file, and pass it straight into unpacker
253 ByteBuffer mappedFile = null;
254 try (FileInputStream fis = new FileInputStream(inFile)) {
255 run(fis, jstream, mappedFile);
256 }
257 // Note: caller is responsible to finish with jstream.
258 }
259
260 private void writeEntry(JarOutputStream j, String name,
261 long mtime, long lsize, boolean deflateHint,
262 ByteBuffer data0, ByteBuffer data1) throws IOException {
263 int size = (int)lsize;
264 if (size != lsize)
265 throw new IOException("file too large: "+lsize);
266
267 CRC32 crc32 = _crc32;
268
269 if (_verbose > 1)
270 Utils.log.fine("Writing entry: "+name+" size="+size
271 +(deflateHint?" deflated":""));
272
273 if (_buf.length < size) {
274 int newSize = size;
275 while (newSize < _buf.length) {
276 newSize <<= 1;
277 if (newSize <= 0) {
278 newSize = size;
279 break;
280 }
281 }
282 _buf = new byte[newSize];
283 }
284 assert(_buf.length >= size);
285
286 int fillp = 0;
287 if (data0 != null) {
288 int size0 = data0.capacity();
289 data0.get(_buf, fillp, size0);
290 fillp += size0;
291 }
292 if (data1 != null) {
293 int size1 = data1.capacity();
294 data1.get(_buf, fillp, size1);
295 fillp += size1;
296 }
297 while (fillp < size) {
298 // Fill in rest of data from the stream itself.
299 int nr = in.read(_buf, fillp, size - fillp);
300 if (nr <= 0) throw new IOException("EOF at end of archive");
301 fillp += nr;
302 }
303
304 ZipEntry z = new ZipEntry(name);
305 z.setTime(mtime * 1000);
306
307 if (size == 0) {
308 z.setMethod(ZipOutputStream.STORED);
309 z.setSize(0);
310 z.setCrc(0);
311 z.setCompressedSize(0);
312 } else if (!deflateHint) {
313 z.setMethod(ZipOutputStream.STORED);
314 z.setSize(size);
315 z.setCompressedSize(size);
316 crc32.reset();
317 crc32.update(_buf, 0, size);
318 z.setCrc(crc32.getValue());
319 } else {
320 z.setMethod(Deflater.DEFLATED);
321 z.setSize(size);
322 }
323
324 j.putNextEntry(z);
325
326 if (size > 0)
327 j.write(_buf, 0, size);
328
329 j.closeEntry();
330 if (_verbose > 0) Utils.log.info("Writing " + Utils.zeString(z));
331 }
332 }
--- EOF ---