org.cryptacular.io.DecodingInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cryptacular Show documentation
Show all versions of cryptacular Show documentation
The spectacular complement to the Bouncy Castle crypto API for Java.
The newest version!
/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.io;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import org.cryptacular.codec.Base64Decoder;
import org.cryptacular.codec.Decoder;
import org.cryptacular.codec.HexDecoder;
/**
* Filters read bytes through a {@link Decoder} such that consumers obtain raw (decoded) bytes from read operations.
*
* @author Middleware Services
*/
public class DecodingInputStream extends FilterInputStream
{
/** Performs decoding. */
private final Decoder decoder;
/** Wraps the input stream to convert bytes to characters. */
private final InputStreamReader reader;
/** Holds input bytes as characters. */
private CharBuffer input;
/** Receives decoding result. */
private ByteBuffer output;
/**
* Creates a new instance that wraps the given stream and performs decoding using the given encoder component.
*
* @param in Input stream to wrap.
* @param d Decoder that provides on-the-fly decoding.
*/
public DecodingInputStream(final InputStream in, final Decoder d)
{
super(in);
if (d == null) {
throw new IllegalArgumentException("Decoder cannot be null.");
}
decoder = d;
reader = new InputStreamReader(in);
}
@Override
public int read()
throws IOException
{
return read(new byte[1]);
}
@Override
public int read(final byte[] b)
throws IOException
{
return read(b, 0, b.length);
}
@Override
public int read(final byte[] b, final int off, final int len)
throws IOException
{
prepareInputBuffer(len - off);
prepareOutputBuffer();
if (reader.read(input) < 0) {
decoder.finalize(output);
if (output.position() == 0) {
return -1;
}
} else {
input.flip();
decoder.decode(input, output);
}
output.flip();
output.get(b, off, output.limit());
return output.position();
}
/**
* Creates a new instance that decodes base64 input from the given stream.
*
* @param in Wrapped input stream.
*
* @return Decoding input stream that decodes base64 output.
*/
public static DecodingInputStream base64(final InputStream in)
{
return new DecodingInputStream(in, new Base64Decoder());
}
/**
* Creates a new instance that decodes hexadecimal input from the given stream.
*
* @param in Wrapped input stream.
*
* @return Decoding input stream that decodes hexadecimal output.
*/
public static DecodingInputStream hex(final InputStream in)
{
return new DecodingInputStream(in, new HexDecoder());
}
/**
* Prepares the input buffer to receive the given number of bytes.
*
* @param required Number of bytes required.
*/
private void prepareInputBuffer(final int required)
{
if (input == null || input.capacity() < required) {
input = CharBuffer.allocate(required);
} else {
input.clear();
}
}
/** Prepares the output buffer based on input buffer capacity. */
private void prepareOutputBuffer()
{
final int required = decoder.outputSize(input.capacity());
if (output == null || output.capacity() < required) {
output = ByteBuffer.allocate(required);
} else {
output.clear();
}
}
}