1 /*
   2  * Copyright (c) 2015, 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.util.Arrays;
  29 import java.util.Locale;
  30 import java.util.Spliterator;
  31 import java.util.function.IntConsumer;
  32 import jdk.internal.HotSpotIntrinsicCandidate;
  33 
  34 import static java.lang.String.UTF16;
  35 import static java.lang.String.LATIN1;
  36 import static java.lang.String.checkIndex;
  37 import static java.lang.String.checkOffset;
  38 import static java.lang.String.checkBoundsOffCount;
  39 
  40 final class StringUTF16 {
  41 
  42     public static byte[] newBytesFor(int len) {
  43         if (len < 0) {
  44             throw new NegativeArraySizeException();
  45         }
  46         if (len > MAX_LENGTH) {
  47             throw new OutOfMemoryError("UTF16 String size is " + len +
  48                                        ", should be less than " + MAX_LENGTH);
  49         }
  50         return new byte[len << 1];
  51     }
  52 
  53     @HotSpotIntrinsicCandidate
  54     public static void putChar(byte[] val, int index, int c) {
  55         index <<= 1;
  56         val[index++] = (byte)(c >> HI_BYTE_SHIFT);
  57         val[index]   = (byte)(c >> LO_BYTE_SHIFT);
  58     }
  59 
  60     @HotSpotIntrinsicCandidate
  61     public static char getChar(byte[] val, int index) {
  62         index <<= 1;
  63         return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
  64                       ((val[index]   & 0xff) << LO_BYTE_SHIFT));
  65     }
  66 
  67     public static char charAt(byte[] value, int index) {
  68         if (index < 0 || index >= value.length >> 1) {
  69             throw new StringIndexOutOfBoundsException(index);
  70         }
  71         return getChar(value, index);
  72     }
  73 
  74     public static int length(byte[] value) {
  75         return value.length >> 1;
  76     }
  77 
  78     public static int codePointAt(byte[] value, int index, int end) {
  79         char c1 = getChar(value, index);
  80         if (Character.isHighSurrogate(c1) && ++index < end) {
  81             char c2 = getChar(value, index);
  82             if (Character.isLowSurrogate(c2)) {
  83                return Character.toCodePoint(c1, c2);
  84             }
  85         }
  86         return c1;
  87     }
  88 
  89     public static int codePointBefore(byte[] value, int index) {
  90         char c2 = getChar(value, --index);
  91         if (Character.isLowSurrogate(c2) && index > 0) {
  92             char c1 = getChar(value, --index);
  93             if (Character.isHighSurrogate(c1)) {
  94                return Character.toCodePoint(c1, c2);
  95             }
  96         }
  97         return c2;
  98     }
  99 
 100     public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
 101         int count = endIndex - beginIndex;
 102         for (int i = beginIndex; i < endIndex; ) {
 103             if (Character.isHighSurrogate(getChar(value, i++)) &&
 104                 i < endIndex &&
 105                 Character.isLowSurrogate(getChar(value, i))) {
 106                 count--;
 107                 i++;
 108             }
 109         }
 110         return count;
 111     }
 112 
 113     public static char[] toChars(byte[] value) {
 114         char[] dst = new char[value.length >> 1];
 115         getChars(value, 0, dst.length, dst, 0);
 116         return dst;
 117     }
 118 
 119     @HotSpotIntrinsicCandidate
 120     public static byte[] toBytes(char[] value, int off, int len) {
 121         byte[] val = newBytesFor(len);
 122         for (int i = 0; i < len; i++) {
 123             putChar(val, i, value[off++]);
 124         }
 125         return val;
 126     }
 127 
 128     public static byte[] compress(char[] val, int off, int len) {
 129         byte[] ret = new byte[len];
 130         if (compress(val, off, ret, 0, len) == len) {
 131             return ret;
 132         }
 133         return null;
 134     }
 135 
 136     public static byte[] compress(byte[] val, int off, int len) {
 137         byte[] ret = new byte[len];
 138         if (compress(val, off, ret, 0, len) == len) {
 139             return ret;
 140         }
 141         return null;
 142     }
 143 
 144     // compressedCopy char[] -> byte[]
 145     @HotSpotIntrinsicCandidate
 146     private static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) {
 147         for (int i = 0; i < len; i++) {
 148             int c = src[srcOff++];
 149             if (c >>> 8 != 0) {
 150                 return 0;
 151             }
 152             dst[dstOff++] = (byte)c;
 153         }
 154         return len;
 155     }
 156 
 157     // compressedCopy byte[] -> byte[]
 158     @HotSpotIntrinsicCandidate
 159     public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
 160         // We need a range check here because 'getChar' has no checks
 161         checkBoundsOffCount(srcOff, len, src.length);
 162         for (int i = 0; i < len; i++) {
 163             int c = getChar(src, srcOff++);
 164             if (c >>> 8 != 0) {
 165                 return 0;
 166             }
 167             dst[dstOff++] = (byte)c;
 168         }
 169         return len;
 170     }
 171 
 172     public static byte[] toBytes(int[] val, int index, int len) {
 173         final int end = index + len;
 174         // Pass 1: Compute precise size of char[]
 175         int n = len;
 176         for (int i = index; i < end; i++) {
 177             int cp = val[i];
 178             if (Character.isBmpCodePoint(cp))
 179                 continue;
 180             else if (Character.isValidCodePoint(cp))
 181                 n++;
 182             else throw new IllegalArgumentException(Integer.toString(cp));
 183         }
 184         // Pass 2: Allocate and fill in <high, low> pair
 185         byte[] buf = newBytesFor(n);
 186         for (int i = index, j = 0; i < end; i++, j++) {
 187             int cp = val[i];
 188             if (Character.isBmpCodePoint(cp)) {
 189                 putChar(buf, j, cp);
 190             } else {
 191                 putChar(buf, j++, Character.highSurrogate(cp));
 192                 putChar(buf, j, Character.lowSurrogate(cp));
 193             }
 194         }
 195         return buf;
 196     }
 197 
 198     public static byte[] toBytes(char c) {
 199         byte[] result = new byte[2];
 200         putChar(result, 0, c);
 201         return result;
 202     }
 203 
 204     @HotSpotIntrinsicCandidate
 205     public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
 206         // We need a range check here because 'getChar' has no checks
 207         checkBoundsOffCount(srcBegin, srcEnd - srcBegin, value.length);
 208         for (int i = srcBegin; i < srcEnd; i++) {
 209             dst[dstBegin++] = getChar(value, i);
 210         }
 211     }
 212 
 213     /* @see java.lang.String.getBytes(int, int, byte[], int) */
 214     public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) {
 215         srcBegin <<= 1;
 216         srcEnd <<= 1;
 217         for (int i = srcBegin + (1 >> LO_BYTE_SHIFT); i < srcEnd; i += 2) {
 218             dst[dstBegin++] = value[i];
 219         }
 220     }
 221 
 222     @HotSpotIntrinsicCandidate
 223     public static boolean equals(byte[] value, byte[] other) {
 224         if (value.length == other.length) {
 225             int len = value.length >> 1;
 226             for (int i = 0; i < len; i++) {
 227                 if (getChar(value, i) != getChar(other, i)) {
 228                     return false;
 229                 }
 230             }
 231             return true;
 232         }
 233         return false;
 234     }
 235 
 236     @HotSpotIntrinsicCandidate
 237     public static int compareTo(byte[] value, byte[] other) {
 238         int len1 = length(value);
 239         int len2 = length(other);
 240         int lim = Math.min(len1, len2);
 241         for (int k = 0; k < lim; k++) {
 242             char c1 = getChar(value, k);
 243             char c2 = getChar(other, k);
 244             if (c1 != c2) {
 245                 return c1 - c2;
 246             }
 247         }
 248         return len1 - len2;
 249     }
 250 
 251     @HotSpotIntrinsicCandidate
 252     public static int compareToLatin1(byte[] value, byte[] other) {
 253         int len1 = length(value);
 254         int len2 = StringLatin1.length(other);
 255         int lim = Math.min(len1, len2);
 256         for (int k = 0; k < lim; k++) {
 257             char c1 = getChar(value, k);
 258             char c2 = StringLatin1.getChar(other, k);
 259             if (c1 != c2) {
 260                 return c1 - c2;
 261             }
 262         }
 263         return len1 - len2;
 264     }
 265 
 266     public static int hashCode(byte[] value) {
 267         int h = 0;
 268         int length = value.length >> 1;
 269         for (int i = 0; i < length; i++) {
 270             h = 31 * h + getChar(value, i);
 271         }
 272         return h;
 273     }
 274 
 275     public static int indexOf(byte[] value, int ch, int fromIndex) {
 276         int max = value.length >> 1;
 277         if (fromIndex < 0) {
 278             fromIndex = 0;
 279         } else if (fromIndex >= max) {
 280             // Note: fromIndex might be near -1>>>1.
 281             return -1;
 282         }
 283         if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
 284             // handle most cases here (ch is a BMP code point or a
 285             // negative value (invalid code point))
 286             return indexOfChar(value, ch, fromIndex, max);
 287         } else {
 288             return indexOfSupplementary(value, ch, fromIndex, max);
 289         }
 290     }
 291 
 292     @HotSpotIntrinsicCandidate
 293     public static int indexOf(byte[] value, byte[] str) {
 294         if (str.length == 0) {
 295             return 0;
 296         }
 297         if (value.length == 0) {
 298             return -1;
 299         }
 300         return indexOf(value, length(value), str, length(str), 0);
 301     }
 302 
 303     @HotSpotIntrinsicCandidate
 304     public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
 305         char first = getChar(str, 0);
 306         int max = (valueCount - strCount);
 307         for (int i = fromIndex; i <= max; i++) {
 308             // Look for first character.
 309             if (getChar(value, i) != first) {
 310                 while (++i <= max && getChar(value, i) != first);
 311             }
 312             // Found first character, now look at the rest of value
 313             if (i <= max) {
 314                 int j = i + 1;
 315                 int end = j + strCount - 1;
 316                 for (int k = 1; j < end && getChar(value, j) == getChar(str, k); j++, k++);
 317                 if (j == end) {
 318                     // Found whole string.
 319                     return i;
 320                 }
 321             }
 322         }
 323         return -1;
 324     }
 325 
 326     /**
 327      * Handles indexOf Latin1 substring in UTF16 string.
 328      */
 329     @HotSpotIntrinsicCandidate
 330     public static int indexOfLatin1(byte[] value, byte[] str) {
 331         if (str.length == 0) {
 332             return 0;
 333         }
 334         if (value.length == 0) {
 335             return -1;
 336         }
 337         return indexOfLatin1(value, length(value), str, str.length, 0);
 338     }
 339 
 340     @HotSpotIntrinsicCandidate
 341     public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) {
 342         char first = (char)(tgt[0] & 0xff);
 343         int max = (srcCount - tgtCount);
 344         for (int i = fromIndex; i <= max; i++) {
 345             // Look for first character.
 346             if (getChar(src, i) != first) {
 347                 while (++i <= max && getChar(src, i) != first);
 348             }
 349             // Found first character, now look at the rest of v2
 350             if (i <= max) {
 351                 int j = i + 1;
 352                 int end = j + tgtCount - 1;
 353                 for (int k = 1;
 354                      j < end && getChar(src, j) == (tgt[k] & 0xff);
 355                      j++, k++);
 356                 if (j == end) {
 357                     // Found whole string.
 358                     return i;
 359                 }
 360             }
 361         }
 362         return -1;
 363     }
 364 
 365     @HotSpotIntrinsicCandidate
 366     private static int indexOfChar(byte[] value, int ch, int fromIndex, int max) {
 367         for (int i = fromIndex; i < max; i++) {
 368             if (getChar(value, i) == ch) {
 369                 return i;
 370             }
 371         }
 372         return -1;
 373     }
 374 
 375     /**
 376      * Handles (rare) calls of indexOf with a supplementary character.
 377      */
 378     private static int indexOfSupplementary(byte[] value, int ch, int fromIndex, int max) {
 379         if (Character.isValidCodePoint(ch)) {
 380             final char hi = Character.highSurrogate(ch);
 381             final char lo = Character.lowSurrogate(ch);
 382             for (int i = fromIndex; i < max - 1; i++) {
 383                 if (getChar(value, i) == hi && getChar(value, i + 1 ) == lo) {
 384                     return i;
 385                 }
 386             }
 387         }
 388         return -1;
 389     }
 390 
 391     public static int lastIndexOf(byte[] src, int srcCount,
 392                                   byte[] tgt, int tgtCount, int fromIndex) {
 393         int min = tgtCount - 1;
 394         int i = min + fromIndex;
 395         int strLastIndex = tgtCount - 1;
 396         char strLastChar = getChar(tgt, strLastIndex);
 397 
 398     startSearchForLastChar:
 399         while (true) {
 400             while (i >= min && getChar(src, i) != strLastChar) {
 401                 i--;
 402             }
 403             if (i < min) {
 404                 return -1;
 405             }
 406             int j = i - 1;
 407             int start = j - strLastIndex;
 408             int k = strLastIndex - 1;
 409             while (j > start) {
 410                 if (getChar(src, j--) != getChar(tgt, k--)) {
 411                     i--;
 412                     continue startSearchForLastChar;
 413                 }
 414             }
 415             return start + 1;
 416         }
 417     }
 418 
 419     public static int lastIndexOf(byte[] value, int ch, int fromIndex) {
 420         if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
 421             // handle most cases here (ch is a BMP code point or a
 422             // negative value (invalid code point))
 423             int i = Math.min(fromIndex, (value.length >> 1) - 1);
 424             for (; i >= 0; i--) {
 425                 if (getChar(value, i) == ch) {
 426                     return i;
 427                 }
 428             }
 429             return -1;
 430         } else {
 431             return lastIndexOfSupplementary(value, ch, fromIndex);
 432         }
 433     }
 434 
 435     /**
 436      * Handles (rare) calls of lastIndexOf with a supplementary character.
 437      */
 438     private static int lastIndexOfSupplementary(final byte[] value, int ch, int fromIndex) {
 439         if (Character.isValidCodePoint(ch)) {
 440             char hi = Character.highSurrogate(ch);
 441             char lo = Character.lowSurrogate(ch);
 442             int i = Math.min(fromIndex, (value.length >> 1) - 2);
 443             for (; i >= 0; i--) {
 444                 if (getChar(value, i) == hi && getChar(value, i + 1) == lo) {
 445                     return i;
 446                 }
 447             }
 448         }
 449         return -1;
 450     }
 451 
 452     public static String replace(byte[] value, char oldChar, char newChar) {
 453         int len = value.length >> 1;
 454         int i = -1;
 455         while (++i < len) {
 456             if (getChar(value, i) == oldChar) {
 457                 break;
 458             }
 459         }
 460         if (i < len) {
 461             byte buf[] = new byte[value.length];
 462             for (int j = 0; j < i; j++) {
 463                 putChar(buf, j, getChar(value, j)); // TBD:arraycopy?
 464             }
 465             while (i < len) {
 466                 char c = getChar(value, i);
 467                 putChar(buf, i, c == oldChar ? newChar : c);
 468                 i++;
 469            }
 470            // Check if we should try to compress to latin1
 471            if (String.COMPACT_STRINGS &&
 472                !StringLatin1.canEncode(oldChar) &&
 473                StringLatin1.canEncode(newChar)) {
 474                byte[] val = compress(buf, 0, len);
 475                if (val != null) {
 476                    return new String(val, LATIN1);
 477                }
 478            }
 479            return new String(buf, UTF16);
 480         }
 481         return null;
 482     }
 483 
 484     public static boolean regionMatchesCI(byte[] value, int toffset,
 485                                           byte[] other, int ooffset, int len) {
 486         int last = toffset + len;
 487         while (toffset < last) {
 488             char c1 = getChar(value, toffset++);
 489             char c2 = getChar(other, ooffset++);
 490             if (c1 == c2) {
 491                 continue;
 492             }
 493             // try converting both characters to uppercase.
 494             // If the results match, then the comparison scan should
 495             // continue.
 496             char u1 = Character.toUpperCase(c1);
 497             char u2 = Character.toUpperCase(c2);
 498             if (u1 == u2) {
 499                 continue;
 500             }
 501             // Unfortunately, conversion to uppercase does not work properly
 502             // for the Georgian alphabet, which has strange rules about case
 503             // conversion.  So we need to make one last check before
 504             // exiting.
 505             if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
 506                 continue;
 507             }
 508             return false;
 509         }
 510         return true;
 511     }
 512 
 513     public static boolean regionMatchesCI_Latin1(byte[] value, int toffset,
 514                                                  byte[] other, int ooffset,
 515                                                  int len) {
 516         int last = toffset + len;
 517         while (toffset < last) {
 518             char c1 = getChar(value, toffset++);
 519             char c2 = (char)(other[ooffset++] & 0xff);
 520             if (c1 == c2) {
 521                 continue;
 522             }
 523             char u1 = Character.toUpperCase(c1);
 524             char u2 = Character.toUpperCase(c2);
 525             if (u1 == u2) {
 526                 continue;
 527             }
 528             if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
 529                 continue;
 530             }
 531             return false;
 532         }
 533         return true;
 534     }
 535 
 536     public static String toLowerCase(String str, byte[] value, Locale locale) {
 537         if (locale == null) {
 538             throw new NullPointerException();
 539         }
 540         int first;
 541         boolean hasSurr = false;
 542         final int len = value.length >> 1;
 543 
 544         // Now check if there are any characters that need to be changed, or are surrogate
 545         for (first = 0 ; first < len; first++) {
 546             int cp = (int)getChar(value, first);
 547             if (Character.isSurrogate((char)cp)) {
 548                 hasSurr = true;
 549                 break;
 550             }
 551             if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
 552                 break;
 553             }
 554         }
 555         if (first == len)
 556             return str;
 557         byte[] result = new byte[value.length];
 558         System.arraycopy(value, 0, result, 0, first << 1);  // Just copy the first few
 559                                                             // lowerCase characters.
 560         String lang = locale.getLanguage();
 561         if (lang == "tr" || lang == "az" || lang == "lt") {
 562             return toLowerCaseEx(str, value, result, first, locale, true);
 563         }
 564         if (hasSurr) {
 565             return toLowerCaseEx(str, value, result, first, locale, false);
 566         }
 567         int bits = 0;
 568         for (int i = first; i < len; i++) {
 569             int cp = (int)getChar(value, i);
 570             if (cp == '\u03A3' ||                       // GREEK CAPITAL LETTER SIGMA
 571                 Character.isSurrogate((char)cp)) {
 572                 return toLowerCaseEx(str, value, result, i, locale, false);
 573             }
 574             if (cp == '\u0130') {                       // LATIN CAPITAL LETTER I WITH DOT ABOVE
 575                 return toLowerCaseEx(str, value, result, i, locale, true);
 576             }
 577             cp = Character.toLowerCase(cp);
 578             if (!Character.isBmpCodePoint(cp)) {
 579                 return toLowerCaseEx(str, value, result, i, locale, false);
 580             }
 581             bits |= cp;
 582             putChar(result, i, cp);
 583         }
 584         if (bits >>> 8 != 0) {
 585             return new String(result, UTF16);
 586         } else {
 587             return newString(result, 0, len);
 588         }
 589     }
 590 
 591     private static String toLowerCaseEx(String str, byte[] value,
 592                                         byte[] result, int first, Locale locale,
 593                                         boolean localeDependent) {
 594         int resultOffset = first;
 595         int length = value.length >> 1;
 596         int srcCount;
 597         for (int i = first; i < length; i += srcCount) {
 598             int srcChar = getChar(value, i);
 599             int lowerChar;
 600             char[] lowerCharArray;
 601             srcCount = 1;
 602             if (Character.isSurrogate((char)srcChar)) {
 603                 srcChar = codePointAt(value, i, length);
 604                 srcCount = Character.charCount(srcChar);
 605             }
 606             if (localeDependent ||
 607                 srcChar == '\u03A3' ||  // GREEK CAPITAL LETTER SIGMA
 608                 srcChar == '\u0130') {  // LATIN CAPITAL LETTER I WITH DOT ABOVE
 609                 lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale);
 610             } else {
 611                 lowerChar = Character.toLowerCase(srcChar);
 612             }
 613             if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
 614                 putChar(result, resultOffset++, lowerChar);
 615             } else {
 616                 if (lowerChar == Character.ERROR) {
 617                     lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale);
 618                 } else {
 619                     lowerCharArray = Character.toChars(lowerChar);
 620                 }
 621                 /* Grow result if needed */
 622                 int mapLen = lowerCharArray.length;
 623                 if (mapLen > srcCount) {
 624                     byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount);
 625                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
 626                     result = result2;
 627                 }
 628                 for (int x = 0; x < mapLen; ++x) {
 629                     putChar(result, resultOffset++, lowerCharArray[x]);
 630                 }
 631             }
 632         }
 633         return newString(result, 0, resultOffset);
 634     }
 635 
 636     public static String toUpperCase(String str, byte[] value, Locale locale) {
 637         if (locale == null) {
 638             throw new NullPointerException();
 639         }
 640         int first;
 641         boolean hasSurr = false;
 642         final int len = value.length >> 1;
 643 
 644         // Now check if there are any characters that need to be changed, or are surrogate
 645         for (first = 0 ; first < len; first++) {
 646             int cp = (int)getChar(value, first);
 647             if (Character.isSurrogate((char)cp)) {
 648                 hasSurr = true;
 649                 break;
 650             }
 651             if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
 652                 break;
 653             }
 654         }
 655         if (first == len) {
 656             return str;
 657         }
 658         byte[] result = new byte[value.length];
 659         System.arraycopy(value, 0, result, 0, first << 1); // Just copy the first few
 660                                                            // upperCase characters.
 661         String lang = locale.getLanguage();
 662         if (lang == "tr" || lang == "az" || lang == "lt") {
 663             return toUpperCaseEx(str, value, result, first, locale, true);
 664         }
 665         if (hasSurr) {
 666             return toUpperCaseEx(str, value, result, first, locale, false);
 667         }
 668         int bits = 0;
 669         for (int i = first; i < len; i++) {
 670             int cp = (int)getChar(value, i);
 671             if (Character.isSurrogate((char)cp)) {
 672                 return toUpperCaseEx(str, value, result, i, locale, false);
 673             }
 674             cp = Character.toUpperCaseEx(cp);
 675             if (!Character.isBmpCodePoint(cp)) {    // Character.ERROR is not bmp
 676                 return toUpperCaseEx(str, value, result, i, locale, false);
 677             }
 678             bits |= cp;
 679             putChar(result, i, cp);
 680         }
 681         if (bits >>> 8 != 0) {
 682             return new String(result, UTF16);
 683         } else {
 684             return newString(result, 0, len);
 685         }
 686     }
 687 
 688     private static String toUpperCaseEx(String str, byte[] value,
 689                                         byte[] result, int first,
 690                                         Locale locale, boolean localeDependent)
 691     {
 692         int resultOffset = first;
 693         int length = value.length >> 1;
 694         int srcCount;
 695         for (int i = first; i < length; i += srcCount) {
 696             int srcChar = getChar(value, i);
 697             int upperChar;
 698             char[] upperCharArray;
 699             srcCount = 1;
 700             if (Character.isSurrogate((char)srcChar)) {
 701                 srcChar = codePointAt(value, i, length);
 702                 srcCount = Character.charCount(srcChar);
 703             }
 704             if (localeDependent) {
 705                 upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale);
 706             } else {
 707                 upperChar = Character.toUpperCaseEx(srcChar);
 708             }
 709             if (Character.isBmpCodePoint(upperChar)) {
 710                 putChar(result, resultOffset++, upperChar);
 711             } else {
 712                 if (upperChar == Character.ERROR) {
 713                     if (localeDependent) {
 714                         upperCharArray =
 715                             ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale);
 716                     } else {
 717                         upperCharArray = Character.toUpperCaseCharArray(srcChar);
 718                     }
 719                 } else {
 720                     upperCharArray = Character.toChars(upperChar);
 721                 }
 722                 /* Grow result if needed */
 723                 int mapLen = upperCharArray.length;
 724                 if (mapLen > srcCount) {
 725                     byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount);
 726                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
 727                     result = result2;
 728                  }
 729                  for (int x = 0; x < mapLen; ++x) {
 730                     putChar(result, resultOffset++, upperCharArray[x]);
 731                  }
 732             }
 733         }
 734         return newString(result, 0, resultOffset);
 735     }
 736 
 737     public static String trim(byte[] value) {
 738         int length = value.length >> 1;
 739         int len = length;
 740         int st = 0;
 741         while (st < len && getChar(value, st) <= ' ') {
 742             st++;
 743         }
 744         while (st < len && getChar(value, len - 1) <= ' ') {
 745             len--;
 746         }
 747         return ((st > 0) || (len < length )) ?
 748             new String(Arrays.copyOfRange(value, st << 1, len << 1), UTF16) :
 749             null;
 750     }
 751 
 752     public static void putChars(byte[] val, int index, char[] str, int off, int end) {
 753         while (off < end) {
 754             putChar(val, index++, str[off++]);
 755         }
 756     }
 757 
 758     public static String newString(byte[] val, int index, int len) {
 759         if (String.COMPACT_STRINGS) {
 760             byte[] buf = compress(val, index, len);
 761             if (buf != null) {
 762                 return new String(buf, LATIN1);
 763             }
 764         }
 765         int last = index + len;
 766         return new String(Arrays.copyOfRange(val, index << 1, last << 1), UTF16);
 767     }
 768 
 769     public static void fillNull(byte[] val, int index, int end) {
 770         Arrays.fill(val, index << 1, end << 1, (byte)0);
 771     }
 772 
 773     static class CharsSpliterator implements Spliterator.OfInt {
 774         private final byte[] array;
 775         private int index;        // current index, modified on advance/split
 776         private final int fence;  // one past last index
 777         private final int cs;
 778 
 779         CharsSpliterator(byte[] array, int acs) {
 780             this(array, 0, array.length >> 1, acs);
 781         }
 782 
 783         CharsSpliterator(byte[] array, int origin, int fence, int acs) {
 784             this.array = array;
 785             this.index = origin;
 786             this.fence = fence;
 787             this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
 788                       | Spliterator.SUBSIZED;
 789         }
 790 
 791         @Override
 792         public OfInt trySplit() {
 793             int lo = index, mid = (lo + fence) >>> 1;
 794             return (lo >= mid)
 795                    ? null
 796                    : new CharsSpliterator(array, lo, index = mid, cs);
 797         }
 798 
 799         @Override
 800         public void forEachRemaining(IntConsumer action) {
 801             byte[] a; int i, hi; // hoist accesses and checks from loop
 802             if (action == null)
 803                 throw new NullPointerException();
 804             if (((a = array).length >> 1) >= (hi = fence) &&
 805                 (i = index) >= 0 && i < (index = hi)) {
 806                 do { action.accept(getChar(a, i)); } while (++i < hi);
 807             }
 808         }
 809 
 810         @Override
 811         public boolean tryAdvance(IntConsumer action) {
 812             if (action == null)
 813                 throw new NullPointerException();
 814             if (index >= 0 && index < fence) {
 815                 action.accept(getChar(array, index++));
 816                 return true;
 817             }
 818             return false;
 819         }
 820 
 821         @Override
 822         public long estimateSize() { return (long)(fence - index); }
 823 
 824         @Override
 825         public int characteristics() {
 826             return cs;
 827         }
 828     }
 829 
 830     static class CodePointsSpliterator implements Spliterator.OfInt {
 831         private final byte[] array;
 832         private int index;        // current index, modified on advance/split
 833         private final int fence;  // one past last index
 834         private final int cs;
 835 
 836         CodePointsSpliterator(byte[] array, int acs) {
 837             this(array, 0, array.length >> 1, acs);
 838         }
 839 
 840         CodePointsSpliterator(byte[] array, int origin, int fence, int acs) {
 841             this.array = array;
 842             this.index = origin;
 843             this.fence = fence;
 844             this.cs = acs | Spliterator.ORDERED;
 845         }
 846 
 847         @Override
 848         public OfInt trySplit() {
 849             int lo = index, mid = (lo + fence) >>> 1;
 850             if (lo >= mid)
 851                 return null;
 852 
 853             int midOneLess;
 854             // If the mid-point intersects a surrogate pair
 855             if (Character.isLowSurrogate(getChar(array, mid)) &&
 856                 Character.isHighSurrogate(getChar(array, midOneLess = (mid -1)))) {
 857                 // If there is only one pair it cannot be split
 858                 if (lo >= midOneLess)
 859                     return null;
 860                 // Shift the mid-point to align with the surrogate pair
 861                 return new CodePointsSpliterator(array, lo, index = midOneLess, cs);
 862             }
 863             return new CodePointsSpliterator(array, lo, index = mid, cs);
 864         }
 865 
 866         @Override
 867         public void forEachRemaining(IntConsumer action) {
 868             byte[] a; int i, hi; // hoist accesses and checks from loop
 869             if (action == null)
 870                 throw new NullPointerException();
 871             if (((a = array).length >> 1) >= (hi = fence) &&
 872                 (i = index) >= 0 && i < (index = hi)) {
 873                 do {
 874                     i = advance(a, i, hi, action);
 875                 } while (i < hi);
 876             }
 877         }
 878 
 879         @Override
 880         public boolean tryAdvance(IntConsumer action) {
 881             if (action == null)
 882                 throw new NullPointerException();
 883             if (index >= 0 && index < fence) {
 884                 index = advance(array, index, fence, action);
 885                 return true;
 886             }
 887             return false;
 888         }
 889 
 890         // Advance one code point from the index, i, and return the next
 891         // index to advance from
 892         private static int advance(byte[] a, int i, int hi, IntConsumer action) {
 893             char c1 = getChar(a, i++);
 894             int cp = c1;
 895             if (Character.isHighSurrogate(c1) && i < hi) {
 896                 char c2 = getChar(a, i);
 897                 if (Character.isLowSurrogate(c2)) {
 898                     i++;
 899                     cp = Character.toCodePoint(c1, c2);
 900                 }
 901             }
 902             action.accept(cp);
 903             return i;
 904         }
 905 
 906         @Override
 907         public long estimateSize() { return (long)(fence - index); }
 908 
 909         @Override
 910         public int characteristics() {
 911             return cs;
 912         }
 913     }
 914 
 915     ////////////////////////////////////////////////////////////////
 916 
 917     public static void putCharSB(byte[] val, int index, int c) {
 918         checkIndex(index, val.length >> 1);
 919         putChar(val, index, c);
 920     }
 921 
 922     public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) {
 923         checkOffset(index + end - off, val.length >> 1);
 924         putChars(val, index, ca, off, end);
 925     }
 926 
 927     public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) {
 928         checkOffset(index + end - off, val.length >> 1);
 929         for (int i = off; i < end; i++) {
 930             putChar(val, index++, s.charAt(i));
 931         }
 932     }
 933 
 934     public static int codePointAtSB(byte[] val, int index, int end) {
 935         checkOffset(end, val.length >> 1);
 936         return codePointAt(val, index, end);
 937     }
 938 
 939     public static int codePointBeforeSB(byte[] val, int index) {
 940         checkOffset(index, val.length >> 1);
 941         return codePointBefore(val, index);
 942     }
 943 
 944     public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) {
 945         checkOffset(endIndex, val.length >> 1);
 946         return codePointCount(val, beginIndex, endIndex);
 947     }
 948 
 949     ////////////////////////////////////////////////////////////////
 950 
 951     private static native boolean isBigEndian();
 952 
 953     static final int HI_BYTE_SHIFT;
 954     static final int LO_BYTE_SHIFT;
 955     static {
 956         if (isBigEndian()) {
 957             HI_BYTE_SHIFT = 8;
 958             LO_BYTE_SHIFT = 0;
 959         } else {
 960             HI_BYTE_SHIFT = 0;
 961             LO_BYTE_SHIFT = 8;
 962         }
 963     }
 964 
 965     static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
 966 }