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