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