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