All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.io.MultiByteArrayInputStream Maven / Gradle / Ivy

There is a newer version: 24.03
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.io;


import java.io.InputStream;
import java.io.IOException;


/**
* Reads binary data from a series of byte arrays.
*
* @author cp  2001.11.03
*/
public class MultiByteArrayInputStream
        extends InputStream
        implements InputStreaming
    {
    // ----- constructors ---------------------------------------------------

    /**
    * Construct a MultiByteArrayInputStream.
    *
    * @param aab  a non-null array of byte arrays of data to stream, each
    *             byte array must be non-null and non-zero-length
    */
    public MultiByteArrayInputStream(byte[][] aab)
        {
        this(aab, false);
        }

    /**
    * Construct a MultiByteArrayInputStream.
    *
    * @param aab           a non-null array of byte arrays of data to stream, each
    *                      byte array must be non-null and non-zero-length
    * @param fDestructive  if true the supplied array will be cleared as it is
    *                      traversed, stream mark positions will be respected
    */
    public MultiByteArrayInputStream(byte[][] aab, boolean fDestructive)
        {
        m_aabArray     = aab;
        m_fDestructive = fDestructive;
        if (aab == null || aab.length == 0)
            {
            m_ab   = EMPTY_BYTES;
            m_fEOF = true;
            }
        else
            {
            m_ab   = aab[0];
            m_fEOF = aab.length == 1 && aab[0].length == 0;
            }
        }


    // ----- InputStream implementation -------------------------------------

    /**
    * Reads the next byte of data from the input stream. The value byte is
    * returned as an int in the range 0 to
    * 255. If no byte is available because the end of the stream
    * has been reached, the value -1 is returned. This method
    * blocks until input data is available, the end of the stream is detected,
    * or an exception is thrown.
    *
    * @return     the next byte of data, or -1 if the end of the
    *             stream is reached.
    * @exception  IOException  if an I/O error occurs.
    */
    public int read() throws IOException
        {
        if (m_fEOF)
            {
            return -1;
            }

        // check "available"
        byte[] ab  = m_ab;
        int    cb  = ab.length;
        int    of  = m_of;
        int    n   = ab[of++] & 0xFF;
        if (of == cb)
            {
            byte[][] aab = m_aabArray;
            int      cab = aab.length;
            int      iab = m_iArray;

            // destroy chunk
            if (m_fDestructive && iab < m_iArrayMarked)
                {
                aab[iab] = null;
                }

            // check for EOF
            if (iab == cab - 1)
                {
                m_fEOF = true;
                }
            else
                {
                m_iArray = ++iab;
                m_ab     = aab[iab];
                m_of     = 0;
                }
            }
        else
            {
            m_of = of;
            }

        return n;
        }

    /**
    * Reads up to len bytes of data from the input stream into
    * an array of bytes.  An attempt is made to read as many as
    * len bytes, but a smaller number may be read, possibly
    * zero. The number of bytes actually read is returned as an integer.
    *
    * @param abDest  the buffer into which the data is read
    * @param ofDest  the start offset in array b
    *                at which the data is written
    * @param cbDest  the maximum number of bytes to read
    *
    * @return the total number of bytes read into the buffer, or
    *         -1 if there is no more data because the end of
    *         the stream has been reached.
    */
    public int read(byte abDest[], int ofDest, int cbDest) throws IOException
        {
        if (m_fEOF)
            {
            return -1;
            }

        int cbRead = 0;
        while (true)
            {
            byte[] ab     = m_ab;
            int    of     = m_of;
            int    cb     = ab.length;
            int    cbLeft = cb - of;

            // check if read can be handled inside the current chunk
            if (cbDest < cbLeft)
                {
                System.arraycopy(ab, of, abDest, ofDest, cbDest);
                m_of    = of + cbDest;
                cbRead += cbDest;
                return cbRead;
                }

            // copy what is available from the current chunk
            System.arraycopy(ab, of, abDest, ofDest, cbLeft);
            cbRead += cbLeft;

            byte[][] aab = m_aabArray;
            int      cab = aab.length;
            int      iab = m_iArray;

            // destroy chunk
            if (m_fDestructive && iab < m_iArrayMarked)
                {
                aab[iab] = null;
                }

            // check for EOF
            if (iab == cab - 1)
                {
                m_of   = ab.length;
                m_fEOF = true;
                return cbRead;
                }

            // advance to next chunk
            m_iArray  = ++iab;
            m_ab      = aab[iab];
            m_of      = 0;

            ofDest += cbLeft;
            cbDest -= cbLeft;
            }
        }

    /**
    * Returns the number of bytes that can be read (or skipped over) from
    * this input stream without blocking by the next caller of a method for
    * this input stream.  The next caller might be the same thread or or
    * another thread.
    *
    * @return     the number of bytes that can be read from this input stream
    *             without blocking.
    */
    public int available()
        {
        if (m_fEOF)
            {
            return 0;
            }

        byte[][] aab = m_aabArray;
        int      of  = m_of;
        int      cb  = 0;
        for (int i = m_iArray, c = aab.length; i < c; ++i)
            {
            cb += aab[i].length - of;
            of  = 0;
            }
        return cb;
        }

    /**
    * Skips over and discards n bytes of data from this input
    * stream. The skip method may, for a variety of reasons, end
    * up skipping over some smaller number of bytes, possibly 0.
    * This may result from any of a number of conditions; reaching end of file
    * before n bytes have been skipped is only one possibility.
    * The actual number of bytes skipped is returned.  If n is
    * negative, no bytes are skipped.
    *
    * @param n  the number of bytes to be skipped
    *
    * @return the actual number of bytes skipped
    */
    public long skip(long n)
        {
        if (n < 0L || n > Integer.MAX_VALUE)
            {
            throw new IllegalArgumentException("out of bounds: skip(n=" + n + ")");
            }

        if (m_fEOF)
            {
            return 0L;
            }

        byte[] ab = m_ab;
        int    of = m_of;
        int    cb = ab.length;

        int    cbSkip = (int) n;
        int    cbLeft = cb - of;

        // check if skip occurs inside the current chunk
        if (cbSkip < cbLeft)
            {
            m_of = of + cbSkip;
            return cbSkip;
            }

        byte[][] aab = m_aabArray;
        int      cab = aab.length;
        int      iab = m_iArray;

        // destroy chunk
        if (m_fDestructive && iab < m_iArrayMarked)
            {
            aab[iab] = null;
            }

        // check for EOF
        if (iab == cab - 1 && cbSkip > cbLeft)
            {
            m_of   = ab.length;
            m_fEOF = true;
            return cbLeft;
            }

        // advance to next chunk (recursively)
        m_iArray  = ++iab;
        m_ab      = aab[iab];
        m_of      = 0;
        return cbLeft + skip(cbSkip - cbLeft);
        }

    /**
    * Close the stream.
    */
    public void close()
        {
        }

    /**
    * Marks the current position in this input stream. A subsequent call to
    * the reset method repositions this stream at the last
    * marked position so that subsequent reads re-read the same bytes.
    *
    * @param readlimit  the maximum limit of bytes that can be read before
    *                   the mark position becomes invalid
    */
    public void mark(int readlimit)
        {
        int iMarkOld = m_iArrayMarked;
        int iMarkNew = m_iArrayMarked = m_iArray;

        m_ofMarked = m_of;

        // destroy chunks between old and new mark
        if (m_fDestructive && iMarkOld != MARK_UNSET)
            {
            for (byte[][] aabArray = m_aabArray; iMarkOld < iMarkNew; ++iMarkOld)
                {
                aabArray[iMarkOld] = null;
                }
            }
        }

    /**
    * Repositions this stream to the position at the time the
    * mark method was last called on this input stream.
    *
    * @throws IOException if the stream has not been marked
    */
    public void reset()
        throws IOException
        {
        int iMarked = m_iArrayMarked;
        if (iMarked == MARK_UNSET)
            {
            throw new IOException("the stream has not been marked");
            }

        byte[][] aabArray = m_aabArray;

        m_iArray = iMarked;
        m_of     = m_ofMarked;
        m_ab     = aabArray[iMarked];
        m_fEOF   = iMarked == aabArray.length - 1 && m_of == m_ab.length;
        }

    /**
    * Tests if this input stream supports the mark and
    * reset methods. The markSupported method
    * of InputStream returns false.
    *
    * @return  true if this true type supports the mark and
    *          reset method; false otherwise
    */
    public boolean markSupported()
        {
        return true;
        }

    
    // ----- data members ---------------------------------------------------

    /**
    * Empty array of bytes.
    */
    protected static final byte[] EMPTY_BYTES = new byte[0];

    /**
    * Marker position indicating that stream is not marked.
    */
    protected static final int MARK_UNSET = Integer.MAX_VALUE;

    /**
    * True after eof is determined.
    */
    protected boolean m_fEOF;

    /**
    * True iff the array will be null'd out as it is traversed.
    */
    protected boolean m_fDestructive;

    /**
    * The array of byte arrays.
    */
    protected byte[][] m_aabArray;

    /**
    * The index of the current byte array.
    */
    protected int m_iArray;

    /**
    * The current byte array.
    */
    protected byte[] m_ab;

    /**
    * The current offset in the current byte array.
    */
    protected int m_of;

    /**
    * The index of the marked byte array.
    */
    protected int m_iArrayMarked = MARK_UNSET;

    /**
    * The marked offset in the marked byte array.
    */
    protected int m_ofMarked;
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy