src/java.base/share/classes/java/util/zip/ZipCoder.java
changeset 48262 daf3b49f4839
parent 47987 85ea7e83af30
child 49442 d6d1c06becda
--- a/src/java.base/share/classes/java/util/zip/ZipCoder.java	Wed Dec 13 15:32:36 2017 +0000
+++ b/src/java.base/share/classes/java/util/zip/ZipCoder.java	Wed Dec 13 07:51:57 2017 -0800
@@ -28,72 +28,60 @@
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
 import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
+import java.nio.charset.CharacterCodingException;
 import java.nio.charset.CodingErrorAction;
-import java.util.Arrays;
-import sun.nio.cs.ArrayDecoder;
-import sun.nio.cs.ArrayEncoder;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 /**
  * Utility class for zipfile name and comment decoding and encoding
  */
 
-final class ZipCoder {
+class ZipCoder {
+
+    private static final jdk.internal.misc.JavaLangAccess JLA =
+        jdk.internal.misc.SharedSecrets.getJavaLangAccess();
+
+    static final class UTF8 extends ZipCoder {
+
+        UTF8(Charset utf8) {
+            super(utf8);
+        }
 
-    private static boolean isASCII(byte[] ba, int off, int len) {
-        for (int i = off; i < off + len; i++) {
-            if (ba[i] < 0)
-                return false;
+        @Override
+        boolean isUTF8() {
+            return true;
         }
-        return true;
+
+        @Override
+        String toString(byte[] ba, int off, int length) {
+            return JLA.newStringUTF8NoRepl(ba, off, length);
+        }
+
+        @Override
+        byte[] getBytes(String s) {
+            return JLA.getBytesUTF8NoRepl(s);
+        }
     }
 
-    private static boolean hasReplaceChar(byte[] ba) {
-        for (int i = 0; i < ba.length; i++) {
-            if (ba[i] == (byte)'?')
-                return true;
-        }
-        return false;
+    // UTF_8.ArrayEn/Decoder is stateless, so make it singleton.
+    private static ZipCoder utf8 = new UTF8(UTF_8);
+
+    public static ZipCoder get(Charset charset) {
+        if (charset == UTF_8)
+            return utf8;
+        return new ZipCoder(charset);
     }
 
     String toString(byte[] ba, int off, int length) {
-
-        // fastpath for UTF-8 cs and ascii only name, leverage the
-        // compact string impl to avoid the unnecessary char[] copy/
-        // paste. A temporary workaround before we have better approach,
-        // such as a String constructor that throws exception for
-        // malformed and/or unmappable characters, instead of silently
-        // replacing with repl char
-        if (isUTF8 && isASCII(ba, off, length)) {
-            return new String(ba, off, length, cs);
-        }
+        try {
+              return decoder().decode(ByteBuffer.wrap(ba, off, length)).toString();
 
-        CharsetDecoder cd = decoder().reset();
-        int len = (int)(length * cd.maxCharsPerByte());
-        char[] ca = new char[len];
-        if (len == 0)
-            return new String(ca);
-        // UTF-8 only for now. Other ArrayDeocder only handles
-        // CodingErrorAction.REPLACE mode. ZipCoder uses
-        // REPORT mode.
-        if (isUTF8 && cd instanceof ArrayDecoder) {
-            int clen = ((ArrayDecoder)cd).decode(ba, off, length, ca);
-            if (clen == -1)    // malformed
-                throw new IllegalArgumentException("MALFORMED");
-            return new String(ca, 0, clen);
+        } catch (CharacterCodingException x) {
+            throw new IllegalArgumentException(x);
         }
-        ByteBuffer bb = ByteBuffer.wrap(ba, off, length);
-        CharBuffer cb = CharBuffer.wrap(ca);
-        CoderResult cr = cd.decode(bb, cb, true);
-        if (!cr.isUnderflow())
-            throw new IllegalArgumentException(cr.toString());
-        cr = cd.flush(cb);
-        if (!cr.isUnderflow())
-            throw new IllegalArgumentException(cr.toString());
-        return new String(ca, 0, cb.position());
     }
 
     String toString(byte[] ba, int length) {
@@ -105,84 +93,47 @@
     }
 
     byte[] getBytes(String s) {
-        if (isUTF8) {
-            // fastpath for UTF8. should only occur when the string
-            // has malformed surrogates. A postscan should still be
-            // faster and use less memory.
-            byte[] ba = s.getBytes(cs);
-            if (!hasReplaceChar(ba)) {
-                return ba;
+        try {
+            ByteBuffer bb = encoder().encode(CharBuffer.wrap(s));
+            int pos = bb.position();
+            int limit = bb.limit();
+            if (bb.hasArray() && pos == 0 && limit == bb.capacity()) {
+                return bb.array();
             }
+            byte[] bytes = new byte[bb.limit() - bb.position()];
+            bb.get(bytes);
+            return bytes;
+        } catch (CharacterCodingException x) {
+            throw new IllegalArgumentException(x);
         }
-        CharsetEncoder ce = encoder().reset();
-        char[] ca = s.toCharArray();
-        int len = (int)(ca.length * ce.maxBytesPerChar());
-        byte[] ba = new byte[len];
-        if (len == 0)
-            return ba;
-        // UTF-8 only for now. Other ArrayDeocder only handles
-        // CodingErrorAction.REPLACE mode.
-        if (isUTF8 && ce instanceof ArrayEncoder) {
-            int blen = ((ArrayEncoder)ce).encode(ca, 0, ca.length, ba);
-            if (blen == -1)    // malformed
-                throw new IllegalArgumentException("MALFORMED");
-            return Arrays.copyOf(ba, blen);
-        }
-        ByteBuffer bb = ByteBuffer.wrap(ba);
-        CharBuffer cb = CharBuffer.wrap(ca);
-        CoderResult cr = ce.encode(cb, bb, true);
-        if (!cr.isUnderflow())
-            throw new IllegalArgumentException(cr.toString());
-        cr = ce.flush(bb);
-        if (!cr.isUnderflow())
-            throw new IllegalArgumentException(cr.toString());
-        if (bb.position() == ba.length)  // defensive copy?
-            return ba;
-        else
-            return Arrays.copyOf(ba, bb.position());
     }
 
     // assume invoked only if "this" is not utf8
     byte[] getBytesUTF8(String s) {
-        if (isUTF8)
-            return getBytes(s);
-        if (utf8 == null)
-            utf8 = new ZipCoder(StandardCharsets.UTF_8);
         return utf8.getBytes(s);
     }
 
     String toStringUTF8(byte[] ba, int len) {
-        return toStringUTF8(ba, 0, len);
+        return utf8.toString(ba, 0, len);
     }
 
     String toStringUTF8(byte[] ba, int off, int len) {
-        if (isUTF8)
-            return toString(ba, off, len);
-        if (utf8 == null)
-            utf8 = new ZipCoder(StandardCharsets.UTF_8);
         return utf8.toString(ba, off, len);
     }
 
     boolean isUTF8() {
-        return isUTF8;
+        return false;
     }
 
     private Charset cs;
     private CharsetDecoder dec;
     private CharsetEncoder enc;
-    private boolean isUTF8;
-    private ZipCoder utf8;
 
     private ZipCoder(Charset cs) {
         this.cs = cs;
-        this.isUTF8 = cs.name().equals(StandardCharsets.UTF_8.name());
     }
 
-    static ZipCoder get(Charset charset) {
-        return new ZipCoder(charset);
-    }
-
-    private CharsetDecoder decoder() {
+    protected CharsetDecoder decoder() {
         if (dec == null) {
             dec = cs.newDecoder()
               .onMalformedInput(CodingErrorAction.REPORT)
@@ -191,7 +142,7 @@
         return dec;
     }
 
-    private CharsetEncoder encoder() {
+    protected CharsetEncoder encoder() {
         if (enc == null) {
             enc = cs.newEncoder()
               .onMalformedInput(CodingErrorAction.REPORT)