1 /*
   2  * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang;
  27 
  28 import java.io.UnsupportedEncodingException;
  29 import java.lang.ref.SoftReference;
  30 import java.nio.ByteBuffer;
  31 import java.nio.CharBuffer;
  32 import java.nio.charset.Charset;
  33 import java.nio.charset.CharsetDecoder;
  34 import java.nio.charset.CharsetEncoder;
  35 import java.nio.charset.CharacterCodingException;
  36 import java.nio.charset.CoderResult;
  37 import java.nio.charset.CodingErrorAction;
  38 import java.nio.charset.IllegalCharsetNameException;
  39 import java.nio.charset.UnsupportedCharsetException;
  40 import java.util.Arrays;
  41 import jdk.internal.HotSpotIntrinsicCandidate;
  42 import sun.nio.cs.HistoricallyNamedCharset;
  43 import sun.nio.cs.ArrayDecoder;
  44 import sun.nio.cs.ArrayEncoder;
  45 
  46 import static java.lang.String.LATIN1;
  47 import static java.lang.String.UTF16;
  48 import static java.lang.String.COMPACT_STRINGS;
  49 
  50 /**
  51  * Utility class for string encoding and decoding.
  52  */
  53 
  54 class StringCoding {
  55 
  56     private StringCoding() { }
  57 
  58     /** The cached coders for each thread */
  59     private static final ThreadLocal<SoftReference<StringDecoder>> decoder =
  60         new ThreadLocal<>();
  61     private static final ThreadLocal<SoftReference<StringEncoder>> encoder =
  62         new ThreadLocal<>();
  63 
  64     private static final Charset ISO_8859_1 = Charset.forName("iso-8859-1");
  65     private static final Charset US_ASCII = Charset.forName("us-ascii");
  66     private static final Charset UTF_8 = Charset.forName("utf-8");
  67 
  68     private static boolean warnUnsupportedCharset = true;
  69 
  70     private static <T> T deref(ThreadLocal<SoftReference<T>> tl) {
  71         SoftReference<T> sr = tl.get();
  72         if (sr == null)
  73             return null;
  74         return sr.get();
  75     }
  76 
  77     private static <T> void set(ThreadLocal<SoftReference<T>> tl, T ob) {
  78         tl.set(new SoftReference<>(ob));
  79     }
  80 
  81     // Trim the given byte array to the given length
  82     //
  83     private static byte[] safeTrim(byte[] ba, int len, boolean isTrusted) {
  84         if (len == ba.length && (isTrusted || System.getSecurityManager() == null))
  85             return ba;
  86         else
  87             return Arrays.copyOf(ba, len);
  88     }
  89 
  90     private static int scale(int len, float expansionFactor) {
  91         // We need to perform double, not float, arithmetic; otherwise
  92         // we lose low order bits when len is larger than 2**24.
  93         return (int)(len * (double)expansionFactor);
  94     }
  95 
  96     private static Charset lookupCharset(String csn) {
  97         if (Charset.isSupported(csn)) {
  98             try {
  99                 return Charset.forName(csn);
 100             } catch (UnsupportedCharsetException x) {
 101                 throw new Error(x);
 102             }
 103         }
 104         return null;
 105     }
 106 
 107     private static void warnUnsupportedCharset(String csn) {
 108         if (warnUnsupportedCharset) {
 109             // Use err(String) rather than the Logging API or System.err
 110             // since this method may be called during VM initialization
 111             // before either is available.
 112             err("WARNING: Default charset " + csn +
 113                 " not supported, using ISO-8859-1 instead\n");
 114             warnUnsupportedCharset = false;
 115         }
 116     }
 117 
 118     static class Result {
 119         byte[] value;
 120         byte coder;
 121 
 122         Result with() {
 123             coder = COMPACT_STRINGS ? LATIN1 : UTF16;
 124             value = new byte[0];
 125             return this;
 126         }
 127 
 128         Result with(char[] val, int off, int len) {
 129             if (String.COMPACT_STRINGS) {
 130                 byte[] bs = StringUTF16.compress(val, off, len);
 131                 if (bs != null) {
 132                     value = bs;
 133                     coder = LATIN1;
 134                     return this;
 135                 }
 136             }
 137             coder = UTF16;
 138             value = StringUTF16.toBytes(val, off, len);
 139             return this;
 140         }
 141 
 142         Result with(byte[] val, byte coder) {
 143             this.coder = coder;
 144             value = val;
 145             return this;
 146         }
 147     }
 148 
 149     @HotSpotIntrinsicCandidate
 150     public static boolean hasNegatives(byte[] ba, int off, int len) {
 151         for (int i = off; i < off + len; i++) {
 152             if (ba[i] < 0) {
 153                 return true;
 154             }
 155         }
 156         return false;
 157     }
 158 
 159     // -- Decoding --
 160     static class StringDecoder {
 161         private final String requestedCharsetName;
 162         private final Charset cs;
 163         private final boolean isASCIICompatible;
 164         private final CharsetDecoder cd;
 165         protected final Result result;
 166 
 167         StringDecoder(Charset cs, String rcn) {
 168             this.requestedCharsetName = rcn;
 169             this.cs = cs;
 170             this.cd = cs.newDecoder()
 171                 .onMalformedInput(CodingErrorAction.REPLACE)
 172                 .onUnmappableCharacter(CodingErrorAction.REPLACE);
 173             this.result = new Result();
 174             this.isASCIICompatible = (cd instanceof ArrayDecoder) &&
 175                     ((ArrayDecoder)cd).isASCIICompatible();
 176         }
 177 
 178         String charsetName() {
 179             if (cs instanceof HistoricallyNamedCharset)
 180                 return ((HistoricallyNamedCharset)cs).historicalName();
 181             return cs.name();
 182         }
 183 
 184         final String requestedCharsetName() {
 185             return requestedCharsetName;
 186         }
 187 
 188         Result decode(byte[] ba, int off, int len) {
 189             if (len == 0) {
 190                 return result.with();
 191             }
 192             // fastpath for ascii compatible
 193             if (isASCIICompatible && !hasNegatives(ba, off, len)) {
 194                 if (COMPACT_STRINGS) {
 195                     return result.with(Arrays.copyOfRange(ba, off, off + len),
 196                                       LATIN1);
 197                 } else {
 198                     return result.with(StringLatin1.inflate(ba, off, len), UTF16);
 199                 }
 200             }
 201             int en = scale(len, cd.maxCharsPerByte());
 202             char[] ca = new char[en];
 203             if (cd instanceof ArrayDecoder) {
 204                 int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
 205                 return result.with(ca, 0, clen);
 206             }
 207             cd.reset();
 208             ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
 209             CharBuffer cb = CharBuffer.wrap(ca);
 210             try {
 211                 CoderResult cr = cd.decode(bb, cb, true);
 212                 if (!cr.isUnderflow())
 213                     cr.throwException();
 214                 cr = cd.flush(cb);
 215                 if (!cr.isUnderflow())
 216                     cr.throwException();
 217             } catch (CharacterCodingException x) {
 218                 // Substitution is always enabled,
 219                 // so this shouldn't happen
 220                 throw new Error(x);
 221             }
 222             return result.with(ca, 0, cb.position());
 223         }
 224     }
 225 
 226     private static class StringDecoder8859_1 extends StringDecoder {
 227         StringDecoder8859_1(Charset cs, String rcn) {
 228             super(cs, rcn);
 229         }
 230         Result decode(byte[] ba, int off, int len) {
 231             if (COMPACT_STRINGS) {
 232                 return result.with(Arrays.copyOfRange(ba, off, off + len), LATIN1);
 233             } else {
 234                 return result.with(StringLatin1.inflate(ba, off, len), UTF16);
 235             }
 236         }
 237     }
 238 
 239     static Result decode(String charsetName, byte[] ba, int off, int len)
 240         throws UnsupportedEncodingException
 241     {
 242         StringDecoder sd = deref(decoder);
 243         String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
 244         if ((sd == null) || !(csn.equals(sd.requestedCharsetName())
 245                               || csn.equals(sd.charsetName()))) {
 246             sd = null;
 247             try {
 248                 Charset cs = lookupCharset(csn);
 249                 if (cs != null) {
 250                     if (cs == UTF_8) {
 251                         sd = new StringDecoderUTF8(cs, csn);
 252                     } else if (cs == ISO_8859_1) {
 253                         sd = new StringDecoder8859_1(cs, csn);
 254                     } else {
 255                         sd = new StringDecoder(cs, csn);
 256                     }
 257                 }
 258             } catch (IllegalCharsetNameException x) {}
 259             if (sd == null)
 260                 throw new UnsupportedEncodingException(csn);
 261             set(decoder, sd);
 262         }
 263         return sd.decode(ba, off, len);
 264     }
 265 
 266     static Result decode(Charset cs, byte[] ba, int off, int len) {
 267         // (1)We never cache the "external" cs, the only benefit of creating
 268         // an additional StringDe/Encoder object to wrap it is to share the
 269         // de/encode() method. These SD/E objects are short-lived, the young-gen
 270         // gc should be able to take care of them well. But the best approach
 271         // is still not to generate them if not really necessary.
 272         // (2)The defensive copy of the input byte/char[] has a big performance
 273         // impact, as well as the outgoing result byte/char[]. Need to do the
 274         // optimization check of (sm==null && classLoader0==null) for both.
 275         // (3)getClass().getClassLoader0() is expensive
 276         // (4)There might be a timing gap in isTrusted setting. getClassLoader0()
 277         // is only checked (and then isTrusted gets set) when (SM==null). It is
 278         // possible that the SM==null for now but then SM is NOT null later
 279         // when safeTrim() is invoked...the "safe" way to do is to redundant
 280         // check (... && (isTrusted || SM == null || getClassLoader0())) in trim
 281         // but it then can be argued that the SM is null when the operation
 282         // is started...
 283         if (cs == UTF_8) {
 284             return StringDecoderUTF8.decode(ba, off, len, new Result());
 285         }
 286         CharsetDecoder cd = cs.newDecoder();
 287         // ascii fastpath
 288         if (cs == ISO_8859_1 || ((cd instanceof ArrayDecoder) &&
 289                                  ((ArrayDecoder)cd).isASCIICompatible() &&
 290                                  !hasNegatives(ba, off, len))) {
 291              if (COMPACT_STRINGS) {
 292                  return new Result().with(Arrays.copyOfRange(ba, off, off + len),
 293                                           LATIN1);
 294              } else {
 295                  return new Result().with(StringLatin1.inflate(ba, off, len), UTF16);
 296              }
 297         }
 298         int en = scale(len, cd.maxCharsPerByte());
 299         if (len == 0) {
 300             return new Result().with();
 301         }
 302         if (System.getSecurityManager() != null &&
 303             cs.getClass().getClassLoader0() != null) {
 304             ba =  Arrays.copyOfRange(ba, off, off + len);
 305             off = 0;
 306         }
 307         cd.onMalformedInput(CodingErrorAction.REPLACE)
 308           .onUnmappableCharacter(CodingErrorAction.REPLACE)
 309           .reset();
 310 
 311         char[] ca = new char[en];
 312         if (cd instanceof ArrayDecoder) {
 313             int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
 314             return new Result().with(ca, 0, clen);
 315         }
 316         ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
 317         CharBuffer cb = CharBuffer.wrap(ca);
 318         try {
 319             CoderResult cr = cd.decode(bb, cb, true);
 320             if (!cr.isUnderflow())
 321                 cr.throwException();
 322             cr = cd.flush(cb);
 323             if (!cr.isUnderflow())
 324                 cr.throwException();
 325         } catch (CharacterCodingException x) {
 326             // Substitution is always enabled,
 327             // so this shouldn't happen
 328             throw new Error(x);
 329         }
 330         return new Result().with(ca, 0, cb.position());
 331     }
 332 
 333     static Result decode(byte[] ba, int off, int len) {
 334         String csn = Charset.defaultCharset().name();
 335         try {
 336             // use charset name decode() variant which provides caching.
 337             return decode(csn, ba, off, len);
 338         } catch (UnsupportedEncodingException x) {
 339             warnUnsupportedCharset(csn);
 340         }
 341         try {
 342             return decode("ISO-8859-1", ba, off, len);
 343         } catch (UnsupportedEncodingException x) {
 344             // If this code is hit during VM initialization, err(String) is
 345             // the only way we will be able to get any kind of error message.
 346             err("ISO-8859-1 charset not available: " + x.toString() + "\n");
 347             // If we can not find ISO-8859-1 (a required encoding) then things
 348             // are seriously wrong with the installation.
 349             System.exit(1);
 350             return null;
 351         }
 352     }
 353 
 354     // -- Encoding --
 355     private static class StringEncoder {
 356         private Charset cs;
 357         private CharsetEncoder ce;
 358         private final boolean isASCIICompatible;
 359         private final String requestedCharsetName;
 360         private final boolean isTrusted;
 361 
 362         private StringEncoder(Charset cs, String rcn) {
 363             this.requestedCharsetName = rcn;
 364             this.cs = cs;
 365             this.ce = cs.newEncoder()
 366                 .onMalformedInput(CodingErrorAction.REPLACE)
 367                 .onUnmappableCharacter(CodingErrorAction.REPLACE);
 368             this.isTrusted = (cs.getClass().getClassLoader0() == null);
 369             this.isASCIICompatible = (ce instanceof ArrayEncoder) &&
 370                     ((ArrayEncoder)ce).isASCIICompatible();
 371         }
 372 
 373         String charsetName() {
 374             if (cs instanceof HistoricallyNamedCharset)
 375                 return ((HistoricallyNamedCharset)cs).historicalName();
 376             return cs.name();
 377         }
 378 
 379         final String requestedCharsetName() {
 380             return requestedCharsetName;
 381         }
 382 
 383         byte[] encode(byte coder, byte[] val) {
 384             // fastpath for ascii compatible
 385             if (coder == LATIN1 && isASCIICompatible &&
 386                 !hasNegatives(val, 0, val.length)) {
 387                 return Arrays.copyOf(val, val.length);
 388             }
 389             int len = val.length >> coder;  // assume LATIN1=0/UTF16=1;
 390             int en = scale(len, ce.maxBytesPerChar());
 391             byte[] ba = new byte[en];
 392             if (len == 0) {
 393                 return ba;
 394             }
 395             if (ce instanceof ArrayEncoder) {
 396                 if (!isTrusted) {
 397                     val = Arrays.copyOf(val, val.length);
 398                 }
 399                 int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba)
 400                                               : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba);
 401                 if (blen != -1) {
 402                     return safeTrim(ba, blen, isTrusted);
 403                 }
 404             }
 405             char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val)
 406                                            : StringUTF16.toChars(val);
 407             ce.reset();
 408             ByteBuffer bb = ByteBuffer.wrap(ba);
 409             CharBuffer cb = CharBuffer.wrap(ca, 0, len);
 410             try {
 411                 CoderResult cr = ce.encode(cb, bb, true);
 412                 if (!cr.isUnderflow())
 413                     cr.throwException();
 414                 cr = ce.flush(bb);
 415                 if (!cr.isUnderflow())
 416                     cr.throwException();
 417             } catch (CharacterCodingException x) {
 418                 // Substitution is always enabled,
 419                 // so this shouldn't happen
 420                 throw new Error(x);
 421             }
 422             return safeTrim(ba, bb.position(), isTrusted);
 423         }
 424     }
 425 
 426     @HotSpotIntrinsicCandidate
 427     private static int implEncodeISOArray(byte[] sa, int sp,
 428                                           byte[] da, int dp, int len) {
 429         int i = 0;
 430         for (; i < len; i++) {
 431             char c = StringUTF16.Trusted.getChar(sa, sp++);
 432             if (c > '\u00FF')
 433                 break;
 434             da[dp++] = (byte)c;
 435         }
 436         return i;
 437     }
 438 
 439     static byte[] encode8859_1(byte coder, byte[] val) {
 440         if (coder == LATIN1) {
 441             return Arrays.copyOf(val, val.length);
 442         }
 443         int len = val.length >> 1;
 444         byte[] dst = new byte[len];
 445         int dp = 0;
 446         int sp = 0;
 447         int sl = len;
 448         while (sp < sl) {
 449             int ret = implEncodeISOArray(val, sp, dst, dp, len);
 450             sp = sp + ret;
 451             dp = dp + ret;
 452             if (ret != len) {
 453                 char c = StringUTF16.Trusted.getChar(val, sp++);
 454                 if (Character.isHighSurrogate(c) && sp < sl &&
 455                     Character.isLowSurrogate(StringUTF16.Trusted.getChar(val, sp))) {
 456                     sp++;
 457                 }
 458                 dst[dp++] = '?';
 459                 len = sl - sp;
 460             }
 461         }
 462         if (dp == dst.length) {
 463             return dst;
 464         }
 465         return Arrays.copyOf(dst, dp);
 466     }
 467 
 468     static byte[] encodeASCII(byte coder, byte[] val) {
 469         if (coder == LATIN1) {
 470             byte[] dst = new byte[val.length];
 471             for (int i = 0; i < val.length; i++) {
 472                 if (val[i] < 0) {
 473                     dst[i] = '?';
 474                 } else {
 475                     dst[i] = val[i];
 476                 }
 477             }
 478             return dst;
 479         }
 480         int len = val.length >> 1;
 481         byte[] dst = new byte[len];
 482         int dp = 0;
 483         for (int i = 0; i < len; i++) {
 484             char c = StringUTF16.Trusted.getChar(val, i);
 485             if (c < 0x80) {
 486                 dst[dp++] = (byte)c;
 487                 continue;
 488             }
 489             if (Character.isHighSurrogate(c) && i + 1 < len &&
 490                 Character.isLowSurrogate(StringUTF16.Trusted.getChar(val, i + 1))) {
 491                 i++;
 492             }
 493             dst[dp++] = '?';
 494         }
 495         if (len == dp) {
 496             return dst;
 497         }
 498         return Arrays.copyOf(dst, dp);
 499     }
 500 
 501    static byte[] encodeUTF8(byte coder, byte[] val) {
 502         int dp = 0;
 503         byte[] dst;
 504         if (coder == LATIN1) {
 505             dst = new byte[val.length << 1];
 506             for (int sp = 0; sp < val.length; sp++) {
 507                 byte c = val[sp];
 508                 if (c < 0) {
 509                     dst[dp++] = (byte)(0xc0 | ((c & 0xff) >> 6));
 510                     dst[dp++] = (byte)(0x80 | (c & 0x3f));
 511                 } else {
 512                     dst[dp++] = c;
 513                 }
 514             }
 515         } else {
 516             int sp = 0;
 517             int sl = val.length >> 1;
 518             dst = new byte[sl * 3];
 519             char c;
 520             while (sp < sl && (c = StringUTF16.Trusted.getChar(val, sp)) < '\u0080') {
 521                 // ascii fast loop;
 522                 dst[dp++] = (byte)c;
 523                 sp++;
 524             }
 525             while (sp < sl) {
 526                 c = StringUTF16.Trusted.getChar(val, sp++);
 527                 if (c < 0x80) {
 528                     dst[dp++] = (byte)c;
 529                 } else if (c < 0x800) {
 530                     dst[dp++] = (byte)(0xc0 | (c >> 6));
 531                     dst[dp++] = (byte)(0x80 | (c & 0x3f));
 532                 } else if (Character.isSurrogate(c)) {
 533                     int uc = -1;
 534                     char c2;
 535                     if (Character.isHighSurrogate(c) && sp < sl &&
 536                         Character.isLowSurrogate(c2 = StringUTF16.Trusted.getChar(val, sp))) {
 537                         uc = Character.toCodePoint(c, c2);
 538                     }
 539                     if (uc < 0) {
 540                         dst[dp++] = '?';
 541                     } else {
 542                         dst[dp++] = (byte)(0xf0 | ((uc >> 18)));
 543                         dst[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f));
 544                         dst[dp++] = (byte)(0x80 | ((uc >>  6) & 0x3f));
 545                         dst[dp++] = (byte)(0x80 | (uc & 0x3f));
 546                         sp++;  // 2 chars
 547                     }
 548                 } else {
 549                     // 3 bytes, 16 bits
 550                     dst[dp++] = (byte)(0xe0 | ((c >> 12)));
 551                     dst[dp++] = (byte)(0x80 | ((c >>  6) & 0x3f));
 552                     dst[dp++] = (byte)(0x80 | (c & 0x3f));
 553                 }
 554             }
 555         }
 556         if (dp == dst.length) {
 557             return dst;
 558         }
 559         return Arrays.copyOf(dst, dp);
 560     }
 561 
 562     static byte[] encode(String charsetName, byte coder, byte[] val)
 563         throws UnsupportedEncodingException
 564     {
 565         StringEncoder se = deref(encoder);
 566         String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
 567         if ((se == null) || !(csn.equals(se.requestedCharsetName())
 568                               || csn.equals(se.charsetName()))) {
 569             se = null;
 570             try {
 571                 Charset cs = lookupCharset(csn);
 572                 if (cs != null) {
 573                     if (cs == UTF_8) {
 574                         return encodeUTF8(coder, val);
 575                     } else if (cs == ISO_8859_1) {
 576                         return encode8859_1(coder, val);
 577                     } else if (cs == US_ASCII) {
 578                         return encodeASCII(coder, val);
 579                     }
 580                     se = new StringEncoder(cs, csn);
 581                 }
 582             } catch (IllegalCharsetNameException x) {}
 583             if (se == null) {
 584                 throw new UnsupportedEncodingException (csn);
 585             }
 586             set(encoder, se);
 587         }
 588         return se.encode(coder, val);
 589     }
 590 
 591     static byte[] encode(Charset cs, byte coder, byte[] val) {
 592         if (cs == UTF_8) {
 593             return encodeUTF8(coder, val);
 594         } else if (cs == ISO_8859_1) {
 595             return encode8859_1(coder, val);
 596         } else if (cs == US_ASCII) {
 597             return encodeASCII(coder, val);
 598         }
 599         CharsetEncoder ce = cs.newEncoder();
 600         // fastpath for ascii compatible
 601         if (coder == LATIN1 && (((ce instanceof ArrayEncoder) &&
 602                                  ((ArrayEncoder)ce).isASCIICompatible() &&
 603                                  !hasNegatives(val, 0, val.length)))) {
 604             return Arrays.copyOf(val, val.length);
 605         }
 606         int len = val.length >> coder;  // assume LATIN1=0/UTF16=1;
 607         int en = scale(len, ce.maxBytesPerChar());
 608         byte[] ba = new byte[en];
 609         if (len == 0) {
 610             return ba;
 611         }
 612         boolean isTrusted = System.getSecurityManager() == null ||
 613                             cs.getClass().getClassLoader0() == null;
 614         ce.onMalformedInput(CodingErrorAction.REPLACE)
 615           .onUnmappableCharacter(CodingErrorAction.REPLACE)
 616           .reset();
 617         if (ce instanceof ArrayEncoder) {
 618             if (!isTrusted) {
 619                 val = Arrays.copyOf(val, val.length);
 620             }
 621             int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba)
 622                                           : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba);
 623             if (blen != -1) {
 624                 return safeTrim(ba, blen, isTrusted);
 625             }
 626         }
 627         char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val)
 628                                        : StringUTF16.toChars(val);
 629         ByteBuffer bb = ByteBuffer.wrap(ba);
 630         CharBuffer cb = CharBuffer.wrap(ca, 0, len);
 631         try {
 632             CoderResult cr = ce.encode(cb, bb, true);
 633             if (!cr.isUnderflow())
 634                 cr.throwException();
 635             cr = ce.flush(bb);
 636             if (!cr.isUnderflow())
 637                 cr.throwException();
 638         } catch (CharacterCodingException x) {
 639             throw new Error(x);
 640         }
 641         return safeTrim(ba, bb.position(), isTrusted);
 642     }
 643 
 644     static byte[] encode(byte coder, byte[] val) {
 645         String csn = Charset.defaultCharset().name();
 646         try {
 647             // use charset name encode() variant which provides caching.
 648             return encode(csn, coder, val);
 649         } catch (UnsupportedEncodingException x) {
 650             warnUnsupportedCharset(csn);
 651         }
 652         try {
 653             return encode("ISO-8859-1", coder, val);
 654         } catch (UnsupportedEncodingException x) {
 655             // If this code is hit during VM initialization, err(String) is
 656             // the only way we will be able to get any kind of error message.
 657             err("ISO-8859-1 charset not available: " + x.toString() + "\n");
 658             // If we can not find ISO-8859-1 (a required encoding) then things
 659             // are seriously wrong with the installation.
 660             System.exit(1);
 661             return null;
 662         }
 663     }
 664 
 665     /**
 666      *  Print a message directly to stderr, bypassing all character conversion
 667      *  methods.
 668      *  @param msg  message to print
 669      */
 670     private static native void err(String msg);
 671 }