blob: 810c4778457c6590740dd65591f5380ee625935d [file] [log] [blame]
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.archivepatcher.shared;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* A pipe that moves data from an {@link InputStream} to an {@link OutputStream}, optionally
* uncompressing the input data on-the-fly.
*/
public class PartiallyUncompressingPipe implements Closeable {
/**
* The uncompressor used to uncompress compressed input streams.
*/
private final DeflateUncompressor uncompressor;
/**
* The output stream to write to.
*/
private final CountingOutputStream out;
/**
* A buffer used when copying bytes.
*/
private final byte[] copyBuffer;
/**
* Modes available for {@link PartiallyUncompressingPipe#pipe(InputStream, Mode)}.
*/
public static enum Mode {
/**
* Copy bytes form the {@link InputStream} to the {@link OutputStream} without modification.
*/
COPY,
/**
* Treat the {@link InputStream} as a deflate stream with nowrap=false, uncompress the bytes
* on-the-fly and write the uncompressed data to the {@link OutputStream}.
*/
UNCOMPRESS_WRAPPED,
/**
* Treat the {@link InputStream} as a deflate stream with nowrap=true, uncompress the bytes
* on-the-fly and write the uncompressed data to the {@link OutputStream}.
*/
UNCOMPRESS_NOWRAP,
}
/**
* Constructs a new stream.
* @param out the stream, to write to
* @param copyBufferSize the size of the buffer to use when copying instead of uncompressing
*/
public PartiallyUncompressingPipe(OutputStream out, int copyBufferSize) {
this.out = new CountingOutputStream(out);
uncompressor = new DeflateUncompressor();
uncompressor.setCaching(true);
copyBuffer = new byte[copyBufferSize];
}
/**
* Pipes the entire contents of the specified {@link InputStream} to the configured
* {@link OutputStream}, optionally uncompressing on-the-fly.
* @param in the stream to read from
* @param mode the mode to use for reading and writing
* @return the number of bytes written to the output stream
* @throws IOException if anything goes wrong
*/
public long pipe(InputStream in, Mode mode) throws IOException {
long bytesWrittenBefore = out.getNumBytesWritten();
if (mode == Mode.COPY) {
int numRead = 0;
while ((numRead = in.read(copyBuffer)) >= 0) {
out.write(copyBuffer, 0, numRead);
}
} else {
uncompressor.setNowrap(mode == Mode.UNCOMPRESS_NOWRAP);
uncompressor.uncompress(in, out);
}
out.flush();
return out.getNumBytesWritten() - bytesWrittenBefore;
}
/**
* Returns the number of bytes written to the stream so far.
* @return as described
*/
public long getNumBytesWritten() {
return out.getNumBytesWritten();
}
@Override
public void close() throws IOException {
uncompressor.release();
out.close();
}
}