summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Kennke <roman@kennke.org>2007-03-28 20:28:40 +0000
committerRoman Kennke <roman@kennke.org>2007-03-28 20:28:40 +0000
commit749bd3a9ca025484014973089eb681f05eeb52db (patch)
tree843c16baab635a34918bc06a7bcd49a59b0d627c
parent954b1e97f255c216a04818307679fc18dd2ceb83 (diff)
downloadclasspath-749bd3a9ca025484014973089eb681f05eeb52db.tar.gz
2007-03-28 Roman Kennke <roman@kennke.org>
* java/io/OutputStreamWriter.java, * java/io/InputStreamReader.java: Revert big stream patch due to problems.
-rw-r--r--ChangeLog6
-rw-r--r--java/io/InputStreamReader.java434
-rw-r--r--java/io/OutputStreamWriter.java347
3 files changed, 353 insertions, 434 deletions
diff --git a/ChangeLog b/ChangeLog
index 7e7bd6667..7e191a1f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2007-03-28 Roman Kennke <roman@kennke.org>
+
+ * java/io/OutputStreamWriter.java,
+ * java/io/InputStreamReader.java: Revert big stream patch due to
+ problems.
+
2007-03-28 Tom Tromey <tromey@redhat.com>
PR classpath/31303:
diff --git a/java/io/InputStreamReader.java b/java/io/InputStreamReader.java
index bc9932c75..6c5297f6b 100644
--- a/java/io/InputStreamReader.java
+++ b/java/io/InputStreamReader.java
@@ -98,11 +98,6 @@ import java.nio.charset.CodingErrorAction;
public class InputStreamReader extends Reader
{
/**
- * The default buffer size.
- */
- private final static int BUFFER_SIZE = 1024;
-
- /**
* The input stream.
*/
private InputStream in;
@@ -118,6 +113,11 @@ public class InputStreamReader extends Reader
private boolean isDone = false;
/**
+ * Need this.
+ */
+ private float maxBytesPerChar;
+
+ /**
* Buffer holding surplus loaded bytes (if any)
*/
private ByteBuffer byteBuffer;
@@ -128,22 +128,21 @@ public class InputStreamReader extends Reader
private String encoding;
/**
- * One char as array to be used in {@link #read()}.
+ * 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[] oneChar = new char[1];
+ private char savedSurrogate;
+ private boolean hasSavedSurrogate = false;
/**
- * The last char array that has been passed to read(char[],int,int). This
- * is used to cache the associated CharBuffer because read(char[],int,int)
- * is usually called with the same array repeatedly and we don't want to
- * allocate a new CharBuffer object on each call.
+ * A byte array to be reused in read(byte[], int, int).
*/
- private char[] lastArray;
+ private byte[] bytesCache;
/**
- * The cached CharBuffer associated with the above array.
+ * Locks the bytesCache above in read(byte[], int, int).
*/
- private CharBuffer lastBuffer;
+ private Object cacheLock = new Object();
/**
* This method initializes a new instance of <code>InputStreamReader</code>
@@ -155,31 +154,38 @@ public class InputStreamReader extends Reader
{
if (in == null)
throw new NullPointerException();
-
this.in = in;
-
- String encodingName = SystemProperties.getProperty("file.encoding");
- try
- {
- Charset cs = EncodingHelper.getCharset(encodingName);
- decoder = cs.newDecoder();
- // The encoding should be the old name, if such exists.
- encoding = EncodingHelper.getOldCanonical(cs.name());
- }
- catch(RuntimeException e)
- {
- // For bootstrapping problems only.
- decoder = null;
- encoding = "ISO8859_1";
- }
- catch (UnsupportedEncodingException ex)
- {
- Charset cs = EncodingHelper.getDefaultCharset();
- decoder = cs.newDecoder();
- // The encoding should be the old name, if such exists.
- encoding = EncodingHelper.getOldCanonical(cs.name());
- }
- initDecoderAndBuffer();
+ try
+ {
+ encoding = SystemProperties.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;
+ }
}
/**
@@ -197,26 +203,39 @@ public class InputStreamReader extends Reader
public InputStreamReader(InputStream in, String encoding_name)
throws UnsupportedEncodingException
{
- if (in == null || encoding_name == null)
+ if (in == null
+ || encoding_name == null)
throw new NullPointerException();
-
+
this.in = in;
-
- try
+ // Don't use NIO if avoidable
+ if(EncodingHelper.isISOLatin1(encoding_name))
{
- Charset cs = EncodingHelper.getCharset(encoding_name);
- decoder = cs.newDecoder();
- // The encoding should be the old name, if such exists.
- encoding = EncodingHelper.getOldCanonical(cs.name());
- }
- catch(RuntimeException e)
- {
- // For bootstrapping problems only.
- decoder = null;
- encoding = "ISO8859_1";
+ encoding = "ISO8859_1";
+ maxBytesPerChar = 1f;
+ decoder = null;
+ return;
}
-
- initDecoderAndBuffer();
+ 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;
+ }
}
/**
@@ -226,15 +245,22 @@ public class InputStreamReader extends Reader
*
* @since 1.4
*/
- public InputStreamReader(InputStream in, Charset charset)
- {
- if (in == null || charset == null)
+ public InputStreamReader(InputStream in, Charset charset) {
+ if (in == null)
throw new NullPointerException();
-
this.in = in;
decoder = charset.newDecoder();
+
+ try {
+ maxBytesPerChar = charset.newEncoder().maxBytesPerChar();
+ } catch(UnsupportedOperationException _){
+ maxBytesPerChar = 1f;
+ }
+
+ decoder.onMalformedInput(CodingErrorAction.REPLACE);
+ decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
+ decoder.reset();
encoding = EncodingHelper.getOldCanonical(charset.name());
- initDecoderAndBuffer();
}
/**
@@ -243,34 +269,31 @@ public class InputStreamReader extends Reader
*
* @since 1.4
*/
- public InputStreamReader(InputStream in, CharsetDecoder decoder)
- {
- if (in == null || decoder == null)
+ public InputStreamReader(InputStream in, CharsetDecoder decoder) {
+ if (in == null)
throw new NullPointerException();
-
this.in = in;
this.decoder = decoder;
- encoding = EncodingHelper.getOldCanonical(decoder.charset().name());
- initDecoderAndBuffer();
- }
-
- /**
- * Initializes the decoder and the input buffer.
- */
- private void initDecoderAndBuffer()
- {
- if (decoder != null)
- {
- decoder.onMalformedInput(CodingErrorAction.REPLACE);
- decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
- decoder.reset();
- }
- byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
- // No bytes available initially.
- byteBuffer.position(byteBuffer.limit());
+ Charset charset = decoder.charset();
+ try {
+ if (charset == null)
+ maxBytesPerChar = 1f;
+ else
+ maxBytesPerChar = charset.newEncoder().maxBytesPerChar();
+ } catch(UnsupportedOperationException _){
+ maxBytesPerChar = 1f;
+ }
+
+ decoder.onMalformedInput(CodingErrorAction.REPLACE);
+ decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
+ decoder.reset();
+ if (charset == null)
+ encoding = "US-ASCII";
+ else
+ encoding = EncodingHelper.getOldCanonical(decoder.charset().name());
}
-
+
/**
* This method closes this stream, as well as the underlying
* <code>InputStream</code>.
@@ -319,7 +342,8 @@ public class InputStreamReader extends Reader
{
if (in == null)
throw new IOException("Reader has been closed");
- return byteBuffer.hasRemaining() || in.available() != 0;
+
+ return in.available() != 0;
}
/**
@@ -341,28 +365,109 @@ public class InputStreamReader extends Reader
throw new IOException("Reader has been closed");
if (isDone)
return -1;
-
- CharBuffer outBuffer = getCharBuffer(buf, offset, length);
- int startPos = outBuffer.position();
- int remaining = outBuffer.remaining();
- int start = remaining;
- CoderResult cr = null;
- // Try to decode as long as the output buffer can hold more data.
- // Decode at least one character (block when necessary).
- boolean moreAvailable = true;
- while (remaining > 0 && moreAvailable)
+ if(decoder != null)
{
- if (byteBuffer.remaining() == 0
- || (cr != null && (cr.isUnderflow())))
+ int totalBytes = (int)((double) length * maxBytesPerChar);
+ if (byteBuffer != null)
+ totalBytes = Math.max(totalBytes, byteBuffer.remaining());
+ byte[] bytes;
+ // Fetch cached bytes array if available and big enough.
+ synchronized(cacheLock)
+ {
+ bytes = bytesCache;
+ if (bytes == null || bytes.length < totalBytes)
+ bytes = new byte[totalBytes];
+ else
+ bytesCache = null;
+ }
+
+ int remaining = 0;
+ if(byteBuffer != null)
+ {
+ remaining = byteBuffer.remaining();
+ byteBuffer.get(bytes, 0, remaining);
+ }
+ int read;
+ if(totalBytes - remaining > 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;
+
+ // Put cached bytes array back if we are finished and the cache
+ // is null or smaller than the used bytes array.
+ synchronized (cacheLock)
{
- // Block when we have not yet decoded at least one character.
- boolean block = remaining == start;
- moreAvailable = refillInputBuffer(block);
+ if (byteBuffer == null
+ && (bytesCache == null || bytesCache.length < bytes.length))
+ bytesCache = bytes;
}
- cr = decode(outBuffer);
- remaining = outBuffer.remaining();
+ return (read <= 0) ? -1 : read;
}
- return outBuffer.position() - startPos;
+ else
+ {
+ byte[] bytes;
+ // Fetch cached bytes array if available and big enough.
+ synchronized (cacheLock)
+ {
+ bytes = bytesCache;
+ if (bytes == null || length < bytes.length)
+ bytes = new byte[length];
+ else
+ bytesCache = null;
+ }
+
+ int read = in.read(bytes);
+ for(int i=0;i<read;i++)
+ buf[offset+i] = (char)(bytes[i]&0xFF);
+
+ // Put back byte array into cache if appropriate.
+ synchronized (cacheLock)
+ {
+ if (bytesCache == null || bytesCache.length < bytes.length)
+ bytesCache = bytes;
+ }
+ return read;
+ }
}
/**
@@ -378,8 +483,9 @@ public class InputStreamReader extends Reader
*/
public int read() throws IOException
{
- int count = read(oneChar, 0, 1);
- return count > 0 ? oneChar[0] : -1;
+ char[] buf = new char[1];
+ int count = read(buf, 0, 1);
+ return count > 0 ? buf[0] : -1;
}
/**
@@ -397,127 +503,7 @@ public class InputStreamReader extends Reader
{
if (in == null)
throw new IOException("Reader has been closed");
-
+
return super.skip(count);
}
-
- /**
- * Returns a CharBuffer that wraps the specified char array. This tries
- * to return a cached instance because usually the read() method is called
- * repeatedly with the same char array instance, or the no-arg read
- * method is called repeatedly which uses the oneChar field of this class
- * over and over again.
- *
- * @param buf the array to wrap
- * @param offset the offset
- * @param length the length
- *
- * @return a prepared CharBuffer to write to
- */
- private final CharBuffer getCharBuffer(char[] buf, int offset, int length)
- {
- CharBuffer outBuffer;
- if (lastArray == buf)
- {
- outBuffer = lastBuffer;
- outBuffer.position(offset);
- outBuffer.limit(offset + length);
- }
- else
- {
- lastArray = buf;
- lastBuffer = CharBuffer.wrap(buf, offset, length);
- outBuffer = lastBuffer;
- }
- return outBuffer;
- }
-
- /**
- * Refills the input buffer by reading a chunk of bytes from the underlying
- * input stream
- *
- * @param block true when this method is allowed to block when necessary,
- * false otherwise
- *
- * @return true when data has been read, false when no data has been
- * available without blocking
- *
- * @throws IOException from the underlying stream
- */
- private final boolean refillInputBuffer(boolean block)
- throws IOException
- {
- boolean refilled = false;
-
- // Refill input buffer.
- byteBuffer.compact();
- if (byteBuffer.hasArray())
- {
- byte[] buffer = byteBuffer.array();
- int offs = byteBuffer.arrayOffset();
- int pos = byteBuffer.position();
- int rem = byteBuffer.remaining();
- int avail = in.available();
- int readBytes = 0;
- int len;
- // Try to not block.
- if (block)
- len = avail != 0 ? Math.min(avail, rem) : rem;
- else
- len = Math.min(avail, rem);
- readBytes = in.read(buffer, offs + pos, len);
-
- if (readBytes > 0)
- {
- byteBuffer.position(pos + readBytes);
- byteBuffer.limit(pos + readBytes);
- refilled = true;
- }
- isDone = readBytes == -1;
- }
- else
- {
- assert false;
- // Shouldn't happen, but anyway...
- byte[] buffer = new byte[byteBuffer.limit()
- - byteBuffer.position()];
- int readBytes = in.read(buffer);
- isDone = readBytes == -1;
- byteBuffer.put(buffer);
- }
- byteBuffer.flip();
- return refilled;
- }
-
- /**
- * Decodes the current byteBuffer into the specified outBuffer. This takes
- * care of the corner case when we have no decoder (i.e. bootstrap problems)
- * and performs a primitive Latin1 decoding in this case.
- *
- * @param outBuffer the buffer to decode to
- *
- * @return the coder result
- */
- private CoderResult decode(CharBuffer outBuffer)
- {
- CoderResult cr;
- if (decoder != null)
- {
- cr = decoder.decode(byteBuffer, outBuffer, false);
- }
- else
- {
- // Perform primitive Latin1 decoding.
- while (outBuffer.hasRemaining() && byteBuffer.hasRemaining())
- {
- outBuffer.put((char) (0xff & byteBuffer.get()));
- }
- // One of the buffers must be drained.
- if (! outBuffer.hasRemaining())
- cr = CoderResult.OVERFLOW;
- else
- cr = CoderResult.UNDERFLOW;
- }
- return cr;
- }
}
diff --git a/java/io/OutputStreamWriter.java b/java/io/OutputStreamWriter.java
index 26b3c96d0..26363401f 100644
--- a/java/io/OutputStreamWriter.java
+++ b/java/io/OutputStreamWriter.java
@@ -42,10 +42,11 @@ import gnu.java.nio.charset.EncodingHelper;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
+import java.nio.charset.MalformedInputException;
/**
* This class writes characters to an output stream that is byte oriented
@@ -83,11 +84,6 @@ import java.nio.charset.CodingErrorAction;
public class OutputStreamWriter extends Writer
{
/**
- * The default buffer size.
- */
- private final static int BUFFER_SIZE = 1024;
-
- /**
* The output stream.
*/
private OutputStream out;
@@ -103,28 +99,10 @@ public class OutputStreamWriter extends Writer
private String encodingName;
/**
- * This buffer receives the encoded data and is flushed to the underlying
- * stream when it gets too full.
+ * Buffer output before character conversion as it has costly overhead.
*/
- private ByteBuffer outputBuffer;
-
- /**
- * A one-char array to be reused in read().
- */
- private char[] oneChar = new char[1];
-
- /**
- * The last char array that has been passed to write(char[],int,int). This
- * is used to cache the associated CharBuffer because write(char[],int,int)
- * is usually called with the same array repeatedly and we don't want to
- * allocate a new CharBuffer object on each call.
- */
- private Object lastArray;
-
- /**
- * The cached char buffer.
- */
- private CharBuffer lastBuffer;
+ private CharBuffer outputBuffer;
+ private final static int BUFFER_SIZE = 1024;
/**
* This method initializes a new instance of <code>OutputStreamWriter</code>
@@ -142,49 +120,60 @@ public class OutputStreamWriter extends Writer
public OutputStreamWriter (OutputStream out, String encoding_scheme)
throws UnsupportedEncodingException
{
- if (out == null || encoding_scheme == null)
- throw new NullPointerException();
-
this.out = out;
try
{
- /*
- * Workraround for encodings with a byte-order-mark.
- * We only want to write it once per stream.
- */
- try
- {
- if(encoding_scheme.equalsIgnoreCase("UnicodeBig")
- ||encoding_scheme.equalsIgnoreCase("UTF-16") ||
- encoding_scheme.equalsIgnoreCase("UTF16"))
- {
- encoding_scheme = "UTF-16BE";
- out.write((byte)0xFE);
- out.write((byte)0xFF);
- }
- else if(encoding_scheme.equalsIgnoreCase("UnicodeLittle")){
- encoding_scheme = "UTF-16LE";
- out.write((byte)0xFF);
- out.write((byte)0xFE);
- }
- }
- catch(IOException ioe)
- {
- }
-
- Charset cs = EncodingHelper.getCharset(encoding_scheme);
- encoder = cs.newEncoder();
- encodingName = EncodingHelper.getOldCanonical(cs.name());
-
+ // Don't use NIO if avoidable
+ if(EncodingHelper.isISOLatin1(encoding_scheme))
+ {
+ encodingName = "ISO8859_1";
+ encoder = null;
+ return;
+ }
+
+ /*
+ * Workraround for encodings with a byte-order-mark.
+ * We only want to write it once per stream.
+ */
+ try
+ {
+ if(encoding_scheme.equalsIgnoreCase("UnicodeBig") ||
+ encoding_scheme.equalsIgnoreCase("UTF-16") ||
+ encoding_scheme.equalsIgnoreCase("UTF16"))
+ {
+ encoding_scheme = "UTF-16BE";
+ out.write((byte)0xFE);
+ out.write((byte)0xFF);
+ }
+ else if(encoding_scheme.equalsIgnoreCase("UnicodeLittle")){
+ encoding_scheme = "UTF-16LE";
+ out.write((byte)0xFF);
+ out.write((byte)0xFE);
+ }
+ }
+ catch(IOException ioe)
+ {
+ }
+
+ outputBuffer = CharBuffer.allocate(BUFFER_SIZE);
+
+ Charset cs = EncodingHelper.getCharset(encoding_scheme);
+ if(cs == null)
+ throw new UnsupportedEncodingException("Encoding "+encoding_scheme+
+ " unknown");
+ encoder = cs.newEncoder();
+ encodingName = EncodingHelper.getOldCanonical(cs.name());
+
+ encoder.onMalformedInput(CodingErrorAction.REPLACE);
+ encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
}
catch(RuntimeException e)
{
- // Default to ISO Latin-1, will happen if this is called, for instance,
- // before the NIO provider is loadable.
- encoder = null;
- encodingName = "ISO8859_1";
+ // Default to ISO Latin-1, will happen if this is called, for instance,
+ // before the NIO provider is loadable.
+ encoder = null;
+ encodingName = "ISO8859_1";
}
- initEncoderAndBuffer();
}
/**
@@ -196,21 +185,26 @@ public class OutputStreamWriter extends Writer
public OutputStreamWriter (OutputStream out)
{
this.out = out;
+ outputBuffer = null;
try
{
- String encoding = System.getProperty("file.encoding");
- Charset cs = Charset.forName(encoding);
- encoder = cs.newEncoder();
- encodingName = EncodingHelper.getOldCanonical(cs.name());
+ String encoding = System.getProperty("file.encoding");
+ Charset cs = Charset.forName(encoding);
+ encoder = cs.newEncoder();
+ encodingName = EncodingHelper.getOldCanonical(cs.name());
}
catch(RuntimeException e)
{
- // For bootstrap problems.
- encoder = null;
- encodingName = "ISO8859_1";
+ encoder = null;
+ encodingName = "ISO8859_1";
}
- initEncoderAndBuffer();
+ if(encoder != null)
+ {
+ encoder.onMalformedInput(CodingErrorAction.REPLACE);
+ encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
+ outputBuffer = CharBuffer.allocate(BUFFER_SIZE);
+ }
}
/**
@@ -226,8 +220,10 @@ public class OutputStreamWriter extends Writer
{
this.out = out;
encoder = cs.newEncoder();
+ encoder.onMalformedInput(CodingErrorAction.REPLACE);
+ encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
+ outputBuffer = CharBuffer.allocate(BUFFER_SIZE);
encodingName = EncodingHelper.getOldCanonical(cs.name());
- initEncoderAndBuffer();
}
/**
@@ -244,25 +240,12 @@ public class OutputStreamWriter extends Writer
{
this.out = out;
encoder = enc;
+ outputBuffer = CharBuffer.allocate(BUFFER_SIZE);
Charset cs = enc.charset();
if (cs == null)
encodingName = "US-ASCII";
else
encodingName = EncodingHelper.getOldCanonical(cs.name());
- initEncoderAndBuffer();
- }
-
- /**
- * Initializes the encoder and the output buffer.
- */
- private void initEncoderAndBuffer()
- {
- if (encoder != null)
- {
- encoder.onMalformedInput(CodingErrorAction.REPLACE);
- encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
- }
- outputBuffer = ByteBuffer.allocate(BUFFER_SIZE);
}
/**
@@ -299,26 +282,18 @@ public class OutputStreamWriter extends Writer
*/
public void flush () throws IOException
{
- int len = outputBuffer.position();
- if (len > 0)
- {
- outputBuffer.flip();
- if (outputBuffer.hasArray())
- {
- byte[] bytes = outputBuffer.array();
- int p = outputBuffer.arrayOffset();
- out.write(bytes, p, len);
- }
- else
- {
- // Shouldn't happen for normal (non-direct) ByteBuffers.
- byte[] bytes = new byte[len];
- outputBuffer.get(bytes);
- out.write(bytes, 0, len);
- }
- outputBuffer.clear();
+ if(out != null){
+ if(outputBuffer != null){
+ char[] buf = new char[outputBuffer.position()];
+ if(buf.length > 0){
+ outputBuffer.flip();
+ outputBuffer.get(buf);
+ writeConvert(buf, 0, buf.length);
+ outputBuffer.clear();
+ }
+ }
+ out.flush ();
}
- out.flush ();
}
/**
@@ -339,9 +314,59 @@ public class OutputStreamWriter extends Writer
if(buf == null)
throw new IOException("Buffer is null.");
- CharBuffer charBuffer = getCharBuffer(buf, offset, count);
- encodeChars(charBuffer);
- flush();
+ if(outputBuffer != null)
+ {
+ if(count >= outputBuffer.remaining())
+ {
+ int r = outputBuffer.remaining();
+ outputBuffer.put(buf, offset, r);
+ writeConvert(outputBuffer.array(), 0, BUFFER_SIZE);
+ outputBuffer.clear();
+ offset += r;
+ count -= r;
+ // if the remaining bytes is larger than the whole buffer,
+ // just don't buffer.
+ if(count >= outputBuffer.remaining()){
+ writeConvert(buf, offset, count);
+ return;
+ }
+ }
+ outputBuffer.put(buf, offset, count);
+ } else writeConvert(buf, offset, count);
+ }
+
+ /**
+ * Converts and writes characters.
+ */
+ private void writeConvert (char[] buf, int offset, int count)
+ throws IOException
+ {
+ if(encoder == null)
+ {
+ byte[] b = new byte[count];
+ for(int i=0;i<count;i++)
+ b[i] = (byte)((buf[offset+i] <= 0xFF)?buf[offset+i]:'?');
+ out.write(b);
+ } else {
+ try {
+ ByteBuffer output = encoder.encode(CharBuffer.wrap(buf,offset,count));
+ encoder.reset();
+ if(output.hasArray())
+ out.write(output.array());
+ else
+ {
+ byte[] outbytes = new byte[output.remaining()];
+ output.get(outbytes);
+ out.write(outbytes);
+ }
+ } catch(IllegalStateException e) {
+ throw new IOException("Internal error.");
+ } catch(MalformedInputException e) {
+ throw new IOException("Invalid character sequence.");
+ } catch(CharacterCodingException e) {
+ throw new IOException("Unmappable character.");
+ }
+ }
}
/**
@@ -358,16 +383,10 @@ public class OutputStreamWriter extends Writer
*/
public void write (String str, int offset, int count) throws IOException
{
- if (out == null)
- throw new IOException("Stream is closed.");
- if (str == null)
- throw new IOException("Buffer is null.");
+ if(str == null)
+ throw new IOException("String is null.");
- // Don't call str.toCharArray() here to avoid allocation.
- // TODO: CharBuffer.wrap(String) should not allocate a char array either.
- CharBuffer charBuffer = getCharBuffer(str, offset, count);
- encodeChars(charBuffer);
- flush();
+ write(str.toCharArray(), offset, count);
}
/**
@@ -379,99 +398,7 @@ public class OutputStreamWriter extends Writer
*/
public void write (int ch) throws IOException
{
- oneChar[0] = (char) ch;
- write(oneChar, 0, 1);
- }
-
- /**
- * Encodes the specified buffer of characters. The encoded data is stored
- * in an intermediate buffer and only flushed when this buffer gets full.
- *
- * @param chars the characters to encode
- *
- * @throws IOException if something goes wrong on the underlying stream
- */
- private void encodeChars(CharBuffer chars)
- throws IOException
- {
- assert out != null;
- assert encoder != null;
- int remaining = chars.remaining();
- while (remaining > 0)
- {
- CoderResult cr = encode(chars);
- remaining = chars.remaining();
- // Flush when the output buffer has no more space or when the
- // space is not enough to hold more encoded data (that when the
- // input buffer does not change).
- if (cr.isOverflow())
- flush();
- }
- }
-
- /**
- * Encodes the specified CharBuffer into the output buffer. This takes
- * care for the seldom case when we have no decoder, i.e. bootstrapping
- * problems.
- *
- * @param chars the char buffer to encode
- */
- private CoderResult encode(CharBuffer chars)
- {
- CoderResult cr;
- if (encoder != null)
- {
- cr = encoder.encode(chars, outputBuffer, false);
- }
- else
- {
- // For bootstrapping weirdness.
- // Perform primitive Latin1 decoding.
- while (chars.hasRemaining() && outputBuffer.hasRemaining())
- {
- outputBuffer.put((byte) (chars.get()));
- }
- // One of the buffers must be drained.
- if (! outputBuffer.hasRemaining())
- cr = CoderResult.OVERFLOW;
- else
- cr = CoderResult.UNDERFLOW;
- }
- return cr;
- }
-
- /**
- * Returns a CharBuffer that wraps the specified char array. This tries
- * to return a cached instance because usually the read() method is called
- * repeatedly with the same char array instance, or the no-arg read
- * method is called repeatedly which uses the oneChar field of this class
- * over and over again.
- *
- * @param buf the array to wrap
- * @param offset the offset
- * @param length the length
- *
- * @return a prepared CharBuffer to write to
- */
- private final CharBuffer getCharBuffer(Object buf, int offset, int length)
- {
- CharBuffer outBuffer;
- if (lastArray == buf)
- {
- outBuffer = lastBuffer;
- outBuffer.position(offset);
- outBuffer.limit(offset + length);
- }
- else
- {
- lastArray = buf;
- if (buf instanceof String)
- lastBuffer = CharBuffer.wrap((String) buf, offset, length);
- else
- lastBuffer = CharBuffer.wrap((char[]) buf, offset, length);
- outBuffer = lastBuffer;
- }
- return outBuffer;
+ write(new char[]{ (char)ch }, 0, 1);
}
} // class OutputStreamWriter