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