--- /home/cpdev/src/classpath/java/io/InputStreamReader.java	2005-07-02 21:03:30.000000000 +0000
+++ java/io/InputStreamReader.java	2005-07-07 05:34:12.000000000 +0000
@@ -38,16 +38,7 @@
 
 package java.io;
 
-import java.nio.charset.UnsupportedCharsetException;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.IllegalCharsetNameException;
-import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.CharBuffer;
-import java.nio.ByteBuffer;
-import gnu.java.nio.charset.EncodingHelper;
+import gnu.gcj.convert.*;
 
 /**
  * This class reads characters from a byte input stream.   The characters
@@ -90,49 +81,26 @@
  * @see BufferedReader
  * @see InputStream
  *
- * @author Robert Schuster
  * @author Aaron M. Renn (arenn@urbanophile.com)
  * @author Per Bothner (bothner@cygnus.com)
  * @date April 22, 1998.  
  */
 public class InputStreamReader extends Reader
 {
-  /**
-   * The input stream.
-   */
-  private InputStream in;
-
-  /**
-   * The charset decoder.
-   */
-  private CharsetDecoder decoder;
-
-  /**
-   * End of stream reached.
-   */
-  private boolean isDone = false;
+  BufferedInputStream in;
 
-  /**
-   * Need this.
+  // Buffer of chars read from in and converted but not consumed.
+  char[] work;
+  // Next available character (in work buffer) to read.
+  int wpos;
+  // Last available character (in work buffer) to read.
+  int wcount;
+
+  /*
+   * This is the byte-character decoder class that does the reading and
+   * translation of bytes from the underlying stream.
    */
-  private float maxBytesPerChar;
-
-  /**
-   * Buffer holding surplus loaded bytes (if any)
-   */
-  private ByteBuffer byteBuffer;
-
-  /**
-   * java.io canonical name of the encoding.
-   */
-  private String encoding;
-
-  /**
-   * We might decode to a 2-char UTF-16 surrogate, which won't fit in the
-   * output buffer. In this case we need to save the surrogate char.
-   */
-  private char savedSurrogate;
-  private boolean hasSavedSurrogate = false;
+  BytesToUnicode converter;
 
   /**
    * This method initializes a new instance of <code>InputStreamReader</code>
@@ -142,40 +110,7 @@
    */
   public InputStreamReader(InputStream in)
   {
-    if (in == null)
-      throw new NullPointerException();
-    this.in = in;
-    try 
-	{ 
-	  encoding = System.getProperty("file.encoding");
-	  // Don't use NIO if avoidable
-	  if(EncodingHelper.isISOLatin1(encoding))
-	    {
-	      encoding = "ISO8859_1";
-	      maxBytesPerChar = 1f;
-	      decoder = null;
-	      return;
-	    }
-	  Charset cs = EncodingHelper.getCharset(encoding);
-	  decoder = cs.newDecoder();
-	  encoding = EncodingHelper.getOldCanonical(cs.name());
-	  try {
-	      maxBytesPerChar = cs.newEncoder().maxBytesPerChar();
-	  } catch(UnsupportedOperationException _){
-	      maxBytesPerChar = 1f;
-	  } 
-	  decoder.onMalformedInput(CodingErrorAction.REPLACE);
-	  decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
-	  decoder.reset();
-	} catch(RuntimeException e) {
-	  encoding = "ISO8859_1";
-	  maxBytesPerChar = 1f;
-	  decoder = null;
-	} catch(UnsupportedEncodingException e) {
-	  encoding = "ISO8859_1";
-	  maxBytesPerChar = 1f;
-	  decoder = null;
-	}
+    this(in, BytesToUnicode.getDefaultDecoder());
   }
 
   /**
@@ -193,77 +128,26 @@
   public InputStreamReader(InputStream in, String encoding_name)
     throws UnsupportedEncodingException
   {
-    if (in == null
-        || encoding_name == null)
-      throw new NullPointerException();
-    
-    this.in = in;
-    // Don't use NIO if avoidable
-    if(EncodingHelper.isISOLatin1(encoding_name))
-      {
-	encoding = "ISO8859_1";
-	maxBytesPerChar = 1f;
-	decoder = null;
-	return;
-      }
-    try {
-      Charset cs = EncodingHelper.getCharset(encoding_name);
-      try {
-        maxBytesPerChar = cs.newEncoder().maxBytesPerChar();
-      } catch(UnsupportedOperationException _){
-	maxBytesPerChar = 1f;
-      } 
-
-      decoder = cs.newDecoder();
-      decoder.onMalformedInput(CodingErrorAction.REPLACE);
-      decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
-      decoder.reset();
-
-      // The encoding should be the old name, if such exists.
-      encoding = EncodingHelper.getOldCanonical(cs.name());
-    } catch(RuntimeException e) {
-      encoding = "ISO8859_1";
-      maxBytesPerChar = 1f;
-      decoder = null;
-    }
+    this(in, BytesToUnicode.getDecoder(encoding_name));
   }
 
-  /**
-   * Creates an InputStreamReader that uses a decoder of the given
-   * charset to decode the bytes in the InputStream into
-   * characters.
-   */
-  public InputStreamReader(InputStream in, Charset charset) {
-    this.in = in;
-    decoder = charset.newDecoder();
-
-    decoder.onMalformedInput(CodingErrorAction.REPLACE);
-    decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
-    decoder.reset();
-    encoding = EncodingHelper.getOldCanonical(charset.name());
+  private InputStreamReader(InputStream in, BytesToUnicode decoder)
+  {
+    // FIXME: someone could pass in a BufferedInputStream whose buffer
+    // is smaller than the longest encoded character for this
+    // encoding.  We will probably go into an infinite loop in this
+    // case.  We probably ought to just have our own byte buffering
+    // here.
+    this.in = in instanceof BufferedInputStream
+              ? (BufferedInputStream) in
+              : new BufferedInputStream(in);
+    /* Don't need to call super(in) here as long as the lock gets set. */
+    this.lock = in;
+    converter = decoder;
+    converter.setInput(this.in.buf, 0, 0);
   }
 
   /**
-   * Creates an InputStreamReader that uses the given charset decoder
-   * to decode the bytes in the InputStream into characters.
-   */
-  public InputStreamReader(InputStream in, CharsetDecoder decoder) {
-    this.in = in;
-    this.decoder = decoder;
-
-    try {
-	maxBytesPerChar = decoder.charset().newEncoder().maxBytesPerChar();
-    } catch(UnsupportedOperationException _){
-	maxBytesPerChar = 1f;
-    } 
-
-    decoder.onMalformedInput(CodingErrorAction.REPLACE);
-    decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
-    decoder.reset();
-    encoding = EncodingHelper.getOldCanonical(decoder.charset().name());      
-  }
-  
-  /**
    * This method closes this stream, as well as the underlying 
    * <code>InputStream</code>.
    *
@@ -273,14 +157,11 @@
   {
     synchronized (lock)
       {
-	// Makes sure all intermediate data is released by the decoder.
-	if (decoder != null)
-	   decoder.reset();
 	if (in != null)
-	   in.close();
+	  in.close();
 	in = null;
-	isDone = true;
-	decoder = null;
+	work = null;
+	wpos = wcount = 0;
       }
   }
 
@@ -293,11 +174,11 @@
    */
   public String getEncoding()
   {
-    return in != null ? encoding : null;
+    return in != null ? converter.getName() : null;
   }
 
   /**
-   * This method checks to see if the stream is ready to be read.  It
+   * This method checks to see if the stream is read to be read.  It
    * will return <code>true</code> if is, or <code>false</code> if it is not.
    * If the stream is not ready to be read, it could (although is not required
    * to) block on the next read attempt.
@@ -309,10 +190,19 @@
    */
   public boolean ready() throws IOException
   {
-    if (in == null)
-      throw new IOException("Reader has been closed");
-    
-    return in.available() != 0;
+    synchronized (lock)
+      {
+	if (in == null)
+	  throw new IOException("Stream closed");
+
+	if (wpos < wcount)
+	  return true;
+
+	// According to the spec, an InputStreamReader is ready if its
+	// input buffer is not empty (above), or if bytes are
+	// available on the underlying byte stream.
+	return in.available () > 0;
+      }
   }
 
   /**
@@ -328,111 +218,94 @@
    *
    * @exception IOException If an error occurs
    */
-  public int read(char[] buf, int offset, int length) throws IOException
+  public int read (char[] buf, int offset, int length) throws IOException
   {
-    if (in == null)
-      throw new IOException("Reader has been closed");
-    if (isDone)
-      return -1;
-    if(decoder != null){
-	int totalBytes = (int)((double)length * maxBytesPerChar);
-	byte[] bytes = new byte[totalBytes];
-
-	int remaining = 0;
-	if(byteBuffer != null)
-	{
-	    remaining = byteBuffer.remaining();
-	    byteBuffer.get(bytes, 0, remaining);
-	}
-	int read;
-	if(totalBytes - remaining > 0)
+    synchronized (lock)
+      {
+	if (in == null)
+	  throw new IOException("Stream closed");
+
+	if (length == 0)
+	  return 0;
+
+	int wavail = wcount - wpos;
+	if (wavail <= 0)
 	  {
-	    read = in.read(bytes, remaining, totalBytes - remaining);
-	    if(read == -1){
-	      read = remaining;
-	      isDone = true;
-	    } else
-	      read += remaining;
-	  } else 
-            read = remaining;
-	byteBuffer = ByteBuffer.wrap(bytes, 0, read);	
-	CharBuffer cb = CharBuffer.wrap(buf, offset, length);
-	int startPos = cb.position();
-
- 	if(hasSavedSurrogate){
- 	    hasSavedSurrogate = false;
- 	    cb.put(savedSurrogate);
-	    read++;
- 	}
-
-	CoderResult cr = decoder.decode(byteBuffer, cb, isDone);
-	decoder.reset();
-	// 1 char remains which is the first half of a surrogate pair.
-	if(cr.isOverflow() && cb.hasRemaining()){
-	    CharBuffer overflowbuf = CharBuffer.allocate(2);
-	    cr = decoder.decode(byteBuffer, overflowbuf, isDone);
-	    overflowbuf.flip();
-	    if(overflowbuf.hasRemaining())
-	    {
-	      cb.put(overflowbuf.get());
-	      savedSurrogate = overflowbuf.get();
-	      hasSavedSurrogate = true;	    
-	      isDone = false;
-	    }
-	}
-
-	if(byteBuffer.hasRemaining()) {
-	    byteBuffer.compact();
-	    byteBuffer.flip();	  
-	    isDone = false;
-	} else
-	    byteBuffer = null;
-
-	read = cb.position() - startPos;
-	return (read <= 0) ? -1 : read;
-    } else {
-	byte[] bytes = new byte[length];
-	int read = in.read(bytes);
-	for(int i=0;i<read;i++)
-          buf[offset+i] = (char)(bytes[i]&0xFF);
-	return read;
-    }
+	    // Nothing waiting, so refill their buffer.
+	    return refill(buf, offset, length);
+	  }
+
+	if (length > wavail)
+	  length = wavail;
+	System.arraycopy(work, wpos, buf, offset, length);
+	wpos += length;
+	return length;
+      }
   }
 
   /**
-   * Reads an char from the input stream and returns it
-   * as an int in the range of 0-65535.  This method also will return -1 if
-   * the end of the stream has been reached.
-   * <p>
-   * This method will block until the char can be read.
+   * This method reads a single character of data from the stream.
    *
-   * @return The char read or -1 if end of stream
+   * @return The char read, as an int, or -1 if end of stream.
    *
    * @exception IOException If an error occurs
    */
   public int read() throws IOException
   {
-    char[] buf = new char[1];
-    int count = read(buf, 0, 1);
-    return count > 0 ? buf[0] : -1;
+    synchronized (lock)
+      {
+	if (in == null)
+	  throw new IOException("Stream closed");
+
+	int wavail = wcount - wpos;
+	if (wavail <= 0)
+	  {
+	    // Nothing waiting, so refill our internal buffer.
+	    wpos = wcount = 0;
+	    if (work == null)
+	       work = new char[100];
+	    int count = refill(work, 0, work.length);
+	    if (count == -1)
+	      return -1;
+	    wcount += count;
+	  }
+
+	return work[wpos++];
+      }
   }
 
-  /**
-   * Skips the specified number of chars in the stream.  It
-   * returns the actual number of chars skipped, which may be less than the
-   * requested amount.
-   *
-   * @param count The requested number of chars to skip
-   *
-   * @return The actual number of chars skipped.
-   *
-   * @exception IOException If an error occurs
-   */
-   public long skip(long count) throws IOException
-   {
-     if (in == null)
-       throw new IOException("Reader has been closed");
-     
-     return super.skip(count);
-   }
+  // Read more bytes and convert them into the specified buffer.
+  // Returns the number of converted characters or -1 on EOF.
+  private int refill(char[] buf, int offset, int length) throws IOException
+  {
+    for (;;)
+      {
+	// We have knowledge of the internals of BufferedInputStream
+	// here.  Eww.
+	// BufferedInputStream.refill() can only be called when
+	// `pos>=count'.
+	boolean r = in.pos < in.count || in.refill ();
+	if (! r)
+	  return -1;
+	converter.setInput(in.buf, in.pos, in.count);
+	int count = converter.read(buf, offset, length);
+
+	// We might have bytes but not have made any progress.  In
+	// this case we try to refill.  If refilling fails, we assume
+	// we have a malformed character at the end of the stream.
+	if (count == 0 && converter.inpos == in.pos)
+	  {
+	    in.mark(in.count);
+	    if (! in.refill ())
+	      throw new CharConversionException ();
+	    in.reset();
+	  }
+	else
+	  {
+	    in.skip(converter.inpos - in.pos);
+	    if (count > 0)
+	      return count;
+	  }
+      }
+  }
 }
