# 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
       }
    }