CachedInputStream.java
/*
** Module : CachedInputStream.java
** Public : A cached instance of an InputStream with FWD Stream interface.
**
** Copyright (c) 2021-2022, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
** 001 OM 20210830 Created initial version.
** EVL 20220321 Adding fix to properly return valid character (16-bit) value within 32-bit integer.
** TW 20220325 Fix infinite loop at temp-table import from jar, fix missing chunk of data at end,
** prevent null padding at end (refs #6160)
** EVL 20220325 Base refactoring for Stream based classes getting single byte and array of bytes.
** EVL 20220328 Reworked to be used as extending of the FileStream class.
** EVL 20220330 Fixed regression for stream reset/reusage.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.util;
import java.io.*;
import java.nio.*;
/**
* This class implements a cached instance of an input stream. The whole stream is cached so using instances
* of these objects for larger streams for longer time is not recommended.
* <p>
* The primary role of this class is to allow unpacked or serialized streams (from jars, or other sources) to
* support {@code mark()} / {@code restore()} operations.
* <p>
* Secondly, if the stream is traversed multiple time, the cache component is a performance improvement.
*/
public class CachedInputStream
extends FileStream
{
/** The initial full size of the stream. */
private final long len;
/** The input stream used to populate internal memory buffer. */
InputStream srcStream = null;
/**
* Default constructor. Use a true {@code InputStream} as source for data.
*
* @param stream
* The stream to be cached.
*
* @throws IOException
* if the stream's size cannot be queried or the stream cannot be read.
*/
public CachedInputStream(InputStream stream)
throws IOException
{
len = stream.available();
srcStream = stream;
// read full stream content into memory buffer
map(0);
}
/**
* Default constructor. Use a {@code byte} array as source for data.
* <p>
* For performance reasons, the data in parameter is NOT copied locally, but it is guaranteed, the byte
* array is not exposed externally and not otherwise modified.
*
* @param ba
* The data to be cached.
*/
public CachedInputStream(byte[] ba)
// throws IOException
{
// not clear if this constructor version will ever be used, kept for compatibility
len = ba.length;
binaryBuffer = ByteBuffer.allocate((int)len);
binaryBuffer.put(ba);
binaryBuffer.rewind();
}
/**
* The length of the stream in bytes. This does not work for "streams" that require sequential access.
*
* @return The length of the stream.
*/
@Override
public long getLen()
throws IOException
{
return len;
}
/**
* Truncates or extends the stream to the specified length if this stream supports such an operation. If
* truncation is requested, all data located after this point in the file is discarded. If extending the
* file is requested, the values of the data in the extended portion of the file is undefined.
*
* @param len
* The new length of the file.
*
* @throws UnsupportedOperationException
* Always.
*/
@Override
public void setLen(long len)
throws UnsupportedOperationException
{
throw new UnsupportedOperationException("Cannot invoke setLen() to an input stream.");
}
/**
* Write the given character to the output stream.
*
* @param ch
* The character to be written.
*
* @throws UnsupportedOperationException
* Always.
*/
@Override
public void writeCh(char ch)
throws UnsupportedOperationException
{
throw new UnsupportedOperationException("Cannot invoke writeCh() on an input stream.");
}
/**
* Write the given byte to the output stream.
*
* @param b
* The byte to be written.
*
* @throws UnsupportedOperationException
* Always.
*/
@Override
public void writeByte(byte b)
throws UnsupportedOperationException
{
throw new UnsupportedOperationException("Cannot invoke writeCh() on an input stream.");
}
/**
* Write the given string to the output stream.
*
* @param data
* The data to be written.
*
* @throws UnsupportedOperationException
* Always.
*/
@Override
public void write(String data)
throws UnsupportedOperationException
{
throw new UnsupportedOperationException("Cannot invoke write() on an input stream.");
}
/**
* Write the given byte array to the output stream.
*
* @param data
* The data to be written.
*
* @throws UnsupportedOperationException
* Always.
*/
@Override
public void write(byte[] data)
throws UnsupportedOperationException
{
throw new UnsupportedOperationException("Cannot invoke write() on an input stream.");
}
/**
* Write the specified range of bytes from the given byte array to the output stream.
*
* @param data
* The data to be written.
* @param off
* Starting offset in data from which to read bytes to be written. Must be non-negative and
* {@code < data.length}.
* @param len
* Length of data to be written. Must be non-negative and {@code <= (data.length - offset)}.
*
* @throws UnsupportedOperationException
* Always.
*/
@Override
public void write(byte[] data, int off, int len)
throws UnsupportedOperationException
{
throw new UnsupportedOperationException("Cannot invoke write() on an input stream.");
}
/**
* Closes the input stream and releases OS resources associated with it.
*/
@Override
public void closeIn()
{
// nothing to do
}
/**
* Closes the output stream and releases OS resources associated with it.
*/
@Override
public void closeOut()
{
// nothing to do
}
/**
* Closes both the input and output streams and releases OS resources associated with it.
*/
@Override
public void close()
{
// nothing to do
}
/**
* State of the input side of the stream.
*
* @return {@code true} if the input side of the stream is active.
*/
@Override
public boolean isIn()
{
return true;
}
/**
* State of the output side of the stream.
*
* @return {@code true} if the output side of the stream is active.
*/
@Override
public boolean isOut()
{
return false;
}
/**
* Assigns the internal stream reference to the given reference.
*
* @param stream
* The new internal stream reference to use for all operations.
*
* @throws UnsupportedOperationException
* Always.
*/
@Override
public void assign(Stream stream)
throws UnsupportedOperationException
{
throw new UnsupportedOperationException("Cannot invoke assign() on an cached input stream.");
}
/**
* Initialize the binary buffer storage with content of the file or steam to be read depending on class
* implementation..
*
* @param start
* The starting position inside source buffer. Not used in this class implementation.
* @param size
* The size of the binary buffer to re-allocate.
*
* @throws IOException
* In the event of an error when decoding the bytes.
*/
protected void initBinaryBuffer(long start, long size)
throws IOException
{
byte[] buf = new byte[(int)size];
if (size > 0)
{
int remainder = (int)size;
int chunksize = 10240;
int lpos = 0;
int retsize = 0;
do
{
retsize = srcStream.read(buf, lpos, Math.min(chunksize, remainder));
lpos += retsize;
remainder -= retsize;
}
while (retsize > 0 && remainder > 0);
binaryBuffer = ByteBuffer.allocate((int)size);
binaryBuffer.put(buf);
binaryBuffer.rewind();
}
}
/**
* Clear/release the memory mapped buffer.
*/
protected void unmap()
{
// do nothing for this stream
}
}