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

com.oracle.coherence.common.io.BufferSequenceInputStream Maven / Gradle / Ivy

/*
 * 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.oracle.coherence.common.io;


import java.io.InputStream;
import java.io.IOException;
import java.io.DataInput;
import java.io.EOFException;
import java.io.DataInputStream;
import java.io.UTFDataFormatException;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;


/**
* An InputStream implementation on top of a BufferSequence.
*
* @author cp/mf  2010.12.08
*/
public class BufferSequenceInputStream
        extends InputStream
        implements DataInput
    {
    // ----- constructors ---------------------------------------------------

    /**
    * Construct a BufferSequenceInputStream over a BufferSequence object.
    *
    * If it is desired for the BufferSequence to be disposed of when the stream
    * is closed then {@link #BufferSequenceInputStream(BufferSequence, boolean)}
    * should be used.
    *
    * @param bufseq  the BufferSequence to read the data from
    */
    public BufferSequenceInputStream(BufferSequence bufseq)
        {
        this (bufseq, false);
        }

    /**
     * Construct a BufferSequenceInputStream over a BufferSequence object.
     *
     * @param bufseq        the BufferSequence to read the data from
     * @param fAutoDispose  true if the sequence should be disposed of when the stream is closed,
     *                      or an unmarked stream is fully consumed
     */
    public BufferSequenceInputStream(BufferSequence bufseq, boolean fAutoDispose)
        {
        m_bufseq       = bufseq;
        m_cb           = bufseq.getLength();
        m_cbMark       = m_cb;
        f_fAutoDispose = fAutoDispose;
        }

    // ----- BufferSequenceInputStream interface ----------------------------

    /**
     * Fill the supplied buffer using the contents of the stream.
     *
     * @param bufDst  the buffer to fill, the position will be updated per {@link ByteBuffer#put}
     */
    public void read(ByteBuffer bufDst)
        {
        for (ByteBuffer bufUnsafe = ensureBuffer(); bufDst.hasRemaining() && bufUnsafe != null; bufUnsafe = ensureBuffer())
            {
            int nPosSrc = m_nPosBuf;
            int cbCopy  = Math.min(m_nLimBuf - nPosSrc,  bufDst.remaining());

            consumeBytes(cbCopy);
            Buffers.copy(bufUnsafe, nPosSrc, cbCopy, bufDst);
            m_nPosBuf += cbCopy;
            }
        }

    /**
     * Return the current buffer which is being read from.
     * 

* Note that the validity of the returned ByteBuffer is tied to that of the BufferSequence. * As this is the buffer currently being consumed by the stream any positional changes made * to this buffer may invalidate the stream, thus in general it should either be used for * "peeking", or just before closing the stream. * * @return the current buffer which is being read from. */ public ByteBuffer getCurrentBuffer() { ByteBuffer bufUnsafe = m_bufUnsafe; if (bufUnsafe == null) { bufUnsafe = ensureBuffer(); } bufUnsafe = bufUnsafe.duplicate(); bufUnsafe.position(m_nPosBuf).limit(m_nLimBuf); return bufUnsafe; } /** * Reset the stream to operate on a new BufferSequence. *

* This is the equivalent of closing this stream and opening a new one. *

* * @param bufseq the new sequence to operate against * * @return this stream */ public BufferSequenceInputStream reset(BufferSequence bufseq) { close(); m_bufseq = bufseq; m_cb = bufseq.getLength(); m_cbMark = m_cb; m_ofNext = 0; m_ofMark = 0; m_posMark = 0; m_bufUnsafe = Buffers.getEmptyBuffer(); m_cbLimit = 0; m_nPosBuf = 0; m_nLimBuf = 0; return this; } // ----- DataInput interface -------------------------------------------- /** * {@inheritDoc} */ @Override public void readFully(byte[] ab) throws IOException { readFully(ab, 0, ab.length); } /** * {@inheritDoc} */ @Override public void readFully(byte[] ab, int off, int len) throws IOException { if (read(ab, off, len) < len) { throw new EOFException(); } } /** * {@inheritDoc} */ @Override public int skipBytes(int n) throws IOException { return (int) skip(n); } /** * {@inheritDoc} */ @Override public boolean readBoolean() throws IOException { int n = read(); if (n < 0) { throw new EOFException(); } return n != 0; } /** * {@inheritDoc} */ @Override public byte readByte() throws IOException { int n = read(); if (n < 0) { throw new EOFException(); } return (byte) n; } /** * {@inheritDoc} */ @Override public int readUnsignedByte() throws IOException { int n = read(); if (n < 0) { throw new EOFException(); } return n; } /** * {@inheritDoc} */ @Override public short readShort() throws IOException { ByteBuffer buf = ensureBuffer(2); return buf.getShort(buf == m_bufTmp ? 0 : m_nPosBuf - 2); } /** * {@inheritDoc} */ @Override public int readUnsignedShort() throws IOException { int ch1 = read(); int ch2 = read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (ch1 << 8) + ch2; } /** * {@inheritDoc} */ @Override public char readChar() throws IOException { ByteBuffer buf = ensureBuffer(2); return buf.getChar(buf == m_bufTmp ? 0 : m_nPosBuf - 2); } /** * {@inheritDoc} */ @Override public int readInt() throws IOException { ByteBuffer buf = ensureBuffer(4); return buf.getInt(buf == m_bufTmp ? 0 : m_nPosBuf - 4); } /** * {@inheritDoc} */ @Override public long readLong() throws IOException { ByteBuffer buf = ensureBuffer(8); return buf.getLong(buf == m_bufTmp ? 0 : m_nPosBuf - 8); } /** * {@inheritDoc} */ @Override public float readFloat() throws IOException { ByteBuffer buf = ensureBuffer(4); return buf.getFloat(buf == m_bufTmp ? 0 : m_nPosBuf - 4); } /** * {@inheritDoc} */ @Override public double readDouble() throws IOException { ByteBuffer buf = ensureBuffer(8); return buf.getDouble(buf == m_bufTmp ? 0 : m_nPosBuf - 8); } /** * The readLine functionality was {@link DataInputStream#readLine * deprecated} as of JDK 1.1. This implementation will always throw an * UnsupportedOperationException. * * @throws UnsupportedOperationException always */ @Override @Deprecated public String readLine() { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public String readUTF() throws IOException { // Note: implementation borrowed from java.io.DataInputStream int utflen = readUnsignedShort(); byte[] bytearr = new byte[utflen]; char[] chararr = new char[utflen]; int c, char2, char3; int count = 0; int chararr_count = 0; readFully(bytearr, 0, utflen); while (count < utflen) { c = (int) bytearr[count] & 0xff; if (c > 127) { break; } count++; chararr[chararr_count++] = (char) c; } while (count < utflen) { c = (int) bytearr[count] & 0xff; switch (c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: /* 0xxxxxxx*/ count++; chararr[chararr_count++] = (char) c; break; case 12: case 13: /* 110x xxxx 10xx xxxx*/ count += 2; if (count > utflen) { throw new UTFDataFormatException( "malformed input: partial character at end"); } char2 = (int) bytearr[count - 1]; if ((char2 & 0xC0) != 0x80) { throw new UTFDataFormatException( "malformed input around byte " + count); } chararr[chararr_count++] = (char) (((c & 0x1F) << 6) | (char2 & 0x3F)); break; case 14: /* 1110 xxxx 10xx xxxx 10xx xxxx */ count += 3; if (count > utflen) { throw new UTFDataFormatException( "malformed input: partial character at end"); } char2 = (int) bytearr[count - 2]; char3 = (int) bytearr[count - 1]; if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) { throw new UTFDataFormatException( "malformed input around byte " + (count - 1)); } chararr[chararr_count++] = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | (char3 & 0x3F)); break; default: /* 10xx xxxx, 1111 xxxx */ throw new UTFDataFormatException( "malformed input around byte " + count); } } // The number of chars produced may be less than utflen return new String(chararr, 0, chararr_count); } // ----- InputStream implementation ------------------------------------- /** * {@inheritDoc} */ public int read() throws IOException { ByteBuffer buf = ensureBuffer(); if (m_nPosBuf < m_nLimBuf) { consumeBytes(1); return ((int) buf.get(m_nPosBuf++)) & 0xFF; } return -1; // for read() and read(byte[]) we don't throw } /** * {@inheritDoc} */ public int read(byte abDest[], int ofDest, int cbDest) throws IOException { if (abDest == null || ofDest < 0 || cbDest < 0 || ofDest + cbDest > abDest.length) { if (abDest == null) { throw new IllegalArgumentException("null byte array"); } else { throw new IllegalArgumentException( "abDest.length=" + abDest.length + ", ofDest=" + ofDest + ", cbDest=" + cbDest); } } int cbResult = 0; do { ByteBuffer bufDupe = ensureBuffer().duplicate(); // duplicate so we can use bulk-get below bufDupe.position(m_nPosBuf).limit(m_nLimBuf); int posStart = bufDupe.position(); bufDupe.get(abDest, ofDest, Math.min(cbDest, bufDupe.remaining())); int cbRead = bufDupe.position() - posStart; if (cbRead == 0 && m_cb == 0) { break; // EOS } consumeBytes(cbRead); // must be in loop to allow for above check m_cb check to work m_nPosBuf += cbRead; cbResult += cbRead; ofDest += cbRead; cbDest -= cbRead; } while (cbDest > 0); return cbResult == 0 && m_cb == 0 ? -1 : cbResult; } /** * {@inheritDoc} */ public long skip(long lcb) throws IOException { if (lcb <= 0) { return 0; } int cb = (int) Math.min(Integer.MAX_VALUE, lcb); int cbResult = 0; while (cb > 0) { ensureBuffer(); // we don't actually use the buffer, but this advances it if empty int cbSkip = Math.min(cb, m_nLimBuf - m_nPosBuf); if (cbSkip == 0 && m_cb == 0) { break; } consumeBytes(cbSkip); // must be in loop for above m_cb check m_nPosBuf += cbSkip; cb -= cbSkip; cbResult += cbSkip; } return cbResult; } /** * {@inheritDoc} */ public int available() throws IOException { return (int) Math.min(Integer.MAX_VALUE, m_cb); // 0 on EOS } /** * {@inheritDoc} */ public void mark(int readlimit) { ensureBuffer(); m_cbLimit = readlimit; m_cbMark = m_cb; m_ofMark = m_ofNext - 1; m_posMark = m_nPosBuf; } /** * {@inheritDoc} */ public void reset() throws IOException { long cbMark = m_cbMark; BufferSequence bufseq = m_bufseq; long cb = m_cb; long cbBack = cbMark - cb; if (cbBack == 0) { // noop, we only do this check in case we are doing a mark/reset on an auto-closed stream // we want to avoid it looking any different then having done it on an EOS return; } else if (cbBack > m_cbLimit) { // Note we could just treat the limit as a hint, but since we can auto-dispose from it we treat it // as a true limit in order to provide consistent behavior throw new IOException("Invalid mark, limit exceeded"); } else if (bufseq == null) // explicitly closed stream { throw new IOException("Closed stream"); } int of = m_ofMark; m_bufUnsafe = bufseq.getUnsafeBuffer(of); m_nLimBuf = bufseq.getBufferLimit(of); m_nPosBuf = m_posMark; m_ofNext = of + 1; m_cb = cbMark; } /** * {@inheritDoc} */ public boolean markSupported() { return true; } /** * {@inheritDoc} */ public void close() { BufferSequence bufSeq = m_bufseq; m_bufUnsafe = null; m_bufseq = null; m_bufTmp = null; m_cb = 0; // for subsequent read(byte[]) calls m_nLimBuf = 0; m_nPosBuf = 0; if (bufSeq != null && f_fAutoDispose) { bufSeq.dispose(); } } // ----- helpers -------------------------------------------------------- /** * Consume the specified number of bytes. * * @param cb the number of bytes consumed. */ protected final void consumeBytes(int cb) { if ((m_cb -= cb) == 0 && f_fAutoDispose && m_cbMark > m_cbLimit) { close(); } } /** * Obtain the next ByteBuffer that this InputStream is based on. * * The returned buffer will be big endian, and the caller must not modify the buffers positional properties, and use/update m_nPosBuf and m_nLimBuf instead. * * @return the underlying ByteBuffer */ protected final ByteBuffer ensureBuffer() { BufferSequence bufseq = m_bufseq; if (bufseq == null) { m_nPosBuf = m_nLimBuf = 0; return Buffers.getEmptyBuffer(); } ByteBuffer bufUnsafe = m_bufUnsafe; int of = m_ofNext; int cBuf = bufseq.getBufferCount(); while (m_nPosBuf == m_nLimBuf && of < cBuf) { bufUnsafe = bufseq.getUnsafeBuffer(of); m_bufUnsafe = bufUnsafe.order() == ByteOrder.BIG_ENDIAN ? bufUnsafe : bufUnsafe.duplicate().order(ByteOrder.BIG_ENDIAN); m_nPosBuf = bufseq.getBufferPosition(of); m_nLimBuf = bufseq.getBufferLimit(of); ++of; } m_ofNext = of; return bufUnsafe; } /** * Helper function which returns a buffer containing at least the specified number of readable bytes which * will have already been logically consumed from the stream. * * If the returned buffer == m_bufTmp then a read may be performed from position 0, otherwise the read * must be at location m_nPos - cb. * * @param cb the number of bytes to read, maximum of 8 * * @return the temp buffer */ private final ByteBuffer ensureBuffer(int cb) throws IOException { int cbUnsafe = m_nLimBuf - m_nPosBuf; ByteBuffer bufUnsafe = m_bufUnsafe; if (cbUnsafe == 0) { bufUnsafe = ensureBuffer(); cbUnsafe = m_nLimBuf - m_nPosBuf; } if (cbUnsafe >= cb) { consumeBytes(cb); m_nPosBuf += cb; return bufUnsafe; } else { ByteBuffer buffTmp = m_bufTmp; if (buffTmp == null) { m_bufTmp = buffTmp = ByteBuffer.allocate(8); } buffTmp.position(0).limit(cb); // will throw if cb > 8 readFully(buffTmp.array(), 0, cb); // updates m_nPosBuf return buffTmp; } } // ----- data members --------------------------------------------------- /** * The BufferSequence object from which data is read. */ protected BufferSequence m_bufseq; /** * A temporary byte buffer which can be used for decoding purposes. */ private ByteBuffer m_bufTmp; /** * The number of bytes remaining in the stream. */ protected long m_cb; /** * The offset of the next ByteBuffer to read from. */ protected int m_ofNext; /** * The offset of the buffer associated with the mark. */ protected int m_ofMark; /** * The buffer position of the buffer associated with the mark. */ protected int m_posMark; /** * The number of bytes remaining in the stream after the mark. */ protected long m_cbMark; /** * The limit associated with the mark. */ protected int m_cbLimit; /** * True if the sequence should be disposed of when the stream is closed. */ protected final boolean f_fAutoDispose; /** * The ByteBuffer object from which data is read. */ protected ByteBuffer m_bufUnsafe = Buffers.getEmptyBuffer(); /** * The position in the m_bufUnsafe */ protected int m_nPosBuf; /** * the limit in m_bufUnsafe; */ protected int m_nLimBuf; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy