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