/*
 *
 * FileName: ByteUnPackInputStream.java 
 *
 *
 * $Author: konki $
 * Author(s): konki 
 *
 *
 * Objective: 
 * Enable reading data to an internal buffer for easy data reception
 *
 *
 * $Log: ByteUnPackInputStream.java,v $
# Revision 1.3  1996/04/01  21:23:06  konki
# Added expand( int size )
#
# Revision 1.2  1996/02/21  00:24:48  konki
# fixed some comments
#
# Revision 1.1  1996/02/18  23:09:35  konki
# Initial revision
#
 * Change Log: 
 * Created on Fri Feb  9 12:13:13 EST 1996
 * Changes:                             Date     Initials
 *
 *
 * Notes: 
 * $Id: ByteUnPackInputStream.java,v 1.3 1996/04/01 21:23:06 konki Exp $
 *
 */


package wb;

import java.io.*;
import java.lang.*;

/**
 * This class lets you read primitive Java data types
 * from a local byte array after the data is read in one single block
 * in a portable way. Primitive data types are well
 * understood types with associated operations.  For example, an 
 * Integer is considered to be a good primitive data type.
 *
 * The byte data can be generated using a BytePackOutputStream.
 */

public class ByteUnPackInputStream 
{
    /**
     *  The buffer where data is stored.
     */
    protected byte buf[];

    /**
     * The number of bytes in the buffer.
     */
    protected int count;

    /**
     * Read index of the local byte buffer
     */
    private int rindex;


    /**
     * The real stream from which the byte array will be filled with data
     */
    protected InputStream instream;


    /**
     * Reads one byte and returns it as an integer.
     * Assumes that the byte array has at least one byte. 
     * @return the int value of the byte
     */
    private int getInt()
    {
        return( (buf[rindex++] & 0xFF) );
    }


    /**
     * Private method to expand the size of the local buffer to accommodate
     * new data. grows in step of 512 bytes.
     */
    private synchronized void expand()
    {
        byte newbuf[] = new byte[buf.length+512];
        System.arraycopy(buf, 0, newbuf, 0, count);
        buf = newbuf;
    }

    /**
     * Private method to expand the size of the local buffer to accommodate
     * new data. grows in step of 512 bytes.
     */
    private synchronized void expand(int num)
    {
        byte newbuf[] = new byte[buf.length+num+128];
        System.arraycopy(buf, 0, newbuf, 0, count);
        buf = newbuf;
    }

    /**
     * Creates a new ByteUnPackInputStream.
     * @param in	the output stream
     */
    public ByteUnPackInputStream(InputStream in) 
    {
	instream = in;
        count = rindex = 0;
        buf = new byte[512];
    }

    /**
     * Fills the byte buffer by reading data from the stream.
     * @return the actual number of bytes read, -1 is returned when the end of
     * stream is reached
     * @exception IOException If an I/O error has occurred
     */
    public synchronized int fill() throws IOException
    {
	int len;
        len = instream.read(buf, count, (buf.length-count));
	count += len;
        return len;
    }

    /**
     * Fills the byte buffer by reading data from the stream.
     * @param num the number of bytes to be read
     * @return the actual number of bytes read, -1 is returned when the end of
     * stream is reached
     * @exception IOException If an I/O error has occurred
     * @exception EOFException If EOF is reached before all "num" bytes are read
     */
    public synchronized int fill( int num ) throws IOException
    {
	int len;
        if ( buf.length < (count+num))
            expand(num);
        len = instream.read(buf, count, num);
	count += len;
        return len;
    }

    /**
     * Read a boolean.
     * @returns the boolean read from the localbuffer
     * @exception IOException If there is insufficient data in the localbuffer
     */
    public synchronized boolean readBoolean()  throws IOException
    {
        if ( count < (rindex + 1) ) {
            throw new IOException("Insufficient Data");
        }
	return ( (getInt() == 0) ? false : true );
    }

    /**
     * Reads an 8 bit byte.
     * @returns the byte value read 
     * @exception IOException If there is insufficient data in the localbuffer
     */
    public synchronized byte readByte() throws IOException 
    {
        if ( count < (rindex + 1) ) {
            throw new IOException("Insufficient Data");
        }
	return ( buf[rindex++] );
    }

    /**
     * Reads a 16 bit short.
     * @returns the 16 bit short read 
     * @exception IOException If there is insufficient data in the localbuffer
     */
    public synchronized short readShort() throws IOException
    {
        if ( count < (rindex + 2) ) {
            throw new IOException("Insufficient Data");
        }
	int msb = getInt();
	int lsb = getInt();
	return  (short)((msb << 8) + (lsb << 0));
    }

    /**
     * Reads a 16 bit char.
     * @returns the char read
     * @exception IOException If there is insufficient data in the localbuffer
     */
    public synchronized char readChar() throws IOException
    {
        if ( count < (rindex + 2) ) {
            throw new IOException("Insufficient Data");
        }
        int msb = getInt();
        int lsb = getInt();
        return  (char)((msb << 8) + (lsb << 0));
    }

    /**
     * Reads a 32 bit int.
     * @returns the 32 bit int read
     * @exception IOException If there is insufficient data in the localbuffer
     */
    public synchronized int readInt() throws IOException 
    {
        if ( count < (rindex + 4) ) {
            throw new IOException("Insufficient Data");
        }
        int msba = getInt();
        int msbb = getInt();
        int lsba = getInt();
        int lsbb = getInt();
        return ((msba << 24) + (msbb << 16) + (lsba << 8) + (lsbb << 0));
    }

    /**
     * Reads a 64 bit long.
     * @returns the 64 bit long read
     * @exception IOException If there is insufficient data in the localbuffer
     */
    public synchronized long readLong() throws IOException 
    {
        if ( count < (rindex + 8) ) {
            throw new IOException("Insufficient Data");
        }
        int msba = getInt();
        int msbb = getInt();
        int msbc = getInt();
        int msbd = getInt();
        int lsba = getInt();
        int lsbb = getInt();
        int lsbc = getInt();
        int lsbd = getInt();
	return ( 
           (((msba << 24)+(msbb << 16)+(msbc << 8)+(msbd << 0)) << 32L) +
           (((lsba << 24)+(lsbb << 16)+(lsbc << 8)+(lsbd << 0)) & 0xFFFFFFFFL)
               );
    }

    /**
     * Reads a 32 bit float.
     * @returns the 32 bit float read
     * @exception IOException If there is insufficient data in the localbuffer
     */
    public synchronized float readFloat() throws IOException
    {
        if ( count < (rindex + 4) ) {
            throw new IOException("Insufficient Data");
        }
        int msba = getInt();
        int msbb = getInt();
        int lsba = getInt();
        int lsbb = getInt();
	return ( Float.intBitsToFloat((msba << 24)+ (msbb << 16)
                +(lsba << 8) + (lsbb << 0)));
    }

    /**
     * Reads a 64 bit double.
     * @returns the 64 bit double read
     * @exception IOException If there is insufficient data in the localbuffer
     */
    public synchronized double readDouble() throws IOException 
    { 
        if ( count < (rindex + 8) ) {
            throw new IOException("Insufficient Data");
        }
        int msba = getInt();
        int msbb = getInt();
        int msbc = getInt();
        int msbd = getInt();
        int lsba = getInt();
        int lsbb = getInt();
        int lsbc = getInt();
        int lsbd = getInt();
	return ( Double.longBitsToDouble( 
           (((msba << 24)+(msbb << 16)+(msbc << 8)+(msbd << 0)) << 32L) +
           (((lsba << 24)+(lsbb << 16)+(lsbc << 8)+(lsbd << 0)) & 0xFFFFFFFFL)
               ));
    }

    /**
     * Reads a String 
     * @returns a read String that has been terminated by a \n, \r, \r\n or EOF
     *          if unterminated it returns all the available. 
     * @exception IOException If there is insufficient data in the localbuffer
     */
    public synchronized String readChars(String s) throws IOException
    {
        int len,msb,lsb,lcount;
        char inchar;
        if ( (len = count-rindex) <= 2 ) 
            throw new IOException("Insufficient Data");
        StringBuffer rstg = new StringBuffer(len);
        lcount = rindex;
loop:   while( count-lcount > 1 ) {
            msb = buf[lcount++]; 
            lsb = buf[lcount++]; 
            inchar = (char)((msb << 8) + (lsb << 0));
            switch((msb << 8) + (lsb << 0)) {
            case -1:
            case '\n':
                rstg.append(inchar);
                break loop;
            case '\r':
                if( (count-lcount>1) && 
                    (((buf[lcount]<<8)+(buf[lcount+1]<<0)) == '\n') ) {
                    rstg.append("\r\n");
                } else {
                    rstg.append("\r");
                }
                break loop;
            default:
                rstg.append(inchar);
                break;
            }
        }
        rindex = lcount; 
        return( rstg.toString());
    }

    /**
     * Returns the current size of the unread local buffer read from the stream 
     * when fill'd
     * @returns the size
     */
    public int size()
    {
        return (count-rindex);
    }
}



/*
 * End of File: ByteUnPackInputStream.java 
 */