# HG changeset patch # User sherman # Date 1423510676 28800 # Node ID 4996a75e8bfb9f3c614a6abb73180c8f3b671442 # Parent ccf9d86e52ec44c13dca0960fe697d7fd0985056 8030179: java/nio/Buffer/Chars.java, testcases seems all pass but jtreg/testng failed with java.lang.AssertionError Summary: fix the surrogate corner case in SingleByte charset encoder Reviewed-by: psandoz, alanb diff -r ccf9d86e52ec -r 4996a75e8bfb jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java Fri Feb 06 15:42:07 2015 -0800 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java Mon Feb 09 11:37:56 2015 -0800 @@ -160,22 +160,18 @@ byte[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); + int len = Math.min(dl - dp, sl - sp); - CoderResult cr = CoderResult.UNDERFLOW; - if ((dl - dp) < (sl - sp)) { - sl = sp + (dl - dp); - cr = CoderResult.OVERFLOW; - } - - while (sp < sl) { + while (len-- > 0) { char c = sa[sp]; int b = encode(c); if (b == UNMAPPABLE_ENCODING) { if (Character.isSurrogate(c)) { if (sgp == null) sgp = new Surrogate.Parser(); - if (sgp.parse(c, sa, sp, sl) < 0) + if (sgp.parse(c, sa, sp, sl) < 0) { return withResult(sgp.error(), src, sp, dst, dp); + } return withResult(sgp.unmappableResult(), src, sp, dst, dp); } return withResult(CoderResult.unmappableForLength(1), @@ -184,7 +180,8 @@ da[dp++] = (byte)b; sp++; } - return withResult(cr, src, sp, dst, dp); + return withResult(sp < sl ? CoderResult.OVERFLOW : CoderResult.UNDERFLOW, + src, sp, dst, dp); } private CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) { diff -r ccf9d86e52ec -r 4996a75e8bfb jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java --- a/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java Fri Feb 06 15:42:07 2015 -0800 +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java Mon Feb 09 11:37:56 2015 -0800 @@ -243,8 +243,11 @@ if (cr.isUnderflow()) { if (lcb.hasRemaining()) { leftoverChar = lcb.get(); - if (cb != null && cb.hasRemaining()) - flushLeftoverChar(cb, endOfInput); + if (cb != null && cb.hasRemaining()) { + lcb.clear(); + lcb.put(leftoverChar).put(cb.get()).flip(); + continue; + } return; } break; @@ -265,24 +268,24 @@ CharBuffer cb = CharBuffer.wrap(cbuf, off, len); if (haveLeftoverChar) - flushLeftoverChar(cb, false); + flushLeftoverChar(cb, false); while (cb.hasRemaining()) { - CoderResult cr = encoder.encode(cb, bb, false); - if (cr.isUnderflow()) { - assert (cb.remaining() <= 1) : cb.remaining(); - if (cb.remaining() == 1) { - haveLeftoverChar = true; - leftoverChar = cb.get(); + CoderResult cr = encoder.encode(cb, bb, false); + if (cr.isUnderflow()) { + assert (cb.remaining() <= 1) : cb.remaining(); + if (cb.remaining() == 1) { + haveLeftoverChar = true; + leftoverChar = cb.get(); + } + break; } - break; - } - if (cr.isOverflow()) { - assert bb.position() > 0; - writeBytes(); - continue; - } - cr.throwException(); + if (cr.isOverflow()) { + assert bb.position() > 0; + writeBytes(); + continue; + } + cr.throwException(); } } diff -r ccf9d86e52ec -r 4996a75e8bfb jdk/test/sun/nio/cs/StreamEncoderOut.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/nio/cs/StreamEncoderOut.java Mon Feb 09 11:37:56 2015 -0800 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + @bug 8030179 + @summary test if the charset encoder deails with surrogate correctly + * @run testng/othervm -esa StreamEncoderOut + */ +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.joining; + +@Test +public class StreamEncoderOut { + + enum Input { + HIGH("\ud834"), + LOW("\udd1e"), + HIGH_LOW("\ud834\udd1e"); + + final String value; + + Input(String value) { + this.value = value; + } + + @Override + public String toString() { + return name() + " : \'" + value + "\""; + } + } + + @DataProvider(name = "CharsetAndString") + // [Charset, Input] + public static Object[][] makeStreamTestData() { + // Cross product of supported charsets and inputs + return Charset.availableCharsets().values().stream(). + filter(Charset::canEncode). + flatMap(cs -> Stream.of(Input.values()).map(i -> new Object[]{cs, i})). + toArray(Object[][]::new); + } + + private static String generate(String s, int n) { + return Stream.generate(() -> s).limit(n).collect(joining()); + } + + static final OutputStream DEV_NULL = new OutputStream() { + @Override + public void write(byte b[], int off, int len) throws IOException {} + + @Override + public void write(int b) throws IOException {} + }; + + @Test(dataProvider = "CharsetAndString") + public void test(Charset cs, Input input) throws IOException { + OutputStreamWriter w = new OutputStreamWriter(DEV_NULL, cs); + String t = generate(input.value, 8193); + for (int i = 0; i < 10; i++) { + w.append(t); + } + } +}