1d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// Copyright 2016 Google Inc. All rights reserved. 2d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// 3d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// Licensed under the Apache License, Version 2.0 (the "License"); 4d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// you may not use this file except in compliance with the License. 5d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// You may obtain a copy of the License at 6d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// 7d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// http://www.apache.org/licenses/LICENSE-2.0 8d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// 9d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// Unless required by applicable law or agreed to in writing, software 10d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// distributed under the License is distributed on an "AS IS" BASIS, 11d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// See the License for the specific language governing permissions and 13d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden// limitations under the License. 14d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 15d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenpackage com.google.archivepatcher.shared; 16d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 17d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenimport java.io.Closeable; 18d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenimport java.io.IOException; 19d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenimport java.io.InputStream; 20d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenimport java.io.OutputStream; 21d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 22d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden/** 23d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * A pipe that moves data from an {@link InputStream} to an {@link OutputStream}, optionally 24d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * uncompressing the input data on-the-fly. 25d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 26d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Haydenpublic class PartiallyUncompressingPipe implements Closeable { 27d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 28d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * The uncompressor used to uncompress compressed input streams. 29d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 30d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden private final DeflateUncompressor uncompressor; 31d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 32d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 33d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * The output stream to write to. 34d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 35d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden private final CountingOutputStream out; 36d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 37d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 38d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * A buffer used when copying bytes. 39d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 40d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden private final byte[] copyBuffer; 41d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 42d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 43d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Modes available for {@link PartiallyUncompressingPipe#pipe(InputStream, Mode)}. 44d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 45d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public static enum Mode { 46d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 47d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Copy bytes form the {@link InputStream} to the {@link OutputStream} without modification. 48d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 49d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden COPY, 50d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 51d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 52d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Treat the {@link InputStream} as a deflate stream with nowrap=false, uncompress the bytes 53d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * on-the-fly and write the uncompressed data to the {@link OutputStream}. 54d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 55d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden UNCOMPRESS_WRAPPED, 56d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 57d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 58d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Treat the {@link InputStream} as a deflate stream with nowrap=true, uncompress the bytes 59d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * on-the-fly and write the uncompressed data to the {@link OutputStream}. 60d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 61d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden UNCOMPRESS_NOWRAP, 62d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 63d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 64d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 65d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Constructs a new stream. 66d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @param out the stream, to write to 67d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @param copyBufferSize the size of the buffer to use when copying instead of uncompressing 68d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 69d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public PartiallyUncompressingPipe(OutputStream out, int copyBufferSize) { 70d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden this.out = new CountingOutputStream(out); 71d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden uncompressor = new DeflateUncompressor(); 72d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden uncompressor.setCaching(true); 73d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden copyBuffer = new byte[copyBufferSize]; 74d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 75d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 76d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 77d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Pipes the entire contents of the specified {@link InputStream} to the configured 78d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * {@link OutputStream}, optionally uncompressing on-the-fly. 79d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @param in the stream to read from 80d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @param mode the mode to use for reading and writing 81d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @return the number of bytes written to the output stream 82d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @throws IOException if anything goes wrong 83d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 84d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public long pipe(InputStream in, Mode mode) throws IOException { 85d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden long bytesWrittenBefore = out.getNumBytesWritten(); 86d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden if (mode == Mode.COPY) { 87d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden int numRead = 0; 88d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden while ((numRead = in.read(copyBuffer)) >= 0) { 89d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden out.write(copyBuffer, 0, numRead); 90d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 91d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } else { 92d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden uncompressor.setNowrap(mode == Mode.UNCOMPRESS_NOWRAP); 93d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden uncompressor.uncompress(in, out); 94d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 95d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden out.flush(); 96d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden return out.getNumBytesWritten() - bytesWrittenBefore; 97d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 98d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 99d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden /** 100d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * Returns the number of bytes written to the stream so far. 101d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden * @return as described 102d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden */ 103d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public long getNumBytesWritten() { 104d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden return out.getNumBytesWritten(); 105d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 106d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden 107d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden @Override 108d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden public void close() throws IOException { 109d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden uncompressor.release(); 110d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden out.close(); 111d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden } 112d84e46c30d0ea027d047ee4c75c040a173ec9ffcAndrew Hayden} 113