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