src/java.base/share/classes/java/lang/StringCoding.java

Print this page

        

@@ -126,13 +126,12 @@
         private final boolean isTrusted;
 
         private StringDecoder(Charset cs, String rcn) {
             this.requestedCharsetName = rcn;
             this.cs = cs;
-            this.cd = cs.newDecoder()
-                .onMalformedInput(CodingErrorAction.REPLACE)
-                .onUnmappableCharacter(CodingErrorAction.REPLACE);
+            this.cd = cs.newDecoder();
+            setupDecoder(cd);
             this.isTrusted = (cs.getClass().getClassLoader0() == null);
         }
 
         String charsetName() {
             if (cs instanceof HistoricallyNamedCharset)

@@ -153,31 +152,41 @@
                 int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
                 return safeTrim(ca, clen, cs, isTrusted);
             } else {
                 cd.reset();
                 ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
-                CharBuffer cb = CharBuffer.wrap(ca);
-                try {
-                    CoderResult cr = cd.decode(bb, cb, true);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                    cr = cd.flush(cb);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                } catch (CharacterCodingException x) {
-                    // Substitution is always enabled,
-                    // so this shouldn't happen
-                    throw new Error(x);
+                return performDecode(cs, isTrusted, ca, cd, bb);
                 }
-                return safeTrim(ca, cb.position(), cs, isTrusted);
+        }
+
+        char[] decode(ByteBuffer bb) {
+            int len = bb.remaining();
+            int en = scale(len, cd.maxCharsPerByte());
+            char[] ca = new char[en];
+            if (len == 0)
+                return ca;
+
+            if (!isTrusted) {
+                // copy the bytebuffer
+                ByteBuffer originalByteBuffer = bb;
+                bb = ByteBuffer.allocate(len)
+                               .put(originalByteBuffer);
+                bb.position(0);
+            }
+
+            cd.reset();
+
+            return performDecode(cs, isTrusted, ca, cd, bb);
             }
         }
+
+    static char[] decode(String charsetName, byte[] ba, int off, int len) throws UnsupportedEncodingException {
+        StringDecoder sd = getStringDecoder(charsetName);
+        return sd.decode(ba, off, len);
     }
 
-    static char[] decode(String charsetName, byte[] ba, int off, int len)
-        throws UnsupportedEncodingException
-    {
+    private static StringDecoder getStringDecoder(String charsetName) throws UnsupportedEncodingException {
         StringDecoder sd = deref(decoder);
         String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
         if ((sd == null) || !(csn.equals(sd.requestedCharsetName())
                               || csn.equals(sd.charsetName()))) {
             sd = null;

@@ -188,11 +197,11 @@
             } catch (IllegalCharsetNameException x) {}
             if (sd == null)
                 throw new UnsupportedEncodingException(csn);
             set(decoder, sd);
         }
-        return sd.decode(ba, off, len);
+        return sd;
     }
 
     static char[] decode(Charset cs, byte[] ba, int off, int len) {
         // (1)We never cache the "external" cs, the only benefit of creating
         // an additional StringDe/Encoder object to wrap it is to share the

@@ -213,25 +222,72 @@
         CharsetDecoder cd = cs.newDecoder();
         int en = scale(len, cd.maxCharsPerByte());
         char[] ca = new char[en];
         if (len == 0)
             return ca;
-        boolean isTrusted = false;
-        if (System.getSecurityManager() != null) {
-            if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
+        
+        boolean isTrusted = isTrusted(cs);
+        if (!isTrusted) {
                 ba =  Arrays.copyOfRange(ba, off, off + len);
                 off = 0;
             }
-        }
-        cd.onMalformedInput(CodingErrorAction.REPLACE)
-          .onUnmappableCharacter(CodingErrorAction.REPLACE)
-          .reset();
+
+        setupDecoder(cd);
+
         if (cd instanceof ArrayDecoder) {
             int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
             return safeTrim(ca, clen, cs, isTrusted);
         } else {
             ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
+            return performDecode(cs, isTrusted, ca, cd, bb);
+        }
+    }
+
+    static char[] decode(Charset cs, ByteBuffer bb) {
+        // See comment at top of decode(Charset,byte[],int,int)
+        CharsetDecoder cd = cs.newDecoder();
+        int len = bb.remaining();
+        int en = scale(len, cd.maxCharsPerByte());
+        char[] ca = new char[en];
+        if (len == 0)
+            return ca;
+
+        boolean isTrusted = isTrusted(cs);
+        if (!isTrusted) {
+            // copy the bytebuffer
+            ByteBuffer originalByteBuffer = bb;
+            bb = ByteBuffer.allocate(len)
+                           .put(originalByteBuffer);
+            bb.position(0);
+        }
+
+        setupDecoder(cd);
+
+        return performDecode(cs, isTrusted, ca, cd, bb);
+    }
+
+    static char[] decode(String charsetName, ByteBuffer bb) throws UnsupportedEncodingException {
+        StringDecoder sd = getStringDecoder(charsetName);
+        return sd.decode(bb);
+    }
+
+    private static boolean isTrusted(Charset cs) {
+        return System.getSecurityManager() != null
+            // null classloader means the class is on the boot classpath
+            && cs.getClass().getClassLoader0() == null;
+    }
+
+    private static void setupDecoder(CharsetDecoder cd) {
+        cd.onMalformedInput(CodingErrorAction.REPLACE)
+          .onUnmappableCharacter(CodingErrorAction.REPLACE)
+          .reset();
+    }
+
+    static char[] performDecode(
+        Charset cs, boolean isTrusted, char[] ca, 
+        CharsetDecoder cd, ByteBuffer bb) {
+
             CharBuffer cb = CharBuffer.wrap(ca);
             try {
                 CoderResult cr = cd.decode(bb, cb, true);
                 if (!cr.isUnderflow())
                     cr.throwException();

@@ -243,11 +299,10 @@
                 // so this shouldn't happen
                 throw new Error(x);
             }
             return safeTrim(ca, cb.position(), cs, isTrusted);
         }
-    }
 
     static char[] decode(byte[] ba, int off, int len) {
         String csn = Charset.defaultCharset().name();
         try {
             // use charset name decode() variant which provides caching.

@@ -304,31 +359,23 @@
                 int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);
                 return safeTrim(ba, blen, cs, isTrusted);
             } else {
                 ce.reset();
                 ByteBuffer bb = ByteBuffer.wrap(ba);
-                CharBuffer cb = CharBuffer.wrap(ca, off, len);
-                try {
-                    CoderResult cr = ce.encode(cb, bb, true);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                    cr = ce.flush(bb);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                } catch (CharacterCodingException x) {
-                    // Substitution is always enabled,
-                    // so this shouldn't happen
-                    throw new Error(x);
-                }
+                StringCoding.encode(ca, off, len, bb, ce);
                 return safeTrim(ba, bb.position(), cs, isTrusted);
             }
         }
     }
 
     static byte[] encode(String charsetName, char[] ca, int off, int len)
-        throws UnsupportedEncodingException
-    {
+        throws UnsupportedEncodingException {
+        StringEncoder se = getStringEncoder(charsetName);
+        return se.encode(ca, off, len);
+    }
+
+    private static StringEncoder getStringEncoder(String charsetName) throws UnsupportedEncodingException {
         StringEncoder se = deref(encoder);
         String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
         if ((se == null) || !(csn.equals(se.requestedCharsetName())
                               || csn.equals(se.charsetName()))) {
             se = null;

@@ -339,47 +386,83 @@
             } catch (IllegalCharsetNameException x) {}
             if (se == null)
                 throw new UnsupportedEncodingException (csn);
             set(encoder, se);
         }
-        return se.encode(ca, off, len);
+        se.ce.reset();
+        return se;
     }
 
     static byte[] encode(Charset cs, char[] ca, int off, int len) {
         CharsetEncoder ce = cs.newEncoder();
         int en = scale(len, ce.maxBytesPerChar());
         byte[] ba = new byte[en];
         if (len == 0)
             return ba;
-        boolean isTrusted = false;
-        if (System.getSecurityManager() != null) {
-            if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
+        boolean isTrusted = isTrusted(cs);
+        if (!isTrusted) {
                 ca =  Arrays.copyOfRange(ca, off, off + len);
                 off = 0;
             }
-        }
-        ce.onMalformedInput(CodingErrorAction.REPLACE)
-          .onUnmappableCharacter(CodingErrorAction.REPLACE)
-          .reset();
+        setupEncoder(ce);
         if (ce instanceof ArrayEncoder) {
             int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);
             return safeTrim(ba, blen, cs, isTrusted);
         } else {
             ByteBuffer bb = ByteBuffer.wrap(ba);
+            encode(ca, off, len, bb, ce);
+            return safeTrim(ba, bb.position(), cs, isTrusted);
+        }
+    }
+
+
+    static int encode(Charset cs, char[] ca, int off, int len, byte[] destBuffer, int destOffset) {
+        ByteBuffer bb = ByteBuffer.wrap(destBuffer, destOffset, destBuffer.length - destOffset);
+        return encode(cs, ca, off, len, bb);
+    }
+
+    static int encode(Charset cs, char[] ca, int off, int len, ByteBuffer destBuffer) {
+        CharsetEncoder ce = cs.newEncoder();
+        return encodeBuffer(ca, off, len, destBuffer, ce);
+    }
+
+    static int encode(String charsetName, char[] ca, int off, int len, byte[] destBuffer, int destOffset) throws UnsupportedEncodingException {
+        ByteBuffer bb = ByteBuffer.wrap(destBuffer, destOffset, destBuffer.length - destOffset);
+        return encode(charsetName, ca, off, len, bb);
+    }
+
+    static int encode(String charsetName, char[] ca, int off, int len, ByteBuffer destBuffer) throws UnsupportedEncodingException {
+        StringEncoder se = getStringEncoder(charsetName);
+        return encodeBuffer(ca, off, len, destBuffer, se.ce);
+    }
+
+    // encode(charsetName, value, 0, value.length, destBuffer, destOffset);
+
+    private static int encodeBuffer(char[] ca, int off, int len, ByteBuffer destBuffer, CharsetEncoder ce) {
+        int originalPosition = destBuffer.position();
+        encode(ca, off, len, destBuffer, ce);
+        return destBuffer.position() - originalPosition;
+    }
+
+    private static void encode(char[] ca, int off, int len, ByteBuffer destBuffer, CharsetEncoder ce) {
             CharBuffer cb = CharBuffer.wrap(ca, off, len);
             try {
-                CoderResult cr = ce.encode(cb, bb, true);
+            CoderResult cr = ce.encode(cb, destBuffer, true);
                 if (!cr.isUnderflow())
                     cr.throwException();
-                cr = ce.flush(bb);
+            cr = ce.flush(destBuffer);
                 if (!cr.isUnderflow())
                     cr.throwException();
             } catch (CharacterCodingException x) {
                 throw new Error(x);
             }
-            return safeTrim(ba, bb.position(), cs, isTrusted);
         }
+
+    private static void setupEncoder(CharsetEncoder encoder) {
+        encoder.onMalformedInput(CodingErrorAction.REPLACE)
+               .onUnmappableCharacter(CodingErrorAction.REPLACE)
+               .reset();
     }
 
     static byte[] encode(char[] ca, int off, int len) {
         String csn = Charset.defaultCharset().name();
         try {