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