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