/* Copyright (C) 1998, 1999  Free Software Foundation

   This file is part of libgcj.

This software is copyrighted work licensed under the terms of the
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
details.  */
 
package java.io;

/**
 * @author Warren Levy <warrenl@cygnus.com>
 * @date October 8, 1998.
 */
/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
 * "The Java Language Specification", ISBN 0-201-63451-1
 * plus online API docs for JDK 1.2 beta from http://www.javasoft.com.
 * Status:  Believed complete and correct.
 */
 
public class BufferedInputStream extends FilterInputStream
{
  /* Internal buffer array for data. */
  protected byte[] buf;

  /* Index one greater than the last valid byte in the buffer. */
  protected int count = 0;

  /* The current position in the buffer. */
  protected int pos = 0;

  /* The value of pos the last time mark() was called. */
  protected int markpos = -1;

  /* The maximum read-ahead allowed before calls to reset() fail. */
  protected int marklimit = 0;

  public BufferedInputStream(InputStream in)
  {
    this(in, 2048);
  }

  public BufferedInputStream(InputStream in, int size)
  {
    super(in);
    if (size <= 0)
      throw new IllegalArgumentException();
    buf = new byte[size];
  }

  public synchronized int available() throws IOException
  {
    return count - pos + super.available();
  }

  public void close() throws IOException
  {
    // Free up the array memory.
    buf = null;
    super.close();
  }

  public synchronized void mark(int readlimit)
  {
    marklimit = readlimit;
    markpos = pos;
  }

  public boolean markSupported()
  {
    return true;
  }

  public synchronized int read() throws IOException
  {
    if (pos >= count && !refill())
      return -1;	// EOF

    if (markpos >= 0 && pos - markpos > marklimit)
      markpos = -1;

    return ((int) buf[pos++]) & 0xFF;
  }

  public synchronized int read(byte[] b, int off, int len) throws IOException
  {
    if (off < 0 || len < 0 || off + len > b.length)
      throw new ArrayIndexOutOfBoundsException();

    if (pos >= count && !refill())
      return -1;		// No bytes were read before EOF.

    int remain = Math.min(count - pos, len);
    System.arraycopy(buf, pos, b, off, remain);
    pos += remain;

    if (markpos >= 0 && pos - markpos > marklimit)
      markpos = -1;

    return remain;
  }

  public synchronized void reset() throws IOException
  {
    if (markpos < 0)
      throw new IOException();

    pos = markpos;
  }

  public synchronized long skip(long n) throws IOException
  {
    final long origN = n;

    while (n > 0L)
      {
	if (pos >= count && !refill())
	  if (n < origN)
	    break;
	  else
	    return -1;	// No bytes were read before EOF.

	int numread = (int) Math.min((long) (count - pos), n);
	pos += numread;
	n -= numread;

        if (markpos >= 0 && pos - markpos > marklimit)
          markpos = -1;
      }

    return origN - n;
  }

  private boolean refill() throws IOException
  {
    if (markpos < 0)
      count = pos = 0;
    else if (markpos > 0)
      {
        // Shift the marked bytes (if any) to the beginning of the array
	// but don't grow it.  This saves space in case a reset is done
	// before we reach the max capacity of this array.
        System.arraycopy(buf, markpos, buf, 0, count - markpos);
	count -= markpos;
	pos -= markpos;
	markpos = 0;
      }
    else if (marklimit >= buf.length)	// BTW, markpos == 0
      {
	// Need to grow the buffer now to have room for marklimit bytes.
	// Note that the new buffer is one greater than marklimit.
	// This is so that there will be one byte past marklimit to be read
	// before having to call refill again, thus allowing marklimit to be
	// invalidated.  That way refill doesn't have to check marklimit.
	byte[] newbuf = new byte[marklimit + 1];
	System.arraycopy(buf, 0, newbuf, 0, count);
	buf = newbuf;
      }

    int numread = super.read(buf, count, buf.length - count);

    if (numread < 0)	// EOF
      return false;

    count += numread;
    return true;
  }
}
