OSDN Git Service

AI 148692: Merge change #148679 into Cupcake, since we probably want to
authorDan Bornstein <nobody@android.com>
Mon, 11 May 2009 20:39:31 +0000 (13:39 -0700)
committerThe Android Open Source Project <initial-contribution@android.com>
Mon, 11 May 2009 20:39:31 +0000 (13:39 -0700)
  take it for rel if there's an opportunity. Original
  description:
  Fix InputStreamReader to not drop input on buffer boundaries.
  This fix takes the salient code from Harmony tip-of-tree,
  though it's not a wholesale replacement of the file. In
  particular, I left in the old encoding name handling as well
  as one remaining "FIXME" that I don't think would have been
  addressed.
  BUG=1836908

Automated import of CL 148692

libcore/luni/src/main/java/java/io/InputStreamReader.java
libcore/luni/src/test/java/tests/api/java/io/InputStreamReaderTest.java

index fc684a3..0f74b14 100644 (file)
@@ -31,6 +31,10 @@ import java.util.HashMap;
 import org.apache.harmony.luni.util.Msg;
 import org.apache.harmony.luni.util.PriviAction;
 
+// BEGIN android-note
+// Later changes from Harmony have been integrated into this version.
+// END android-note
+
 /**
  * A class for turning a byte stream into a character stream. Data read from the
  * source input stream is converted into characters by either a default or a
@@ -73,6 +77,7 @@ public class InputStreamReader extends Reader {
         decoder = Charset.forName(encoding).newDecoder().onMalformedInput(
                 CodingErrorAction.REPLACE).onUnmappableCharacter(
                 CodingErrorAction.REPLACE);
+        bytes.limit(0);
     }
 
     /**
@@ -103,8 +108,10 @@ public class InputStreamReader extends Reader {
                     CodingErrorAction.REPLACE).onUnmappableCharacter(
                     CodingErrorAction.REPLACE);
         } catch (IllegalArgumentException e) {
-            throw new UnsupportedEncodingException();
+            throw (UnsupportedEncodingException)
+                    new UnsupportedEncodingException().initCause(e);
         }
+        bytes.limit(0);
     }
 
     /**
@@ -122,6 +129,7 @@ public class InputStreamReader extends Reader {
         dec.averageCharsPerByte();
         this.in = in;
         decoder = dec;
+        bytes.limit(0);
     }
 
     /**
@@ -140,6 +148,7 @@ public class InputStreamReader extends Reader {
         decoder = charset.newDecoder().onMalformedInput(
                 CodingErrorAction.REPLACE).onUnmappableCharacter(
                 CodingErrorAction.REPLACE);
+        bytes.limit(0);
     }
 
     /**
@@ -408,67 +417,47 @@ public class InputStreamReader extends Reader {
             if (length == 0) {
                 return 0;
             }
-            
-            // allocate enough space for bytes if the default length is
-            // inadequate
-            int availableLen = in.available();     
-            if (Math.min(availableLen, length) > bytes.capacity()) {
-                bytes = ByteBuffer.allocate(availableLen);
-            }
-            
+
             CharBuffer out = CharBuffer.wrap(buf, offset, length);
             CoderResult result = CoderResult.UNDERFLOW;
-            byte[] a = bytes.array();
-            boolean has_been_read = false;
 
-            if (!bytes.hasRemaining() || bytes.limit() == bytes.capacity()) {
-                // Nothing is available in the buffer...
-                if (!bytes.hasRemaining()) {
-                    bytes.clear();
-                }
-                int readed = in.read(a, bytes.arrayOffset(), bytes.remaining());
-                if (readed == -1) {
-                    endOfInput = true;
-                    return -1;
-                }
-                bytes.limit(readed);
-                has_been_read = true;
-            }
+            // bytes.remaining() indicates number of bytes in buffer
+            // when 1-st time entered, it'll be equal to zero
+            boolean needInput = !bytes.hasRemaining();
 
             while (out.hasRemaining()) {
-                if (bytes.hasRemaining()) {
-                    result = decoder.decode(bytes, out, false);
-                    if (!bytes.hasRemaining() && endOfInput) {
-                        decoder.decode(bytes, out, true);
-                        decoder.flush(out);
-                        decoder.reset();
+                // fill the buffer if needed
+                if (needInput) {
+                    if ((in.available() == 0) && (out.position() > offset)) {
+                        // we could return the result without blocking read
                         break;
                     }
-                    if (!out.hasRemaining()
-                            || bytes.position() == bytes.limit()) {
-                        bytes.compact();
-                    }
-                }
-                if (in.available() > 0
-                        && (!has_been_read && out.hasRemaining())
-                        || out.position() == 0) {
-                    bytes.compact();
-                    int to_read = bytes.remaining();
-                    int off = bytes.arrayOffset() + bytes.position();
 
-                    to_read = in.read(a, off, to_read);
-                    if (to_read == -1) {
-                        if (bytes.hasRemaining()) {
-                            bytes.flip();
-                        }
+                    int to_read = bytes.capacity() - bytes.limit();
+                    int off = bytes.arrayOffset() + bytes.limit();
+                    int was_red = in.read(bytes.array(), off, to_read);
+
+                    if (was_red == -1) {
                         endOfInput = true;
                         break;
+                    } else if (was_red == 0) {
+                        break;
                     }
-                    has_been_read = true;
-                    if (to_read > 0) {
-                        bytes.limit(bytes.position() + to_read);
+                    bytes.limit(bytes.limit() + was_red);
+                    needInput = false;
+                }
+
+                // decode bytes
+                result = decoder.decode(bytes, out, false);
+
+                if (result.isUnderflow()) {
+                    // compact the buffer if no space left
+                    if (bytes.limit() == bytes.capacity()) {
+                        bytes.compact();
+                        bytes.limit(bytes.position());
                         bytes.position(0);
                     }
+                    needInput = true;
                 } else {
                     break;
                 }
@@ -479,7 +468,7 @@ public class InputStreamReader extends Reader {
                 // FIXME: should flush at first, but seems ICU has a bug that it
                 // will throw IAE if some malform/unmappable bytes found during
                 // decoding
-                // result = decoder.flush(chars);
+                // result = decoder.flush(out);
                 decoder.reset();
             }
             if (result.isMalformed()) {
@@ -487,9 +476,6 @@ public class InputStreamReader extends Reader {
             } else if (result.isUnmappable()) {
                 throw new UnmappableCharacterException(result.length());
             }
-            if (result == CoderResult.OVERFLOW && bytes.position() != 0) {
-                bytes.flip();
-            }
 
             return out.position() - offset == 0 ? -1 : out.position() - offset;
         }
@@ -525,7 +511,7 @@ public class InputStreamReader extends Reader {
                 throw new IOException(Msg.getString("K0070")); //$NON-NLS-1$
             }
             try {
-                return bytes.limit() != bytes.capacity() || in.available() > 0;
+                return bytes.hasRemaining() || in.available() > 0;
             } catch (IOException e) {
                 return false;
             }
index e3d10a4..bb3f8f5 100644 (file)
@@ -694,5 +694,61 @@ public class InputStreamReaderTest extends TestCase {
         }
     }
     
+    /**
+     * Test for regression of a bug that dropped characters when
+     * multibyte encodings spanned buffer boundaries.
+     */
+    @TestTargetNew(
+        level = TestLevel.PARTIAL_COMPLETE,
+        notes = "",
+        method = "read",
+        args = {}
+    )      
+    public void test_readWhenCharacterSpansBuffer() {
+        final byte[] suffix = {
+            (byte) 0x93, (byte) 0xa1, (byte) 0x8c, (byte) 0xb4,
+            (byte) 0x97, (byte) 0x43, (byte) 0x88, (byte) 0xea,
+            (byte) 0x98, (byte) 0x59
+        };
+        final char[] decodedSuffix = {
+            (char) 0x85e4, (char) 0x539f, (char) 0x4f51, (char) 0x4e00,
+            (char) 0x90ce
+        };
+        final int prefixLength = 8189;
     
+        byte[] bytes = new byte[prefixLength + 10];
+        Arrays.fill(bytes, (byte) ' ');
+        System.arraycopy(suffix, 0, bytes, prefixLength, suffix.length);
+        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+
+        try {
+            InputStreamReader isr = new InputStreamReader(is, "SHIFT_JIS");
+            char[] chars = new char[8192];
+            int at = 0;
+
+            outer:
+            for (;;) {
+                int amt = isr.read(chars);
+                if (amt <= 0) break;
+                for (int i = 0; i < amt; i++) {
+                    char c = chars[i];
+                    if (at < prefixLength) {
+                        if (c != ' ') {
+                            fail("Found bad prefix character " +
+                                    (int) c + " at " + at);
+                        }
+                    } else {
+                        char decoded = decodedSuffix[at - prefixLength];
+                        if (c != decoded) {
+                            fail("Found mismatched character " +
+                                    (int) c + " at " + at);
+                        }
+                    }
+                    at++;
+                }
+            }
+        } catch (IOException ex) {
+            throw new RuntimeException("unexpected exception", ex);
+        }
+    }    
 }