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 }