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.IntConsumer; 33 import java.util.stream.IntStream; 34 import jdk.internal.HotSpotIntrinsicCandidate; 35 36 import static java.lang.String.LATIN1; 37 import static java.lang.String.UTF16; 38 import static java.lang.String.checkOffset; 39 40 final class StringLatin1 { 41 42 public static char charAt(byte[] value, int index) { 43 if (index < 0 || index >= value.length) { 44 throw new StringIndexOutOfBoundsException(index); 45 } 46 return (char)(value[index] & 0xff); 47 } 48 49 public static boolean canEncode(int cp) { 50 return cp >>> 8 == 0; 51 } 52 53 public static int length(byte[] value) { 54 return value.length; 55 } 56 57 public static int codePointAt(byte[] value, int index, int end) { 58 return value[index] & 0xff; 59 } 60 61 public static int codePointBefore(byte[] value, int index) { 62 return value[index - 1] & 0xff; 63 } 64 65 public static int codePointCount(byte[] value, int beginIndex, int endIndex) { 66 return endIndex - beginIndex; 67 } 68 69 public static char[] toChars(byte[] value) { 70 char[] dst = new char[value.length]; 71 inflate(value, 0, dst, 0, value.length); 72 return dst; 73 } 74 75 public static byte[] inflate(byte[] value, int off, int len) { 76 byte[] ret = StringUTF16.newBytesFor(len); 77 inflate(value, off, ret, 0, len); 78 return ret; 79 } 80 81 public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) { 82 inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); 83 } 84 85 public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) { 86 System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); 87 } 88 89 @HotSpotIntrinsicCandidate 90 public static boolean equals(byte[] value, byte[] other) { 91 if (value.length == other.length) { 92 for (int i = 0; i < value.length; i++) { 93 if (value[i] != other[i]) { 94 return false; 95 } 96 } 97 return true; 98 } 99 return false; 100 } 101 102 @HotSpotIntrinsicCandidate 103 public static int compareTo(byte[] value, byte[] other) { 104 int len1 = value.length; 105 int len2 = other.length; 106 return compareTo(value, other, len1, len2); 107 } 108 109 public static int compareTo(byte[] value, byte[] other, int len1, int len2) { 110 int lim = Math.min(len1, len2); 111 for (int k = 0; k < lim; k++) { 112 if (value[k] != other[k]) { 113 return getChar(value, k) - getChar(other, k); 114 } 115 } 116 return len1 - len2; 117 } 118 119 @HotSpotIntrinsicCandidate 120 public static int compareToUTF16(byte[] value, byte[] other) { 121 int len1 = length(value); 122 int len2 = StringUTF16.length(other); 123 return compareToUTF16Values(value, other, len1, len2); 124 } 125 126 /* 127 * Checks the boundary and then compares the byte arrays. 128 */ 129 public static int compareToUTF16(byte[] value, byte[] other, int len1, int len2) { 130 checkOffset(len1, length(value)); 131 checkOffset(len2, StringUTF16.length(other)); 132 133 return compareToUTF16Values(value, other, len1, len2); 134 } 135 136 private static int compareToUTF16Values(byte[] value, byte[] other, int len1, int len2) { 137 int lim = Math.min(len1, len2); 138 for (int k = 0; k < lim; k++) { 139 char c1 = getChar(value, k); 140 char c2 = StringUTF16.getChar(other, k); 141 if (c1 != c2) { 142 return c1 - c2; 143 } 144 } 145 return len1 - len2; 146 } 147 148 public static int compareToCI(byte[] value, byte[] other) { 149 int len1 = value.length; 150 int len2 = other.length; 151 int lim = Math.min(len1, len2); 152 for (int k = 0; k < lim; k++) { 153 if (value[k] != other[k]) { 154 char c1 = (char) CharacterDataLatin1.instance.toUpperCase(getChar(value, k)); 155 char c2 = (char) CharacterDataLatin1.instance.toUpperCase(getChar(other, k)); 156 if (c1 != c2) { 157 c1 = Character.toLowerCase(c1); 158 c2 = Character.toLowerCase(c2); 159 if (c1 != c2) { 160 return c1 - c2; 161 } 162 } 163 } 164 } 165 return len1 - len2; 166 } 167 168 public static int compareToCI_UTF16(byte[] value, byte[] other) { 169 int len1 = length(value); 170 int len2 = StringUTF16.length(other); 171 int lim = Math.min(len1, len2); 172 for (int k = 0; k < lim; k++) { 173 char c1 = getChar(value, k); 174 char c2 = StringUTF16.getChar(other, k); 175 if (c1 != c2) { 176 c1 = Character.toUpperCase(c1); 177 c2 = Character.toUpperCase(c2); 178 if (c1 != c2) { 179 c1 = Character.toLowerCase(c1); 180 c2 = Character.toLowerCase(c2); 181 if (c1 != c2) { 182 return c1 - c2; 183 } 184 } 185 } 186 } 187 return len1 - len2; 188 } 189 190 public static int hashCode(byte[] value) { 191 int h = 0; 192 for (byte v : value) { 193 h = 31 * h + (v & 0xff); 194 } 195 return h; 196 } 197 198 public static int indexOf(byte[] value, int ch, int fromIndex) { 199 if (!canEncode(ch)) { 200 return -1; 201 } 202 int max = value.length; 203 if (fromIndex < 0) { 204 fromIndex = 0; 205 } else if (fromIndex >= max) { 206 // Note: fromIndex might be near -1>>>1. 207 return -1; 208 } 209 byte c = (byte)ch; 210 for (int i = fromIndex; i < max; i++) { 211 if (value[i] == c) { 212 return i; 213 } 214 } 215 return -1; 216 } 217 218 @HotSpotIntrinsicCandidate 219 public static int indexOf(byte[] value, byte[] str) { 220 if (str.length == 0) { 221 return 0; 222 } 223 if (value.length == 0) { 224 return -1; 225 } 226 return indexOf(value, value.length, str, str.length, 0); 227 } 228 229 @HotSpotIntrinsicCandidate 230 public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { 231 byte first = str[0]; 232 int max = (valueCount - strCount); 233 for (int i = fromIndex; i <= max; i++) { 234 // Look for first character. 235 if (value[i] != first) { 236 while (++i <= max && value[i] != first); 237 } 238 // Found first character, now look at the rest of value 239 if (i <= max) { 240 int j = i + 1; 241 int end = j + strCount - 1; 242 for (int k = 1; j < end && value[j] == str[k]; j++, k++); 243 if (j == end) { 244 // Found whole string. 245 return i; 246 } 247 } 248 } 249 return -1; 250 } 251 252 public static int lastIndexOf(byte[] src, int srcCount, 253 byte[] tgt, int tgtCount, int fromIndex) { 254 int min = tgtCount - 1; 255 int i = min + fromIndex; 256 int strLastIndex = tgtCount - 1; 257 char strLastChar = (char)(tgt[strLastIndex] & 0xff); 258 259 startSearchForLastChar: 260 while (true) { 261 while (i >= min && (src[i] & 0xff) != strLastChar) { 262 i--; 263 } 264 if (i < min) { 265 return -1; 266 } 267 int j = i - 1; 268 int start = j - strLastIndex; 269 int k = strLastIndex - 1; 270 while (j > start) { 271 if ((src[j--] & 0xff) != (tgt[k--] & 0xff)) { 272 i--; 273 continue startSearchForLastChar; 274 } 275 } 276 return start + 1; 277 } 278 } 279 280 public static int lastIndexOf(final byte[] value, int ch, int fromIndex) { 281 if (!canEncode(ch)) { 282 return -1; 283 } 284 int off = Math.min(fromIndex, value.length - 1); 285 for (; off >= 0; off--) { 286 if (value[off] == (byte)ch) { 287 return off; 288 } 289 } 290 return -1; 291 } 292 293 public static String replace(byte[] value, char oldChar, char newChar) { 294 if (canEncode(oldChar)) { 295 int len = value.length; 296 int i = -1; 297 while (++i < len) { 298 if (value[i] == (byte)oldChar) { 299 break; 300 } 301 } 302 if (i < len) { 303 if (canEncode(newChar)) { 304 byte buf[] = new byte[len]; 305 for (int j = 0; j < i; j++) { // TBD arraycopy? 306 buf[j] = value[j]; 307 } 308 while (i < len) { 309 byte c = value[i]; 310 buf[i] = (c == (byte)oldChar) ? (byte)newChar : c; 311 i++; 312 } 313 return new String(buf, LATIN1); 314 } else { 315 byte[] buf = StringUTF16.newBytesFor(len); 316 // inflate from latin1 to UTF16 317 inflate(value, 0, buf, 0, i); 318 while (i < len) { 319 char c = (char)(value[i] & 0xff); 320 StringUTF16.putChar(buf, i, (c == oldChar) ? newChar : c); 321 i++; 322 } 323 return new String(buf, UTF16); 324 } 325 } 326 } 327 return null; // for string to return this; 328 } 329 330 // case insensitive 331 public static boolean regionMatchesCI(byte[] value, int toffset, 332 byte[] other, int ooffset, int len) { 333 int last = toffset + len; 334 while (toffset < last) { 335 char c1 = (char)(value[toffset++] & 0xff); 336 char c2 = (char)(other[ooffset++] & 0xff); 337 if (c1 == c2) { 338 continue; 339 } 340 char u1 = Character.toUpperCase(c1); 341 char u2 = Character.toUpperCase(c2); 342 if (u1 == u2) { 343 continue; 344 } 345 if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { 346 continue; 347 } 348 return false; 349 } 350 return true; 351 } 352 353 public static boolean regionMatchesCI_UTF16(byte[] value, int toffset, 354 byte[] other, int ooffset, int len) { 355 int last = toffset + len; 356 while (toffset < last) { 357 char c1 = (char)(value[toffset++] & 0xff); 358 char c2 = StringUTF16.getChar(other, ooffset++); 359 if (c1 == c2) { 360 continue; 361 } 362 char u1 = Character.toUpperCase(c1); 363 char u2 = Character.toUpperCase(c2); 364 if (u1 == u2) { 365 continue; 366 } 367 if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { 368 continue; 369 } 370 return false; 371 } 372 return true; 373 } 374 375 public static String toLowerCase(String str, byte[] value, Locale locale) { 376 if (locale == null) { 377 throw new NullPointerException(); 378 } 379 int first; 380 final int len = value.length; 381 // Now check if there are any characters that need to be changed, or are surrogate 382 for (first = 0 ; first < len; first++) { 383 int cp = value[first] & 0xff; 384 if (cp != Character.toLowerCase(cp)) { // no need to check Character.ERROR 385 break; 386 } 387 } 388 if (first == len) 389 return str; 390 String lang = locale.getLanguage(); 391 if (lang == "tr" || lang == "az" || lang == "lt") { 392 return toLowerCaseEx(str, value, first, locale, true); 393 } 394 byte[] result = new byte[len]; 395 System.arraycopy(value, 0, result, 0, first); // Just copy the first few 396 // lowerCase characters. 397 for (int i = first; i < len; i++) { 398 int cp = value[i] & 0xff; 399 cp = Character.toLowerCase(cp); 400 if (!canEncode(cp)) { // not a latin1 character 401 return toLowerCaseEx(str, value, first, locale, false); 402 } 403 result[i] = (byte)cp; 404 } 405 return new String(result, LATIN1); 406 } 407 408 private static String toLowerCaseEx(String str, byte[] value, 409 int first, Locale locale, boolean localeDependent) 410 { 411 byte[] result = StringUTF16.newBytesFor(value.length); 412 int resultOffset = 0; 413 for (int i = 0; i < first; i++) { 414 StringUTF16.putChar(result, resultOffset++, value[i] & 0xff); 415 } 416 for (int i = first; i < value.length; i++) { 417 int srcChar = value[i] & 0xff; 418 int lowerChar; 419 char[] lowerCharArray; 420 if (localeDependent) { 421 lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale); 422 } else { 423 lowerChar = Character.toLowerCase(srcChar); 424 } 425 if (Character.isBmpCodePoint(lowerChar)) { // Character.ERROR is not a bmp 426 StringUTF16.putChar(result, resultOffset++, lowerChar); 427 } else { 428 if (lowerChar == Character.ERROR) { 429 lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale); 430 } else { 431 lowerCharArray = Character.toChars(lowerChar); 432 } 433 /* Grow result if needed */ 434 int mapLen = lowerCharArray.length; 435 if (mapLen > 1) { 436 byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1); 437 System.arraycopy(result, 0, result2, 0, resultOffset << 1); 438 result = result2; 439 } 440 for (int x = 0; x < mapLen; ++x) { 441 StringUTF16.putChar(result, resultOffset++, lowerCharArray[x]); 442 } 443 } 444 } 445 return StringUTF16.newString(result, 0, resultOffset); 446 } 447 448 public static String toUpperCase(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 455 // Now check if there are any characters that need to be changed, or are surrogate 456 for (first = 0 ; first < len; first++ ) { 457 int cp = value[first] & 0xff; 458 if (cp != Character.toUpperCaseEx(cp)) { // no need to check Character.ERROR 459 break; 460 } 461 } 462 if (first == len) { 463 return str; 464 } 465 String lang = locale.getLanguage(); 466 if (lang == "tr" || lang == "az" || lang == "lt") { 467 return toUpperCaseEx(str, value, first, locale, true); 468 } 469 byte[] result = new byte[len]; 470 System.arraycopy(value, 0, result, 0, first); // Just copy the first few 471 // upperCase characters. 472 for (int i = first; i < len; i++) { 473 int cp = value[i] & 0xff; 474 cp = Character.toUpperCaseEx(cp); 475 if (!canEncode(cp)) { // not a latin1 character 476 return toUpperCaseEx(str, value, first, locale, false); 477 } 478 result[i] = (byte)cp; 479 } 480 return new String(result, LATIN1); 481 } 482 483 private static String toUpperCaseEx(String str, byte[] value, 484 int first, Locale locale, boolean localeDependent) 485 { 486 byte[] result = StringUTF16.newBytesFor(value.length); 487 int resultOffset = 0; 488 for (int i = 0; i < first; i++) { 489 StringUTF16.putChar(result, resultOffset++, value[i] & 0xff); 490 } 491 for (int i = first; i < value.length; i++) { 492 int srcChar = value[i] & 0xff; 493 int upperChar; 494 char[] upperCharArray; 495 if (localeDependent) { 496 upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale); 497 } else { 498 upperChar = Character.toUpperCaseEx(srcChar); 499 } 500 if (Character.isBmpCodePoint(upperChar)) { 501 StringUTF16.putChar(result, resultOffset++, upperChar); 502 } else { 503 if (upperChar == Character.ERROR) { 504 if (localeDependent) { 505 upperCharArray = 506 ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale); 507 } else { 508 upperCharArray = Character.toUpperCaseCharArray(srcChar); 509 } 510 } else { 511 upperCharArray = Character.toChars(upperChar); 512 } 513 /* Grow result if needed */ 514 int mapLen = upperCharArray.length; 515 if (mapLen > 1) { 516 byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1); 517 System.arraycopy(result, 0, result2, 0, resultOffset << 1); 518 result = result2; 519 } 520 for (int x = 0; x < mapLen; ++x) { 521 StringUTF16.putChar(result, resultOffset++, upperCharArray[x]); 522 } 523 } 524 } 525 return StringUTF16.newString(result, 0, resultOffset); 526 } 527 528 public static String trim(byte[] value) { 529 int len = value.length; 530 int st = 0; 531 while ((st < len) && ((value[st] & 0xff) <= ' ')) { 532 st++; 533 } 534 while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) { 535 len--; 536 } 537 return ((st > 0) || (len < value.length)) ? 538 newString(value, st, len - st) : null; 539 } 540 541 public static void putChar(byte[] val, int index, int c) { 542 //assert (canEncode(c)); 543 val[index] = (byte)(c); 544 } 545 546 public static char getChar(byte[] val, int index) { 547 return (char)(val[index] & 0xff); 548 } 549 550 public static byte[] toBytes(int[] val, int off, int len) { 551 byte[] ret = new byte[len]; 552 for (int i = 0; i < len; i++) { 553 int cp = val[off++]; 554 if (!canEncode(cp)) { 555 return null; 556 } 557 ret[i] = (byte)cp; 558 } 559 return ret; 560 } 561 562 public static byte[] toBytes(char c) { 563 return new byte[] { (byte)c }; 564 } 565 566 public static String newString(byte[] val, int index, int len) { 567 return new String(Arrays.copyOfRange(val, index, index + len), 568 LATIN1); 569 } 570 571 public static void fillNull(byte[] val, int index, int end) { 572 Arrays.fill(val, index, end, (byte)0); 573 } 574 575 // inflatedCopy byte[] -> char[] 576 @HotSpotIntrinsicCandidate 577 public static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) { 578 for (int i = 0; i < len; i++) { 579 dst[dstOff++] = (char)(src[srcOff++] & 0xff); 580 } 581 } 582 583 // inflatedCopy byte[] -> byte[] 584 @HotSpotIntrinsicCandidate 585 public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { 586 StringUTF16.inflate(src, srcOff, dst, dstOff, len); 587 } 588 589 static class CharsSpliterator implements Spliterator.OfInt { 590 private final byte[] array; 591 private int index; // current index, modified on advance/split 592 private final int fence; // one past last index 593 private final int cs; 594 595 CharsSpliterator(byte[] array, int acs) { 596 this(array, 0, array.length, acs); 597 } 598 599 CharsSpliterator(byte[] array, int origin, int fence, int acs) { 600 this.array = array; 601 this.index = origin; 602 this.fence = fence; 603 this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED 604 | Spliterator.SUBSIZED; 605 } 606 607 @Override 608 public OfInt trySplit() { 609 int lo = index, mid = (lo + fence) >>> 1; 610 return (lo >= mid) 611 ? null 612 : new CharsSpliterator(array, lo, index = mid, cs); 613 } 614 615 @Override 616 public void forEachRemaining(IntConsumer action) { 617 byte[] a; int i, hi; // hoist accesses and checks from loop 618 if (action == null) 619 throw new NullPointerException(); 620 if ((a = array).length >= (hi = fence) && 621 (i = index) >= 0 && i < (index = hi)) { 622 do { action.accept(a[i] & 0xff); } while (++i < hi); 623 } 624 } 625 626 @Override 627 public boolean tryAdvance(IntConsumer action) { 628 if (action == null) 629 throw new NullPointerException(); 630 if (index >= 0 && index < fence) { 631 action.accept(array[index++] & 0xff); 632 return true; 633 } 634 return false; 635 } 636 637 @Override 638 public long estimateSize() { return (long)(fence - index); } 639 640 @Override 641 public int characteristics() { 642 return cs; 643 } 644 } 645 }