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