
/* **************************************************************************

  $Archive: /njcl_v2rmi/src/com/novell/service/jncpv2r/misc/EndianInputStream.java $
  $Revision: 3 $
  $Modtime: 1/05/01 1:11p $
 
  Copyright (c) 1998 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/

package com.novell.service.jncpv2r.misc;


import java.io.InputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.EOFException;
import com.novell.service.jncpv2r.misc.EndianInputStream;


/**
 * Provides for writing primitive Java data types to a stream in an
 * endian specific way.
 *
 * <p>Primitive data types are well understood types with associated
 * operations. For example, an integer is considered to be a good
 * primitive data type. The data can be converted back using an
 * EndianInputStream.</p>
 * 
 * @see EndianInput
 */
public class EndianInputStream
   extends InputStream
   implements EndianInput
{
   /** @internal
    * The byte array containing the data. 
    */
   protected byte[] buffer;

   /** @internal
    * The index of the next character to read from the input stream buffer.
    */
   protected int position;

   /** @internal
    * The currently marked position in the stream.
    * EndianInputStreams are marked at position zero by
    * default when constructed.  They may be marked at another
    * position within the buffer by the <code>mark()</code> method.
    * The current buffer position is set to this point by the
    * <code>reset()</code> method.
    */
   protected int mark = 0;

   /** @internal
    * The index one greater than the last valid character in the input 
    * stream buffer. 
    *
    */
   protected int count;

   /**
    * Creates a new EndianInputStream object that reads data from the 
    * specified byte array, which is not copied. 
    *
    * @param buffer The input byte array buffer.
    */
   public EndianInputStream (
         byte buffer[])
   {
      this (buffer, 0, buffer.length);
   }

   /**
    * Creates a new EndianInputStream object that reads data from the 
    * specified byte array. Up to length characters are read from the
    * byte array, starting at the indicated offset. The byte array is
    * not copied. 
    *
    * @param buf    The input buffer.
    * @param offset The offset in the buffer for the first byte to read.
    * @param length The maximum number of bytes to read from the buffer.
    */
   public EndianInputStream (
         byte buffer[],
         int offset,
         int length)
   {
      this.buffer = buffer;
      this.position = offset;
      this.count = Math.min (offset + length, buffer.length);
      this.dis = new DataInputStream (this);
    }


   // ******************** InputStream Class ********************

   /**
    * Reads the next byte of data from this input stream. The byte
    * value 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. The read() method of cannot block. 
    *
    * @return The next byte of data, or -1 if the end of the
    *         stream has been reached.
    */
   public synchronized int read ()
   {
      return ((position < count) ? (buffer[position++] & 0xff) : -1);
   }

   /**
    * Reads up to the specified bytes of data into an array of bytes 
    * from this input stream. This read() method cannot block. 
    *
    * @param buffer The buffer into which the data is to be read.
    * @param offset The start offset of the data.
    * @param length The maximum number of bytes to read.
    *
    * @return The total number of bytes read into the buffer, or
    *         -1 if the end of the stream has been reached and
    *         there is no more data.
    */
   public synchronized int read (
         byte buffer[],
         int offset,
         int length)
   {
      if (position >= count)
      {
         return (-1);
      }
      if (position + length > count)
      {
         length = count - position;
      }
      if (length <= 0)
      {
         return (0);
      }
      System.arraycopy (this.buffer, position, buffer, offset, length);
      position += length;
      return (length);
   }

   /**
    * Skips the specified bytes of input from this input stream. Fewer 
    * bytes might be skipped if the end of the input stream is reached. 
    *
    * @param n The number of bytes to be skipped.
    *
    * @return The actual number of bytes skipped.
    */
   public synchronized long skip (
         long n)
   {
      if (position + n > count)
      {
         n = count - position;
      }
      if (n < 0)
      {
         return (0);
      }
      position += n;
      return (n);
   }

   /**
    * Returns the number of bytes that can be read from this input 
    * stream without blocking. The available() method returns the
    * value of count- position, which is the number of bytes remaining
    * to be read from the input buffer.
    *
    * @return The number of bytes that can be read from the input stream
    *          without blocking.
    */
   public synchronized int available ()
   {
      return (count - position);
   }

   /**
    * Determines if EndianInputStream supports mark() and reset().
    *
    * @ see #mark
    * @ see #reset
    */
   public boolean markSupported ()
   {
      return (true);
   }

   /**
    * Sets the current marked position in the stream. EndianInputStream
    * objects are marked by default at position zero when constructed.
    * They may be marked at another position within the buffer by this
    * method.
    *
    * @see #reset
    * @see #markSupported
    */
   public void mark (
         int markpos)
   {
      mark = position;
   }

   /**
    * Resets the buffer to the marked position. The marked position
    * is the beginning unless another position was marked.
    *
    * @see #mark
    * @see #markSupported
    */
   public synchronized void reset ()
   {
      position = mark;
   }


   // ******************** DataInput Interface ********************

   /*
    * Provide the methods to implement DataInput.
    * They delegate to an Instance of DataInputStream that
    * reads its input from the EndianInputStream.
    * This allows this stream to manage the blocked data
    * as necessary.
    */
   private DataInputStream dis;
    
   /**
    * Reads bytes of data into a buffer, blocking until all bytes are read.
    *
    * @param data The buffer into which the data is read
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final void readFully (
         byte[] data)
      throws IOException
   {
      dis.readFully (data);
   }

   /**
    * Reads bytes of data into a buffer, blocking until all bytes are read.
    *
    * @param b   The buffer into which the data is read
    * @param off The start offset of the data
    * @param len The maximum number of bytes to read
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final void readFully (
         byte[] data,
         int offset,
         int length)
      throws IOException
   {
      if (length < 0)
      {
         throw (new IndexOutOfBoundsException ());
      }
      dis.readFully (data, offset, length);
   }

   /**
    * Skips the specified bytes, block until all bytes are skipped.
    *
    * @param n The number of bytes to be skipped
    *
    * @return The actual number of bytes skipped.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final int skipBytes (
         int n)
      throws IOException
   {
      return (dis.skipBytes (n));
   }

   /**
    * Reads a boolean value.
    *
    * @return The boolean value read.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final boolean readBoolean ()
      throws IOException
   {
      return (dis.readBoolean ());
   }

   /**
    * Reads an 8-bit byte.
    *
    * @return The 8-bit byte read.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final byte readByte ()
      throws IOException
   {
      return (dis.readByte ());
   }

   /**
    * Reads an unsigned 8-bit byte.
    *
    * @return The 8-bit byte read.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final int readUnsignedByte ()
      throws IOException
   {
      return (dis.readUnsignedByte ());
   }

   /**
    * Reads an unsigned 8-bit byte.
    *
    * @return The 8-bit byte read.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final short readShort ()
      throws IOException
   {
      return (dis.readShort ());
   }

   /**
    * Reads an unsigned 16-bit short.
    *
    * @return The 16-bit short read.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final int readUnsignedShort ()
      throws IOException
   {                 
      return (dis.readUnsignedShort ());
   }

   /**
    * Reads a 16-bit char.
    *
    * @return The 16-bit char read. 
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final char readChar ()
      throws IOException
   {
      return (dis.readChar ());
   }

   /**
    * Reads a 32-bit integer.
    *
    * @return The 32-bit integer read.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final int readInt ()
      throws IOException
   {
      return (dis.readInt ());
   }

   /**
    * Reads a 64-bit long.
    *
    * @return The read 64-bit long.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final long readLong ()
      throws IOException
   {
      return (dis.readLong ());
   }

   /**
    * Reads a 32-bit float.
    *
    * @return The 32-bit float read.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final float readFloat ()
      throws IOException
   {
      return (dis.readFloat ());
   }

   /**
    * Reads a 64-bit double.
    *
    * @return The 64-bit double read.
    *
    * @exception EOFException If end of file is reached.
    * @exception IOException If any other I/O error has occurred.
    */
   public final double readDouble ()
      throws IOException
   {
      return (dis.readDouble ());
   }

   /**
    * Reads in a line that has been terminated by a \n, \r, 
    * \r\n or EOF.
    *
    * @return A String copy of the line.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public String readLine ()
      throws IOException
   {
      return (dis.readLine ());
   }

   /**
    * Reads a UTF format String.
    *
    * @return The UTF format String.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public String readUTF ()
      throws IOException
   {
      return (dis.readUTF ());
   }
   

   // ******************** EndianInput Interface ********************

   /**
    * Reads a 16-bit char from high-low order.
    *
    * @return The read 16-bit char.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final char readHiLoChar ()
      throws IOException
   {
      return (readChar ());
   }

   /**
    * Reads a 16-bit char from low-high order.
    *
    * @return The read 16-bit char.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final char readLoHiChar ()
      throws IOException
   {
      int ch2 = read ();
      int ch1 = read ();

      if ((ch1 | ch2) < 0)
      {
         throw (new EOFException ());
      }
      return ((char)((ch1 << 8) + (ch2 << 0)));
   }

   private char charsBuffer [];

   /**
    * Reads in a sequence of chars from high-low order that has been
    * NULL-terminated or EOF.
    *
    * @return A String copy of the sequence.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final String readHiLoChars ()
      throws IOException
   {
      char buf[] = charsBuffer;

      if (buf == null)
      {
         buf = charsBuffer = new char [256];
      }

      int room = buf.length;
      int offset = 0;
      int c;

loop: while (true)
      {
         switch (c = readShort ())
         {
            case -1:
            case 0:
            {
               break loop;
            }

            default:
            {
               if (--room < 0)
               {
                  buf = new char [offset + 256];
                  room = buf.length - offset - 1;
                  System.arraycopy (charsBuffer, 0, buf, 0, offset);
                  charsBuffer = buf;
               }
               buf [offset++] = (char) c;
               break;
            }
         }
      }
      if ((c == -1) && (offset == 0))
      {
         return (null);
      }
      return (String.copyValueOf (buf, 0, offset));

   } /* readHiLoChars () */

   /**
    * Reads in a sequence of chars from low-high order that has been
    * NULL-terminated or EOF.
    *
    * @return A String copy of the sequence.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final String readLoHiChars ()
      throws IOException
   {
      char buf[] = charsBuffer;

      if (buf == null)
      {
         buf = charsBuffer = new char [256];
      }

      int room = buf.length;
      int offset = 0;
      int c;

loop: while (true)
      {
         switch (c = readLoHiShort ())
         {
            case -1:
            case 0:
            {
               break loop;
            }

            default:
            {
               if (--room < 0)
               {
                  buf = new char [offset + 256];
                  room = buf.length - offset - 1;
                  System.arraycopy (charsBuffer, 0, buf, 0, offset);
                  charsBuffer = buf;
               }
               buf [offset++] = (char) c;
               break;
            }
         }
      }
      if ((c == -1) && (offset == 0))
      {
         return (null);
      }
      return (String.copyValueOf (buf, 0, offset));

   }

   /**
    * Reads in a sequence of chars from high-low order that has been
    * length preceeded.
    *
    * @return A String copy of the sequence.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final String readHiLoString ()
      throws IOException
   {
      int offset;
      char buffer[] = new char[readInt () / 2];

      for (offset = 0; offset < buffer.length; offset++)
      {
         buffer[offset] = (char) readShort ();
      }
      if (offset == 0)
      {
         return (null);
      }
      return (String.copyValueOf (buffer, 0, offset));
   }

   /** @internal
    * @deprecated
    */
   private static final native String getString (
         byte[] bytes,
         int position);

   /** @deprecated
    *
    * Reads in a sequence of chars from low-high order that has been
    * length preceeded and null-terminated.
    * </p>
    *
    * @return                    A String copy of the sequence.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final String readUString ()
      throws IOException
   {
      int length = readLoHiInt ();
      String string;
//      byte[] buffer = new byte[count];

      // a zero-length Unicode string is not null terminated
      //    return empty String
      if (length == 0)
      {
         return (new String ());
      }

      if (position + length > count)
      {
         throw (new EOFException ());
      }

//      System.arraycopy (this.buffer, position, buffer, 0, count);

      string = getString (buffer, position);

      position += length;

      return (string);
   }

   /**
    * Reads a 32-bit int from high-low order.
    *
    * @return The 32-bit integer read.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final int readHiLoInt ()
      throws IOException
   {
      return (readInt ());
   }

   /**
    * Reads a 32-bit int from low-high order.
    *
    * @return The 32-bit integer read.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final int readLoHiInt ()
      throws IOException
   {
      int ch4 = read ();
      int ch3 = read ();
      int ch2 = read ();
      int ch1 = read ();

      if ((ch1 | ch2 | ch3 | ch4) < 0)
      {
         throw (new EOFException ());
      }
      return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
   }

   /**
    * Reads a 16-bit short from high-low order.
    *
    * @return The 16-bit short read.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final short readHiLoShort ()
      throws IOException
   {
      return (readShort ());
   }

   /**
    * Reads a 16-bit short from low-high order.
    *
    * @return The 16-bit short read.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final short readLoHiShort ()
      throws IOException
   {
      int ch2 = read ();
      int ch1 = read ();

      if ((ch1 | ch2) < 0)
      {
         throw (new EOFException ());
      }
      return ((short)((ch1 << 8) + (ch2 << 0)));
   }

   /**
    * Reads a 64-bit long from high-low order.
    *
    * @return The 64-bit long read.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final long readHiLoLong ()
      throws IOException
   {
      return (readLong ());
   }

   /**
    * Reads a 64-bit long from low-high order.
    *
    * @return The 64-bit long read.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final long readLoHiLong ()
      throws IOException
   {
      return (((long)(readLoHiInt()) << 32) + (readLoHiInt() & 0xFFFFFFFFL));
   }

   /**
    * Aligns a value a on 32-bit boundary.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final void align32 ()
      throws IOException
   {
      position = (position + 3) & ~3;
   }

   /**
    * Unpads a 32-bit value on the specified byte boundary.
    *
    * @param n The byte boundry.
    *
    * @exception IOException If an I/O error has occurred.
    */
   public final void unpad (
         int n)
      throws IOException
   {
/*      InputStream in = this.in;
        int len = read % n;

        for (int i = 0 ; i < len ; i++) {
            in.read(0);
        }
        read += len;*/
   }

} /* EndianInputStream */


