< prev index next >

src/java.base/share/classes/java/lang/StringUTF16.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -28,16 +28,18 @@
 import java.util.Arrays;
 import java.util.Locale;
 import java.util.Spliterator;
 import java.util.function.IntConsumer;
 import jdk.internal.HotSpotIntrinsicCandidate;
+import jdk.internal.vm.annotation.ForceInline;
+import jdk.internal.vm.annotation.DontInline;
 
 import static java.lang.String.UTF16;
 import static java.lang.String.LATIN1;
-import static java.lang.String.checkIndex;
-import static java.lang.String.checkOffset;
-import static java.lang.String.checkBoundsOffCount;
+//import static java.lang.String.checkIndex;
+//import static java.lang.String.checkOffset;
+//import static java.lang.String.checkBoundsOffCount;
 
 final class StringUTF16 {
 
     public static byte[] newBytesFor(int len) {
         if (len < 0) {

@@ -49,80 +51,108 @@
         }
         return new byte[len << 1];
     }
 
     @HotSpotIntrinsicCandidate
-    public static void putChar(byte[] val, int index, int c) {
+    // should be private but then javac generates an accessor method
+    // intrinsic performs no bounds checks
+    static void putCharUnsafe(byte[] val, int index, int c) {
         index <<= 1;
         val[index++] = (byte)(c >> HI_BYTE_SHIFT);
         val[index]   = (byte)(c >> LO_BYTE_SHIFT);
     }
 
     @HotSpotIntrinsicCandidate
-    public static char getChar(byte[] val, int index) {
+    // should be private but then javac generates an accessor method
+    // intrinsic performs no bounds checks
+    static char getCharUnsafe(byte[] val, int index) {
         index <<= 1;
         return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
                       ((val[index]   & 0xff) << LO_BYTE_SHIFT));
     }
 
-    public static char charAt(byte[] value, int index) {
-        if (index < 0 || index >= value.length >> 1) {
-            throw new StringIndexOutOfBoundsException(index);
-        }
-        return getChar(value, index);
-    }
-
     public static int length(byte[] value) {
         return value.length >> 1;
     }
 
-    public static int codePointAt(byte[] value, int index, int end) {
-        char c1 = getChar(value, index);
+    private static int codePointAt(byte[] value, int index, int end, boolean checked) {
+        assert index < end;
+        if (checked) {
+            checkIndex(index, value);
+        }
+        char c1 = Trusted.getChar(value, index);
         if (Character.isHighSurrogate(c1) && ++index < end) {
-            char c2 = getChar(value, index);
+            if (checked) {
+                checkIndex(index, value);
+            }
+            char c2 = Trusted.getChar(value, index);
             if (Character.isLowSurrogate(c2)) {
                return Character.toCodePoint(c1, c2);
             }
         }
         return c1;
     }
 
-    public static int codePointBefore(byte[] value, int index) {
-        char c2 = getChar(value, --index);
+    public static int codePointAt(byte[] value, int index, int end) {
+       return codePointAt(value, index, end, false /* unchecked */);
+    }
+
+    private static int codePointBefore(byte[] value, int index, boolean checked) {
+        --index;
+        if (checked) {
+            checkIndex(index, value);
+        }
+        char c2 = Trusted.getChar(value, index);
         if (Character.isLowSurrogate(c2) && index > 0) {
-            char c1 = getChar(value, --index);
+            --index;
+            if (checked) {
+                checkIndex(index, value);
+            }
+            char c1 = Trusted.getChar(value, index);
             if (Character.isHighSurrogate(c1)) {
                return Character.toCodePoint(c1, c2);
             }
         }
         return c2;
     }
 
-    public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
+    public static int codePointBefore(byte[] value, int index) {
+        return codePointBefore(value, index, false /* unchecked */);
+    }
+
+    private static int codePointCount(byte[] value, int beginIndex, int endIndex, boolean checked) {
+        assert beginIndex <= endIndex;
         int count = endIndex - beginIndex;
-        for (int i = beginIndex; i < endIndex; ) {
-            if (Character.isHighSurrogate(getChar(value, i++)) &&
-                i < endIndex &&
-                Character.isLowSurrogate(getChar(value, i))) {
+        int i = beginIndex;
+        if (checked && i < endIndex) {
+            checkBoundsBeginEnd(i, endIndex, value);
+        }
+        for (; i < endIndex - 1; ) {
+            if (Character.isHighSurrogate(Trusted.getChar(value, i++)) &&
+                Character.isLowSurrogate(Trusted.getChar(value, i))) {
                 count--;
                 i++;
             }
         }
         return count;
     }
 
+    public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
+        return codePointCount(value, beginIndex, endIndex, false /* unchecked */);
+    }
+
     public static char[] toChars(byte[] value) {
         char[] dst = new char[value.length >> 1];
         getChars(value, 0, dst.length, dst, 0);
         return dst;
     }
 
     @HotSpotIntrinsicCandidate
     public static byte[] toBytes(char[] value, int off, int len) {
         byte[] val = newBytesFor(len);
         for (int i = 0; i < len; i++) {
-            putChar(val, i, value[off]);
+            Trusted.putChar(val, i, value[off]);
             off++;
         }
         return val;
     }
 

@@ -160,13 +190,13 @@
 
     // compressedCopy byte[] -> byte[]
     @HotSpotIntrinsicCandidate
     public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
         // We need a range check here because 'getChar' has no checks
-        checkBoundsOffCount(srcOff << 1, len << 1, src.length);
+        checkBoundsOffCount(srcOff, len, src);
         for (int i = 0; i < len; i++) {
-            char c = getChar(src, srcOff);
+            char c = Trusted.getChar(src, srcOff);
             if (c > 0xFF) {
                 len = 0;
                 break;
             }
             dst[dstOff] = (byte)c;

@@ -191,33 +221,33 @@
         // Pass 2: Allocate and fill in <high, low> pair
         byte[] buf = newBytesFor(n);
         for (int i = index, j = 0; i < end; i++, j++) {
             int cp = val[i];
             if (Character.isBmpCodePoint(cp)) {
-                putChar(buf, j, cp);
+                Trusted.putChar(buf, j, cp);
             } else {
-                putChar(buf, j++, Character.highSurrogate(cp));
-                putChar(buf, j, Character.lowSurrogate(cp));
+                Trusted.putChar(buf, j++, Character.highSurrogate(cp));
+                Trusted.putChar(buf, j, Character.lowSurrogate(cp));
             }
         }
         return buf;
     }
 
     public static byte[] toBytes(char c) {
         byte[] result = new byte[2];
-        putChar(result, 0, c);
+        Trusted.putChar(result, 0, c);
         return result;
     }
 
     @HotSpotIntrinsicCandidate
     public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
         // We need a range check here because 'getChar' has no checks
         if (srcBegin < srcEnd) {
-            checkBoundsOffCount(srcBegin << 1, (srcEnd - srcBegin) << 1, value.length);
+            checkBoundsOffCount(srcBegin, srcEnd - srcBegin, value);
         }
         for (int i = srcBegin; i < srcEnd; i++) {
-            dst[dstBegin++] = getChar(value, i);
+            dst[dstBegin++] = Trusted.getChar(value, i);
         }
     }
 
     /* @see java.lang.String.getBytes(int, int, byte[], int) */
     public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) {

@@ -231,11 +261,11 @@
     @HotSpotIntrinsicCandidate
     public static boolean equals(byte[] value, byte[] other) {
         if (value.length == other.length) {
             int len = value.length >> 1;
             for (int i = 0; i < len; i++) {
-                if (getChar(value, i) != getChar(other, i)) {
+                if (Trusted.getChar(value, i) != Trusted.getChar(other, i)) {
                     return false;
                 }
             }
             return true;
         }

@@ -246,12 +276,12 @@
     public static int compareTo(byte[] value, byte[] other) {
         int len1 = length(value);
         int len2 = length(other);
         int lim = Math.min(len1, len2);
         for (int k = 0; k < lim; k++) {
-            char c1 = getChar(value, k);
-            char c2 = getChar(other, k);
+            char c1 = Trusted.getChar(value, k);
+            char c2 = Trusted.getChar(other, k);
             if (c1 != c2) {
                 return c1 - c2;
             }
         }
         return len1 - len2;

@@ -265,12 +295,12 @@
     public static int compareToCI(byte[] value, byte[] other) {
         int len1 = length(value);
         int len2 = length(other);
         int lim = Math.min(len1, len2);
         for (int k = 0; k < lim; k++) {
-            char c1 = getChar(value, k);
-            char c2 = getChar(other, k);
+            char c1 = Trusted.getChar(value, k);
+            char c2 = Trusted.getChar(other, k);
             if (c1 != c2) {
                 c1 = Character.toUpperCase(c1);
                 c2 = Character.toUpperCase(c2);
                 if (c1 != c2) {
                     c1 = Character.toLowerCase(c1);

@@ -290,11 +320,11 @@
 
     public static int hashCode(byte[] value) {
         int h = 0;
         int length = value.length >> 1;
         for (int i = 0; i < length; i++) {
-            h = 31 * h + getChar(value, i);
+            h = 31 * h + Trusted.getChar(value, i);
         }
         return h;
     }
 
     public static int indexOf(byte[] value, int ch, int fromIndex) {

@@ -317,68 +347,90 @@
     @HotSpotIntrinsicCandidate
     public static int indexOf(byte[] value, byte[] str) {
         if (str.length == 0) {
             return 0;
         }
-        if (value.length == 0) {
+        if (value.length < str.length) {
             return -1;
         }
-        return indexOf(value, length(value), str, length(str), 0);
+        return indexOfUnsafe(value, length(value), str, length(str), 0);
     }
 
     @HotSpotIntrinsicCandidate
     public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
-        char first = getChar(str, 0);
+        checkBoundsBeginEnd(fromIndex, valueCount, value);
+        checkBoundsBeginEnd(0, strCount, str);
+        return indexOfUnsafe(value, valueCount, str, strCount, fromIndex);
+    }
+
+
+    private static int indexOfUnsafe(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
+        assert fromIndex >= 0;
+        assert strCount > 0;
+        assert strCount <= length(str);
+        assert valueCount >= strCount;
+        char first = Trusted.getChar(str, 0);
         int max = (valueCount - strCount);
         for (int i = fromIndex; i <= max; i++) {
             // Look for first character.
-            if (getChar(value, i) != first) {
-                while (++i <= max && getChar(value, i) != first);
+            if (Trusted.getChar(value, i) != first) {
+                while (++i <= max && Trusted.getChar(value, i) != first);
             }
             // Found first character, now look at the rest of value
             if (i <= max) {
                 int j = i + 1;
                 int end = j + strCount - 1;
-                for (int k = 1; j < end && getChar(value, j) == getChar(str, k); j++, k++);
+                for (int k = 1; j < end && Trusted.getChar(value, j) == Trusted.getChar(str, k); j++, k++);
                 if (j == end) {
                     // Found whole string.
                     return i;
                 }
             }
         }
         return -1;
     }
 
+
     /**
      * Handles indexOf Latin1 substring in UTF16 string.
      */
     @HotSpotIntrinsicCandidate
     public static int indexOfLatin1(byte[] value, byte[] str) {
         if (str.length == 0) {
             return 0;
         }
-        if (value.length == 0) {
+        if (length(value) < str.length) {
             return -1;
         }
-        return indexOfLatin1(value, length(value), str, str.length, 0);
+        return indexOfLatin1Unsafe(value, length(value), str, str.length, 0);
     }
 
     @HotSpotIntrinsicCandidate
     public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) {
+        checkBoundsBeginEnd(fromIndex, srcCount, src);
+        String.checkBoundsBeginEnd(0, tgtCount, tgt.length);
+        return indexOfLatin1Unsafe(src, srcCount, tgt, tgtCount, fromIndex);
+    }
+
+    public static int indexOfLatin1Unsafe(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) {
+        assert fromIndex >= 0;
+        assert tgtCount > 0;
+        assert tgtCount <= tgt.length;
+        assert srcCount >= tgtCount;
         char first = (char)(tgt[0] & 0xff);
         int max = (srcCount - tgtCount);
         for (int i = fromIndex; i <= max; i++) {
             // Look for first character.
-            if (getChar(src, i) != first) {
-                while (++i <= max && getChar(src, i) != first);
+            if (Trusted.getChar(src, i) != first) {
+                while (++i <= max && Trusted.getChar(src, i) != first);
             }
             // Found first character, now look at the rest of v2
             if (i <= max) {
                 int j = i + 1;
                 int end = j + tgtCount - 1;
                 for (int k = 1;
-                     j < end && getChar(src, j) == (tgt[k] & 0xff);
+                     j < end && Trusted.getChar(src, j) == (tgt[k] & 0xff);
                      j++, k++);
                 if (j == end) {
                     // Found whole string.
                     return i;
                 }

@@ -387,12 +439,17 @@
         return -1;
     }
 
     @HotSpotIntrinsicCandidate
     private static int indexOfChar(byte[] value, int ch, int fromIndex, int max) {
+        checkBoundsBeginEnd(fromIndex, max, value);
+        return indexOfCharUnsafe(value, ch, fromIndex, max);
+    }
+
+    private static int indexOfCharUnsafe(byte[] value, int ch, int fromIndex, int max) {
         for (int i = fromIndex; i < max; i++) {
-            if (getChar(value, i) == ch) {
+            if (Trusted.getChar(value, i) == ch) {
                 return i;
             }
         }
         return -1;
     }

@@ -402,39 +459,48 @@
      */
     private static int indexOfSupplementary(byte[] value, int ch, int fromIndex, int max) {
         if (Character.isValidCodePoint(ch)) {
             final char hi = Character.highSurrogate(ch);
             final char lo = Character.lowSurrogate(ch);
+            checkBoundsBeginEnd(fromIndex, max, value);
             for (int i = fromIndex; i < max - 1; i++) {
-                if (getChar(value, i) == hi && getChar(value, i + 1 ) == lo) {
+                if (Trusted.getChar(value, i) == hi && Trusted.getChar(value, i + 1 ) == lo) {
                     return i;
                 }
             }
         }
         return -1;
     }
 
+    // srcCoder == UTF16 && tgtCoder == UTF16
     public static int lastIndexOf(byte[] src, int srcCount,
                                   byte[] tgt, int tgtCount, int fromIndex) {
+        assert fromIndex >= 0;
+        assert tgtCount > 0;
+        assert tgtCount <= length(tgt);
         int min = tgtCount - 1;
         int i = min + fromIndex;
         int strLastIndex = tgtCount - 1;
-        char strLastChar = getChar(tgt, strLastIndex);
+
+        checkIndex(strLastIndex, tgt);
+        char strLastChar = Trusted.getChar(tgt, strLastIndex);
+
+        checkIndex(i, src);
 
     startSearchForLastChar:
         while (true) {
-            while (i >= min && getChar(src, i) != strLastChar) {
+            while (i >= min && Trusted.getChar(src, i) != strLastChar) {
                 i--;
             }
             if (i < min) {
                 return -1;
             }
             int j = i - 1;
             int start = j - strLastIndex;
             int k = strLastIndex - 1;
             while (j > start) {
-                if (getChar(src, j--) != getChar(tgt, k--)) {
+                if (Trusted.getChar(src, j--) != Trusted.getChar(tgt, k--)) {
                     i--;
                     continue startSearchForLastChar;
                 }
             }
             return start + 1;

@@ -445,11 +511,11 @@
         if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
             // handle most cases here (ch is a BMP code point or a
             // negative value (invalid code point))
             int i = Math.min(fromIndex, (value.length >> 1) - 1);
             for (; i >= 0; i--) {
-                if (getChar(value, i) == ch) {
+                if (Trusted.getChar(value, i) == ch) {
                     return i;
                 }
             }
             return -1;
         } else {

@@ -464,11 +530,11 @@
         if (Character.isValidCodePoint(ch)) {
             char hi = Character.highSurrogate(ch);
             char lo = Character.lowSurrogate(ch);
             int i = Math.min(fromIndex, (value.length >> 1) - 2);
             for (; i >= 0; i--) {
-                if (getChar(value, i) == hi && getChar(value, i + 1) == lo) {
+                if (Trusted.getChar(value, i) == hi && Trusted.getChar(value, i + 1) == lo) {
                     return i;
                 }
             }
         }
         return -1;

@@ -476,22 +542,22 @@
 
     public static String replace(byte[] value, char oldChar, char newChar) {
         int len = value.length >> 1;
         int i = -1;
         while (++i < len) {
-            if (getChar(value, i) == oldChar) {
+            if (Trusted.getChar(value, i) == oldChar) {
                 break;
             }
         }
         if (i < len) {
             byte buf[] = new byte[value.length];
             for (int j = 0; j < i; j++) {
-                putChar(buf, j, getChar(value, j)); // TBD:arraycopy?
+                Trusted.putChar(buf, j, Trusted.getChar(value, j)); // TBD:arraycopy?
             }
             while (i < len) {
-                char c = getChar(value, i);
-                putChar(buf, i, c == oldChar ? newChar : c);
+                char c = Trusted.getChar(value, i);
+                Trusted.putChar(buf, i, c == oldChar ? newChar : c);
                 i++;
            }
            // Check if we should try to compress to latin1
            if (String.COMPACT_STRINGS &&
                !StringLatin1.canEncode(oldChar) &&

@@ -507,13 +573,16 @@
     }
 
     public static boolean regionMatchesCI(byte[] value, int toffset,
                                           byte[] other, int ooffset, int len) {
         int last = toffset + len;
+        assert toffset >= 0 && ooffset >= 0;
+        assert ooffset + len <= length(other);
+        assert last <= length(value);
         while (toffset < last) {
-            char c1 = getChar(value, toffset++);
-            char c2 = getChar(other, ooffset++);
+            char c1 = Trusted.getChar(value, toffset++);
+            char c2 = Trusted.getChar(other, ooffset++);
             if (c1 == c2) {
                 continue;
             }
             // try converting both characters to uppercase.
             // If the results match, then the comparison scan should

@@ -549,11 +618,11 @@
         boolean hasSurr = false;
         final int len = value.length >> 1;
 
         // Now check if there are any characters that need to be changed, or are surrogate
         for (first = 0 ; first < len; first++) {
-            int cp = (int)getChar(value, first);
+            int cp = (int)Trusted.getChar(value, first);
             if (Character.isSurrogate((char)cp)) {
                 hasSurr = true;
                 break;
             }
             if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR

@@ -572,11 +641,11 @@
         if (hasSurr) {
             return toLowerCaseEx(str, value, result, first, locale, false);
         }
         int bits = 0;
         for (int i = first; i < len; i++) {
-            int cp = (int)getChar(value, i);
+            int cp = (int)Trusted.getChar(value, i);
             if (cp == '\u03A3' ||                       // GREEK CAPITAL LETTER SIGMA
                 Character.isSurrogate((char)cp)) {
                 return toLowerCaseEx(str, value, result, i, locale, false);
             }
             if (cp == '\u0130') {                       // LATIN CAPITAL LETTER I WITH DOT ABOVE

@@ -585,11 +654,11 @@
             cp = Character.toLowerCase(cp);
             if (!Character.isBmpCodePoint(cp)) {
                 return toLowerCaseEx(str, value, result, i, locale, false);
             }
             bits |= cp;
-            putChar(result, i, cp);
+            Trusted.putChar(result, i, cp);
         }
         if (bits > 0xFF) {
             return new String(result, UTF16);
         } else {
             return newString(result, 0, len);

@@ -597,15 +666,17 @@
     }
 
     private static String toLowerCaseEx(String str, byte[] value,
                                         byte[] result, int first, Locale locale,
                                         boolean localeDependent) {
+        assert(result.length == value.length);
+        assert(first >= 0);
         int resultOffset = first;
         int length = value.length >> 1;
         int srcCount;
         for (int i = first; i < length; i += srcCount) {
-            int srcChar = getChar(value, i);
+            int srcChar = Trusted.getChar(value, i);
             int lowerChar;
             char[] lowerCharArray;
             srcCount = 1;
             if (Character.isSurrogate((char)srcChar)) {
                 srcChar = codePointAt(value, i, length);

@@ -617,11 +688,11 @@
                 lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale);
             } else {
                 lowerChar = Character.toLowerCase(srcChar);
             }
             if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
-                putChar(result, resultOffset++, lowerChar);
+                Trusted.putChar(result, resultOffset++, lowerChar);
             } else {
                 if (lowerChar == Character.ERROR) {
                     lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale);
                 } else {
                     lowerCharArray = Character.toChars(lowerChar);

@@ -631,12 +702,14 @@
                 if (mapLen > srcCount) {
                     byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount);
                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
                     result = result2;
                 }
+                assert resultOffset >= 0;
+                assert resultOffset + mapLen <= length(result);
                 for (int x = 0; x < mapLen; ++x) {
-                    putChar(result, resultOffset++, lowerCharArray[x]);
+                    Trusted.putChar(result, resultOffset++, lowerCharArray[x]);
                 }
             }
         }
         return newString(result, 0, resultOffset);
     }

@@ -649,11 +722,11 @@
         boolean hasSurr = false;
         final int len = value.length >> 1;
 
         // Now check if there are any characters that need to be changed, or are surrogate
         for (first = 0 ; first < len; first++) {
-            int cp = (int)getChar(value, first);
+            int cp = (int)Trusted.getChar(value, first);
             if (Character.isSurrogate((char)cp)) {
                 hasSurr = true;
                 break;
             }
             if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR

@@ -673,20 +746,20 @@
         if (hasSurr) {
             return toUpperCaseEx(str, value, result, first, locale, false);
         }
         int bits = 0;
         for (int i = first; i < len; i++) {
-            int cp = (int)getChar(value, i);
+            int cp = (int)Trusted.getChar(value, i);
             if (Character.isSurrogate((char)cp)) {
                 return toUpperCaseEx(str, value, result, i, locale, false);
             }
             cp = Character.toUpperCaseEx(cp);
             if (!Character.isBmpCodePoint(cp)) {    // Character.ERROR is not bmp
                 return toUpperCaseEx(str, value, result, i, locale, false);
             }
             bits |= cp;
-            putChar(result, i, cp);
+            Trusted.putChar(result, i, cp);
         }
         if (bits > 0xFF) {
             return new String(result, UTF16);
         } else {
             return newString(result, 0, len);

@@ -695,15 +768,17 @@
 
     private static String toUpperCaseEx(String str, byte[] value,
                                         byte[] result, int first,
                                         Locale locale, boolean localeDependent)
     {
+        assert(result.length == value.length);
+        assert(first >= 0);
         int resultOffset = first;
         int length = value.length >> 1;
         int srcCount;
         for (int i = first; i < length; i += srcCount) {
-            int srcChar = getChar(value, i);
+            int srcChar = Trusted.getChar(value, i);
             int upperChar;
             char[] upperCharArray;
             srcCount = 1;
             if (Character.isSurrogate((char)srcChar)) {
                 srcChar = codePointAt(value, i, length);

@@ -713,11 +788,11 @@
                 upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale);
             } else {
                 upperChar = Character.toUpperCaseEx(srcChar);
             }
             if (Character.isBmpCodePoint(upperChar)) {
-                putChar(result, resultOffset++, upperChar);
+                Trusted.putChar(result, resultOffset++, upperChar);
             } else {
                 if (upperChar == Character.ERROR) {
                     if (localeDependent) {
                         upperCharArray =
                             ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale);

@@ -732,36 +807,38 @@
                 if (mapLen > srcCount) {
                     byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount);
                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
                     result = result2;
                  }
+                assert resultOffset >= 0;
+                assert resultOffset + mapLen <= length(result);
                  for (int x = 0; x < mapLen; ++x) {
-                    putChar(result, resultOffset++, upperCharArray[x]);
+                    Trusted.putChar(result, resultOffset++, upperCharArray[x]);
                  }
             }
         }
         return newString(result, 0, resultOffset);
     }
 
     public static String trim(byte[] value) {
         int length = value.length >> 1;
         int len = length;
         int st = 0;
-        while (st < len && getChar(value, st) <= ' ') {
+        while (st < len && Trusted.getChar(value, st) <= ' ') {
             st++;
         }
-        while (st < len && getChar(value, len - 1) <= ' ') {
+        while (st < len && Trusted.getChar(value, len - 1) <= ' ') {
             len--;
         }
         return ((st > 0) || (len < length )) ?
             new String(Arrays.copyOfRange(value, st << 1, len << 1), UTF16) :
             null;
     }
 
-    public static void putChars(byte[] val, int index, char[] str, int off, int end) {
+    private static void putChars(byte[] val, int index, char[] str, int off, int end) {
         while (off < end) {
-            putChar(val, index++, str[off++]);
+            Trusted.putChar(val, index++, str[off++]);
         }
     }
 
     public static String newString(byte[] val, int index, int len) {
         if (String.COMPACT_STRINGS) {

@@ -925,39 +1002,176 @@
     }
 
     ////////////////////////////////////////////////////////////////
 
     public static void putCharSB(byte[] val, int index, int c) {
-        checkIndex(index, val.length >> 1);
-        putChar(val, index, c);
+        checkIndex(index, val);
+        Trusted.putChar(val, index, c);
     }
 
     public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) {
-        checkOffset(index + end - off, val.length >> 1);
+        checkBoundsBeginEnd(index, index + end - off, val);
         putChars(val, index, ca, off, end);
     }
 
     public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) {
-        checkOffset(index + end - off, val.length >> 1);
+        checkBoundsBeginEnd(index, index + end - off, val);
         for (int i = off; i < end; i++) {
-            putChar(val, index++, s.charAt(i));
+            Trusted.putChar(val, index++, s.charAt(i));
         }
     }
 
     public static int codePointAtSB(byte[] val, int index, int end) {
-        checkOffset(end, val.length >> 1);
-        return codePointAt(val, index, end);
+        return codePointAt(val, index, end, true /* checked */);
     }
 
     public static int codePointBeforeSB(byte[] val, int index) {
-        checkOffset(index, val.length >> 1);
-        return codePointBefore(val, index);
+        return codePointBefore(val, index, true /* checked */);
     }
 
     public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) {
-        checkOffset(endIndex, val.length >> 1);
-        return codePointCount(val, beginIndex, endIndex);
+        return codePointCount(val, beginIndex, endIndex, true /* checked */);
+    }
+
+    public static int getChars(int i, int begin, int end, byte[] value) {
+        checkBoundsBeginEnd(begin, end, value);
+        int pos = Trusted.getChars(i, end, value);
+        assert begin == pos;
+        return pos;
+    }
+
+    public static int getChars(long l, int begin, int end, byte[] value) {
+        checkBoundsBeginEnd(begin, end, value);
+        int pos = Trusted.getChars(l, end, value);
+        assert begin == pos;
+        return pos;
+    }
+
+    public static boolean contentEquals(byte[] v1, byte[] v2, int len) {
+        checkBoundsOffCount(0, len, v2);
+        for (int i = 0; i < len; i++) {
+            if ((char)(v1[i] & 0xff) != Trusted.getChar(v2, i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static boolean contentEquals(byte[] value, CharSequence cs, int len) {
+        checkOffset(len, value);
+        for (int i = 0; i < len; i++) {
+            if (Trusted.getChar(value, i) != cs.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) {
+        int end = i + 4;
+        checkBoundsBeginEnd(i, end, value);
+        Trusted.putChar(value, i++, c1);
+        Trusted.putChar(value, i++, c2);
+        Trusted.putChar(value, i++, c3);
+        Trusted.putChar(value, i++, c4);
+        assert(i == end);
+        return end;
+    }
+
+    public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) {
+        int end = i + 5;
+        checkBoundsBeginEnd(i, end, value);
+        Trusted.putChar(value, i++, c1);
+        Trusted.putChar(value, i++, c2);
+        Trusted.putChar(value, i++, c3);
+        Trusted.putChar(value, i++, c4);
+        Trusted.putChar(value, i++, c5);
+        assert(i == end);
+        return end;
+    }
+
+    public static char charAt(byte[] value, int index) {
+        checkIndex(index, value);
+        return Trusted.getChar(value, index);
+    }
+
+    public static void reverse(byte[] val, int count) {
+        checkOffset(count, val);
+        int n = count - 1;
+        boolean hasSurrogates = false;
+        for (int j = (n-1) >> 1; j >= 0; j--) {
+            int k = n - j;
+            char cj = Trusted.getChar(val, j);
+            char ck = Trusted.getChar(val, k);
+            Trusted.putChar(val, j, ck);
+            Trusted.putChar(val, k, cj);
+            if (Character.isSurrogate(cj) ||
+                Character.isSurrogate(ck)) {
+                hasSurrogates = true;
+            }
+        }
+        if (hasSurrogates) {
+            reverseAllValidSurrogatePairs(val, count);
+        }
+    }
+
+    /** Outlined helper method for reverse() */
+    private static void reverseAllValidSurrogatePairs(byte[] val, int count) {
+        for (int i = 0; i < count - 1; i++) {
+            char c2 = Trusted.getChar(val, i);
+            if (Character.isLowSurrogate(c2)) {
+                char c1 = Trusted.getChar(val, i + 1);
+                if (Character.isHighSurrogate(c1)) {
+                    Trusted.putChar(val, i++, c1);
+                    Trusted.putChar(val, i, c2);
+                }
+            }
+        }
+    }
+
+    // inflatedCopy byte[] -> byte[]
+    public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+        // We need a range check here because 'putChar' has no checks
+        checkBoundsOffCount(dstOff, len, dst);
+        for (int i = 0; i < len; i++) {
+            Trusted.putChar(dst, dstOff++, src[srcOff++] & 0xff);
+        }
+    }
+
+    // srcCoder == UTF16 && tgtCoder == LATIN1
+    public static int lastIndexOfLatin1(byte[] src, int srcCount,
+                                        byte[] tgt, int tgtCount, int fromIndex) {
+        assert fromIndex >= 0;
+        assert tgtCount > 0;
+        assert tgtCount <= tgt.length;
+        int min = tgtCount - 1;
+        int i = min + fromIndex;
+        int strLastIndex = tgtCount - 1;
+
+        char strLastChar = (char)(tgt[strLastIndex] & 0xff);
+
+        checkIndex(i, src);
+
+    startSearchForLastChar:
+        while (true) {
+            while (i >= min && Trusted.getChar(src, i) != strLastChar) {
+                i--;
+            }
+            if (i < min) {
+                return -1;
+            }
+            int j = i - 1;
+            int start = j - strLastIndex;
+            int k = strLastIndex - 1;
+            while (j > start) {
+                if (Trusted.getChar(src, j--) != (tgt[k--] & 0xff)) {
+                    i--;
+                    continue startSearchForLastChar;
+                }
+            }
+            return start + 1;
+        }
     }
 
     ////////////////////////////////////////////////////////////////
 
     private static native boolean isBigEndian();

@@ -973,6 +1187,164 @@
             LO_BYTE_SHIFT = 8;
         }
     }
 
     static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
+
+    @DontInline
+    static char throwAssertionError(Throwable e) {
+        throw new AssertionError("Trusted caller missed bounds check", e);
+    }
+
+    @DontInline
+    static AssertionError assertionError(Throwable e) {
+        return new AssertionError("Trusted caller missed bounds check", e);
+    }
+
+    // Used by trusted callers.  Assumes all necessary bounds checks have
+    // been done by the caller.
+    static class Trusted {
+
+        @ForceInline
+        static char getChar(byte[] val, int index) {
+            assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
+            if (!String.DEBUG_INTRINSICS) {
+                return getCharUnsafe(val, index);
+            }
+            try {
+                return getCharUnsafe(val, index);
+            } catch (IndexOutOfBoundsException e) {
+                return throwAssertionError(e);
+            }
+        }
+
+        @ForceInline
+        static void putChar(byte[] val, int index, int c) {
+            assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
+            if (!String.DEBUG_INTRINSICS) {
+                putCharUnsafe(val, index, c);
+                return;
+            }
+            try {
+                putCharUnsafe(val, index, c);
+            } catch (IndexOutOfBoundsException e) {
+                throwAssertionError(e);
+            }
+        }
+
+        /**
+         * This is a variant of {@link Integer#getChars(int, int, byte[])}, but for
+         * UTF-16 coder.
+         *
+         * @param i     value to convert
+         * @param index next index, after the least significant digit
+         * @param buf   target buffer, UTF16-coded.
+         * @return index of the most significant digit or minus sign, if present
+         */
+        static int getChars(int i, int index, byte[] buf) {
+            int q, r;
+            int charPos = index;
+
+            boolean negative = (i < 0);
+            if (!negative) {
+                i = -i;
+            }
+
+            // Get 2 digits/iteration using ints
+            while (i <= -100) {
+                q = i / 100;
+                r = (q * 100) - i;
+                i = q;
+                Trusted.putChar(buf, --charPos, Integer.DigitOnes[r]);
+                Trusted.putChar(buf, --charPos, Integer.DigitTens[r]);
+            }
+
+            // We know there are at most two digits left at this point.
+            q = i / 10;
+            r = (q * 10) - i;
+            Trusted.putChar(buf, --charPos, '0' + r);
+
+            // Whatever left is the remaining digit.
+            if (q < 0) {
+                Trusted.putChar(buf, --charPos, '0' - q);
+            }
+
+            if (negative) {
+                Trusted.putChar(buf, --charPos, '-');
+            }
+            return charPos;
+        }
+
+        /**
+         * This is a variant of {@link Long#getChars(long, int, byte[])}, but for
+         * UTF-16 coder.
+         *
+         * @param i     value to convert
+         * @param index next index, after the least significant digit
+         * @param buf   target buffer, UTF16-coded.
+         * @return index of the most significant digit or minus sign, if present
+         */
+        static int getChars(long i, int index, byte[] buf) {
+            long q;
+            int r;
+            int charPos = index;
+
+            boolean negative = (i < 0);
+            if (!negative) {
+                i = -i;
+            }
+
+            // Get 2 digits/iteration using longs until quotient fits into an int
+            while (i <= Integer.MIN_VALUE) {
+                q = i / 100;
+                r = (int)((q * 100) - i);
+                i = q;
+                Trusted.putChar(buf, --charPos, Integer.DigitOnes[r]);
+                Trusted.putChar(buf, --charPos, Integer.DigitTens[r]);
+            }
+
+            // Get 2 digits/iteration using ints
+            int q2;
+            int i2 = (int)i;
+            while (i2 <= -100) {
+                q2 = i2 / 100;
+                r  = (q2 * 100) - i2;
+                i2 = q2;
+                Trusted.putChar(buf, --charPos, Integer.DigitOnes[r]);
+                Trusted.putChar(buf, --charPos, Integer.DigitTens[r]);
+            }
+
+            // We know there are at most two digits left at this point.
+            q2 = i2 / 10;
+            r  = (q2 * 10) - i2;
+            Trusted.putChar(buf, --charPos, '0' + r);
+
+            // Whatever left is the remaining digit.
+            if (q2 < 0) {
+                Trusted.putChar(buf, --charPos, '0' - q2);
+            }
+
+            if (negative) {
+                Trusted.putChar(buf, --charPos, '-');
+            }
+            return charPos;
+        }
+
+    }
+
+    public static void checkIndex(int off, byte[] val) {
+        String.checkIndex(off, length(val));
+    }
+
+    public static void checkOffset(int off, byte[] val) {
+        String.checkOffset(off, length(val));
+    }
+
+    public static void checkBoundsBeginEnd(int begin, int end, byte[] val) {
+        String.checkBoundsBeginEnd(begin, end, length(val));
+    }
+
+    public static void checkBoundsOffCount(int offset, int count, byte[] val) {
+        String.checkBoundsOffCount(offset, count, length(val));
+    }
+
 }
< prev index next >