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 = StringConcatHelper.newArray(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     public static String replace(byte[] value, int valLen, byte[] targ,
 342                                  int targLen, byte[] repl, int replLen)
 343     {
 344         assert targLen > 0;
 345         int i, j, p = 0;
 346         if (valLen == 0 || (i = indexOf(value, valLen, targ, targLen, 0)) < 0) {
 347             return null; // for string to return this;
 348         }
 349 
 350         // find and store indices of substrings to replace
 351         int[] pos = new int[16];
 352         pos[0] = i;
 353         i += targLen;
 354         while ((j = indexOf(value, valLen, targ, targLen, i)) > 0) {
 355             if (++p == pos.length) {
 356                 int cap = p + (p >> 1);
 357                 // overflow-conscious code
 358                 if (cap - MAX_ARRAY_SIZE > 0) {
 359                     if (p == MAX_ARRAY_SIZE) {
 360                         throw new OutOfMemoryError();
 361                     }
 362                     cap = MAX_ARRAY_SIZE;
 363                 }
 364                 pos = Arrays.copyOf(pos, cap);
 365             }
 366             pos[p] = j;
 367             i = j + targLen;
 368         }
 369 
 370         int resultLen;
 371         try {
 372             resultLen = Math.addExact(valLen,
 373                     Math.multiplyExact(++p, replLen - targLen));
 374         } catch (ArithmeticException ignored) {
 375             throw new OutOfMemoryError();
 376         }
 377         if (resultLen == 0) {
 378             return "";
 379         }
 380 
 381         byte[] result = StringConcatHelper.newArray(resultLen);
 382         int posFrom = 0, posTo = 0;
 383         for (int q = 0; q < p; ++q) {
 384             int nextPos = pos[q];
 385             while (posFrom < nextPos) {
 386                 result[posTo++] = value[posFrom++];
 387             }
 388             posFrom += targLen;
 389             for (int k = 0; k < replLen; ++k) {
 390                 result[posTo++] = repl[k];
 391             }
 392         }
 393         while (posFrom < valLen) {
 394             result[posTo++] = value[posFrom++];
 395         }
 396         return new String(result, LATIN1);
 397     }
 398 
 399     // case insensitive
 400     public static boolean regionMatchesCI(byte[] value, int toffset,
 401                                           byte[] other, int ooffset, int len) {
 402         int last = toffset + len;
 403         while (toffset < last) {
 404             char c1 = (char)(value[toffset++] & 0xff);
 405             char c2 = (char)(other[ooffset++] & 0xff);
 406             if (c1 == c2) {
 407                 continue;
 408             }
 409             char u1 = Character.toUpperCase(c1);
 410             char u2 = Character.toUpperCase(c2);
 411             if (u1 == u2) {
 412                 continue;
 413             }
 414             if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
 415                 continue;
 416             }
 417             return false;
 418         }
 419         return true;
 420     }
 421 
 422     public static boolean regionMatchesCI_UTF16(byte[] value, int toffset,
 423                                                 byte[] other, int ooffset, int len) {
 424         int last = toffset + len;
 425         while (toffset < last) {
 426             char c1 = (char)(value[toffset++] & 0xff);
 427             char c2 = StringUTF16.getChar(other, ooffset++);
 428             if (c1 == c2) {
 429                 continue;
 430             }
 431             char u1 = Character.toUpperCase(c1);
 432             char u2 = Character.toUpperCase(c2);
 433             if (u1 == u2) {
 434                 continue;
 435             }
 436             if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
 437                 continue;
 438             }
 439             return false;
 440         }
 441         return true;
 442     }
 443 
 444     public static String toLowerCase(String str, byte[] value, Locale locale) {
 445         if (locale == null) {
 446             throw new NullPointerException();
 447         }
 448         int first;
 449         final int len = value.length;
 450         // Now check if there are any characters that need to be changed, or are surrogate
 451         for (first = 0 ; first < len; first++) {
 452             int cp = value[first] & 0xff;
 453             if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
 454                 break;
 455             }
 456         }
 457         if (first == len)
 458             return str;
 459         String lang = locale.getLanguage();
 460         if (lang == "tr" || lang == "az" || lang == "lt") {
 461             return toLowerCaseEx(str, value, first, locale, true);
 462         }
 463         byte[] result = new byte[len];
 464         System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
 465                                                        // lowerCase characters.
 466         for (int i = first; i < len; i++) {
 467             int cp = value[i] & 0xff;
 468             cp = Character.toLowerCase(cp);
 469             if (!canEncode(cp)) {                      // not a latin1 character
 470                 return toLowerCaseEx(str, value, first, locale, false);
 471             }
 472             result[i] = (byte)cp;
 473         }
 474         return new String(result, LATIN1);
 475     }
 476 
 477     private static String toLowerCaseEx(String str, byte[] value,
 478                                         int first, Locale locale, boolean localeDependent)
 479     {
 480         byte[] result = StringUTF16.newBytesFor(value.length);
 481         int resultOffset = 0;
 482         for (int i = 0; i < first; i++) {
 483             StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
 484         }
 485         for (int i = first; i < value.length; i++) {
 486             int srcChar = value[i] & 0xff;
 487             int lowerChar;
 488             char[] lowerCharArray;
 489             if (localeDependent) {
 490                 lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale);
 491             } else {
 492                 lowerChar = Character.toLowerCase(srcChar);
 493             }
 494             if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
 495                 StringUTF16.putChar(result, resultOffset++, lowerChar);
 496             } else {
 497                 if (lowerChar == Character.ERROR) {
 498                     lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale);
 499                 } else {
 500                     lowerCharArray = Character.toChars(lowerChar);
 501                 }
 502                 /* Grow result if needed */
 503                 int mapLen = lowerCharArray.length;
 504                 if (mapLen > 1) {
 505                     byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
 506                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
 507                     result = result2;
 508                 }
 509                 for (int x = 0; x < mapLen; ++x) {
 510                     StringUTF16.putChar(result, resultOffset++, lowerCharArray[x]);
 511                 }
 512             }
 513         }
 514         return StringUTF16.newString(result, 0, resultOffset);
 515     }
 516 
 517     public static String toUpperCase(String str, byte[] value, Locale locale) {
 518         if (locale == null) {
 519             throw new NullPointerException();
 520         }
 521         int first;
 522         final int len = value.length;
 523 
 524         // Now check if there are any characters that need to be changed, or are surrogate
 525         for (first = 0 ; first < len; first++ ) {
 526             int cp = value[first] & 0xff;
 527             if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
 528                 break;
 529             }
 530         }
 531         if (first == len) {
 532             return str;
 533         }
 534         String lang = locale.getLanguage();
 535         if (lang == "tr" || lang == "az" || lang == "lt") {
 536             return toUpperCaseEx(str, value, first, locale, true);
 537         }
 538         byte[] result = new byte[len];
 539         System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
 540                                                        // upperCase characters.
 541         for (int i = first; i < len; i++) {
 542             int cp = value[i] & 0xff;
 543             cp = Character.toUpperCaseEx(cp);
 544             if (!canEncode(cp)) {                      // not a latin1 character
 545                 return toUpperCaseEx(str, value, first, locale, false);
 546             }
 547             result[i] = (byte)cp;
 548         }
 549         return new String(result, LATIN1);
 550     }
 551 
 552     private static String toUpperCaseEx(String str, byte[] value,
 553                                         int first, Locale locale, boolean localeDependent)
 554     {
 555         byte[] result = StringUTF16.newBytesFor(value.length);
 556         int resultOffset = 0;
 557         for (int i = 0; i < first; i++) {
 558             StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
 559         }
 560         for (int i = first; i < value.length; i++) {
 561             int srcChar = value[i] & 0xff;
 562             int upperChar;
 563             char[] upperCharArray;
 564             if (localeDependent) {
 565                 upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale);
 566             } else {
 567                 upperChar = Character.toUpperCaseEx(srcChar);
 568             }
 569             if (Character.isBmpCodePoint(upperChar)) {
 570                 StringUTF16.putChar(result, resultOffset++, upperChar);
 571             } else {
 572                 if (upperChar == Character.ERROR) {
 573                     if (localeDependent) {
 574                         upperCharArray =
 575                             ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale);
 576                     } else {
 577                         upperCharArray = Character.toUpperCaseCharArray(srcChar);
 578                     }
 579                 } else {
 580                     upperCharArray = Character.toChars(upperChar);
 581                 }
 582                 /* Grow result if needed */
 583                 int mapLen = upperCharArray.length;
 584                 if (mapLen > 1) {
 585                     byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
 586                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
 587                     result = result2;
 588                 }
 589                 for (int x = 0; x < mapLen; ++x) {
 590                     StringUTF16.putChar(result, resultOffset++, upperCharArray[x]);
 591                 }
 592             }
 593         }
 594         return StringUTF16.newString(result, 0, resultOffset);
 595     }
 596 
 597     public static String trim(byte[] value) {
 598         int len = value.length;
 599         int st = 0;
 600         while ((st < len) && ((value[st] & 0xff) <= ' ')) {
 601             st++;
 602         }
 603         while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) {
 604             len--;
 605         }
 606         return ((st > 0) || (len < value.length)) ?
 607             newString(value, st, len - st) : null;
 608     }
 609 
 610     public static int indexOfNonWhitespace(byte[] value) {
 611         int length = value.length;
 612         int left = 0;
 613         while (left < length) {
 614             char ch = getChar(value, left);
 615             if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
 616                 break;
 617             }
 618             left++;
 619         }
 620         return left;
 621     }
 622 
 623     public static int lastIndexOfNonWhitespace(byte[] value) {
 624         int length = value.length;
 625         int right = length;
 626         while (0 < right) {
 627             char ch = getChar(value, right - 1);
 628             if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
 629                 break;
 630             }
 631             right--;
 632         }
 633         return right;
 634     }
 635 
 636     public static String strip(byte[] value) {
 637         int left = indexOfNonWhitespace(value);
 638         if (left == value.length) {
 639             return "";
 640         }
 641         int right = lastIndexOfNonWhitespace(value);
 642         boolean ifChanged = (left > 0) || (right < value.length);
 643         return ifChanged ? newString(value, left, right - left) : null;
 644     }
 645 
 646     public static String stripLeading(byte[] value) {
 647         int left = indexOfNonWhitespace(value);
 648         if (left == value.length) {
 649             return "";
 650         }
 651         return (left != 0) ? newString(value, left, value.length - left) : null;
 652     }
 653 
 654     public static String stripTrailing(byte[] value) {
 655         int right = lastIndexOfNonWhitespace(value);
 656         if (right == 0) {
 657             return "";
 658         }
 659         return (right != value.length) ? newString(value, 0, right) : null;
 660     }
 661 
 662     private final static class LinesSpliterator implements Spliterator<String> {
 663         private byte[] value;
 664         private int index;        // current index, modified on advance/split
 665         private final int fence;  // one past last index
 666 
 667         private LinesSpliterator(byte[] value, int start, int length) {
 668             this.value = value;
 669             this.index = start;
 670             this.fence = start + length;
 671         }
 672 
 673         private int indexOfLineSeparator(int start) {
 674             for (int current = start; current < fence; current++) {
 675                 char ch = getChar(value, current);
 676                 if (ch == '\n' || ch == '\r') {
 677                     return current;
 678                 }
 679             }
 680             return fence;
 681         }
 682 
 683         private int skipLineSeparator(int start) {
 684             if (start < fence) {
 685                 if (getChar(value, start) == '\r') {
 686                     int next = start + 1;
 687                     if (next < fence && getChar(value, next) == '\n') {
 688                         return next + 1;
 689                     }
 690                 }
 691                 return start + 1;
 692             }
 693             return fence;
 694         }
 695 
 696         private String next() {
 697             int start = index;
 698             int end = indexOfLineSeparator(start);
 699             index = skipLineSeparator(end);
 700             return newString(value, start, end - start);
 701         }
 702 
 703         @Override
 704         public boolean tryAdvance(Consumer<? super String> action) {
 705             if (action == null) {
 706                 throw new NullPointerException("tryAdvance action missing");
 707             }
 708             if (index != fence) {
 709                 action.accept(next());
 710                 return true;
 711             }
 712             return false;
 713         }
 714 
 715         @Override
 716         public void forEachRemaining(Consumer<? super String> action) {
 717             if (action == null) {
 718                 throw new NullPointerException("forEachRemaining action missing");
 719             }
 720             while (index != fence) {
 721                 action.accept(next());
 722             }
 723         }
 724 
 725         @Override
 726         public Spliterator<String> trySplit() {
 727             int half = (fence + index) >>> 1;
 728             int mid = skipLineSeparator(indexOfLineSeparator(half));
 729             if (mid < fence) {
 730                 int start = index;
 731                 index = mid;
 732                 return new LinesSpliterator(value, start, mid - start);
 733             }
 734             return null;
 735         }
 736 
 737         @Override
 738         public long estimateSize() {
 739             return fence - index + 1;
 740         }
 741 
 742         @Override
 743         public int characteristics() {
 744             return Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL;
 745         }
 746 
 747         static LinesSpliterator spliterator(byte[] value) {
 748             return new LinesSpliterator(value, 0, value.length);
 749         }
 750 
 751         static LinesSpliterator spliterator(byte[] value, int leading, int trailing) {
 752             int length = value.length;
 753             int left = 0;
 754             int index;
 755             for (int l = 0; l < leading; l++) {
 756                 index = skipBlankForward(value, left, length);
 757                 if (index == left) {
 758                     break;
 759                 }
 760                 left = index;
 761             }
 762             int right = length;
 763             for (int t = 0; t < trailing; t++) {
 764                 index = skipBlankBackward(value, left, right);
 765                 if (index == right) {
 766                     break;
 767                 }
 768                 right = index;
 769             }
 770             return new LinesSpliterator(value, left, right - left);
 771         }
 772 
 773         private static int skipBlankForward(byte[] value, int start, int length) {
 774             int index = start;
 775             while (index < length) {
 776                 char ch = getChar(value, index++);
 777                 if (ch == '\n') {
 778                     return index;
 779                 }
 780                 if (ch == '\r') {
 781                     if (index < length && getChar(value, index) == '\n') {
 782                         return index + 1;
 783                     }
 784                     return index;
 785                 }
 786                 if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
 787                     return start;
 788                 }
 789             }
 790             return length;
 791         }
 792 
 793         private static int skipBlankBackward(byte[] value, int start, int fence) {
 794             int index = fence;
 795             if (start < index && getChar(value, index - 1) == '\n') {
 796                 index--;
 797             }
 798             if (start < index && getChar(value, index - 1) == '\r') {
 799                 index--;
 800             }
 801             while (start < index) {
 802                 char ch = getChar(value, --index);
 803                 if (ch == '\r' || ch == '\n') {
 804                     return index + 1;
 805                 }
 806                 if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
 807                     return fence;
 808                 }
 809             }
 810             return start;
 811         }
 812     }
 813 
 814     static Stream<String> lines(byte[] value, int leading, int trailing) {
 815         if (leading == 0 && trailing == 0) {
 816             return StreamSupport.stream(LinesSpliterator.spliterator(value), false);
 817         } else {
 818             return StreamSupport.stream(LinesSpliterator.spliterator(value, leading, trailing), false);
 819         }
 820     }
 821 
 822     public static void putChar(byte[] val, int index, int c) {
 823         //assert (canEncode(c));
 824         val[index] = (byte)(c);
 825     }
 826 
 827     public static char getChar(byte[] val, int index) {
 828         return (char)(val[index] & 0xff);
 829     }
 830 
 831     public static byte[] toBytes(int[] val, int off, int len) {
 832         byte[] ret = new byte[len];
 833         for (int i = 0; i < len; i++) {
 834             int cp = val[off++];
 835             if (!canEncode(cp)) {
 836                 return null;
 837             }
 838             ret[i] = (byte)cp;
 839         }
 840         return ret;
 841     }
 842 
 843     public static byte[] toBytes(char c) {
 844         return new byte[] { (byte)c };
 845     }
 846 
 847     public static String newString(byte[] val, int index, int len) {
 848         return new String(Arrays.copyOfRange(val, index, index + len),
 849                           LATIN1);
 850     }
 851 
 852     public static void fillNull(byte[] val, int index, int end) {
 853         Arrays.fill(val, index, end, (byte)0);
 854     }
 855 
 856     // inflatedCopy byte[] -> char[]
 857     @HotSpotIntrinsicCandidate
 858     public static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
 859         for (int i = 0; i < len; i++) {
 860             dst[dstOff++] = (char)(src[srcOff++] & 0xff);
 861         }
 862     }
 863 
 864     // inflatedCopy byte[] -> byte[]
 865     @HotSpotIntrinsicCandidate
 866     public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
 867         StringUTF16.inflate(src, srcOff, dst, dstOff, len);
 868     }
 869 
 870     static class CharsSpliterator implements Spliterator.OfInt {
 871         private final byte[] array;
 872         private int index;        // current index, modified on advance/split
 873         private final int fence;  // one past last index
 874         private final int cs;
 875 
 876         CharsSpliterator(byte[] array, int acs) {
 877             this(array, 0, array.length, acs);
 878         }
 879 
 880         CharsSpliterator(byte[] array, int origin, int fence, int acs) {
 881             this.array = array;
 882             this.index = origin;
 883             this.fence = fence;
 884             this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
 885                       | Spliterator.SUBSIZED;
 886         }
 887 
 888         @Override
 889         public OfInt trySplit() {
 890             int lo = index, mid = (lo + fence) >>> 1;
 891             return (lo >= mid)
 892                    ? null
 893                    : new CharsSpliterator(array, lo, index = mid, cs);
 894         }
 895 
 896         @Override
 897         public void forEachRemaining(IntConsumer action) {
 898             byte[] a; int i, hi; // hoist accesses and checks from loop
 899             if (action == null)
 900                 throw new NullPointerException();
 901             if ((a = array).length >= (hi = fence) &&
 902                 (i = index) >= 0 && i < (index = hi)) {
 903                 do { action.accept(a[i] & 0xff); } while (++i < hi);
 904             }
 905         }
 906 
 907         @Override
 908         public boolean tryAdvance(IntConsumer action) {
 909             if (action == null)
 910                 throw new NullPointerException();
 911             if (index >= 0 && index < fence) {
 912                 action.accept(array[index++] & 0xff);
 913                 return true;
 914             }
 915             return false;
 916         }
 917 
 918         @Override
 919         public long estimateSize() { return (long)(fence - index); }
 920 
 921         @Override
 922         public int characteristics() {
 923             return cs;
 924         }
 925     }
 926 }