1 /*
2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.lang;
27 
28 import java.util.Arrays;
29 import java.util.Locale;
30 import java.util.Objects;
31 import java.util.Spliterator;
32 import java.util.function.Consumer;
33 import java.util.function.IntConsumer;
34 import java.util.stream.IntStream;
35 import java.util.stream.Stream;
36 import java.util.stream.StreamSupport;
37 import jdk.internal.HotSpotIntrinsicCandidate;
38 
39 import static java.lang.String.LATIN1;
40 import static java.lang.String.UTF16;
41 import static java.lang.String.checkOffset;
42 
43 final class StringLatin1 {
44 
charAt(byte[] value, int index)45     public static char charAt(byte[] value, int index) {
46         if (index < 0 || index >= value.length) {
47             throw new StringIndexOutOfBoundsException(index);
48         }
49         return (char)(value[index] & 0xff);
50     }
51 
canEncode(int cp)52     public static boolean canEncode(int cp) {
53         return cp >>> 8 == 0;
54     }
55 
length(byte[] value)56     public static int length(byte[] value) {
57         return value.length;
58     }
59 
codePointAt(byte[] value, int index, int end)60     public static int codePointAt(byte[] value, int index, int end) {
61         return value[index] & 0xff;
62     }
63 
codePointBefore(byte[] value, int index)64     public static int codePointBefore(byte[] value, int index) {
65         return value[index - 1] & 0xff;
66     }
67 
codePointCount(byte[] value, int beginIndex, int endIndex)68     public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
69         return endIndex - beginIndex;
70     }
71 
toChars(byte[] value)72     public static char[] toChars(byte[] value) {
73         char[] dst = new char[value.length];
74         inflate(value, 0, dst, 0, value.length);
75         return dst;
76     }
77 
inflate(byte[] value, int off, int len)78     public static byte[] inflate(byte[] value, int off, int len) {
79         byte[] ret = StringUTF16.newBytesFor(len);
80         inflate(value, off, ret, 0, len);
81         return ret;
82     }
83 
getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin)84     public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
85         inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
86     }
87 
getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin)88     public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) {
89         System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
90     }
91 
92     @HotSpotIntrinsicCandidate
equals(byte[] value, byte[] other)93     public static boolean equals(byte[] value, byte[] other) {
94         if (value.length == other.length) {
95             for (int i = 0; i < value.length; i++) {
96                 if (value[i] != other[i]) {
97                     return false;
98                 }
99             }
100             return true;
101         }
102         return false;
103     }
104 
105     @HotSpotIntrinsicCandidate
compareTo(byte[] value, byte[] other)106     public static int compareTo(byte[] value, byte[] other) {
107         int len1 = value.length;
108         int len2 = other.length;
109         return compareTo(value, other, len1, len2);
110     }
111 
compareTo(byte[] value, byte[] other, int len1, int len2)112     public static int compareTo(byte[] value, byte[] other, int len1, int len2) {
113         int lim = Math.min(len1, len2);
114         for (int k = 0; k < lim; k++) {
115             if (value[k] != other[k]) {
116                 return getChar(value, k) - getChar(other, k);
117             }
118         }
119         return len1 - len2;
120     }
121 
122     @HotSpotIntrinsicCandidate
compareToUTF16(byte[] value, byte[] other)123     public static int compareToUTF16(byte[] value, byte[] other) {
124         int len1 = length(value);
125         int len2 = StringUTF16.length(other);
126         return compareToUTF16Values(value, other, len1, len2);
127     }
128 
129     /*
130      * Checks the boundary and then compares the byte arrays.
131      */
compareToUTF16(byte[] value, byte[] other, int len1, int len2)132     public static int compareToUTF16(byte[] value, byte[] other, int len1, int len2) {
133         checkOffset(len1, length(value));
134         checkOffset(len2, StringUTF16.length(other));
135 
136         return compareToUTF16Values(value, other, len1, len2);
137     }
138 
compareToUTF16Values(byte[] value, byte[] other, int len1, int len2)139     private static int compareToUTF16Values(byte[] value, byte[] other, int len1, int len2) {
140         int lim = Math.min(len1, len2);
141         for (int k = 0; k < lim; k++) {
142             char c1 = getChar(value, k);
143             char c2 = StringUTF16.getChar(other, k);
144             if (c1 != c2) {
145                 return c1 - c2;
146             }
147         }
148         return len1 - len2;
149     }
150 
compareToCI(byte[] value, byte[] other)151     public static int compareToCI(byte[] value, byte[] other) {
152         int len1 = value.length;
153         int len2 = other.length;
154         int lim = Math.min(len1, len2);
155         for (int k = 0; k < lim; k++) {
156             if (value[k] != other[k]) {
157                 // Android-changed: libcore doesn't have CharacterDataLatin1.
158                 // char c1 = (char) CharacterDataLatin1.instance.toUpperCase(getChar(value, k));
159                 // char c2 = (char) CharacterDataLatin1.instance.toUpperCase(getChar(other, k));
160                 char c1 = (char) Character.toUpperCase(getChar(value, k));
161                 char c2 = (char) Character.toUpperCase(getChar(other, k));
162                 if (c1 != c2) {
163                     c1 = Character.toLowerCase(c1);
164                     c2 = Character.toLowerCase(c2);
165                     if (c1 != c2) {
166                         return c1 - c2;
167                     }
168                 }
169             }
170         }
171         return len1 - len2;
172     }
173 
compareToCI_UTF16(byte[] value, byte[] other)174     public static int compareToCI_UTF16(byte[] value, byte[] other) {
175         int len1 = length(value);
176         int len2 = StringUTF16.length(other);
177         int lim = Math.min(len1, len2);
178         for (int k = 0; k < lim; k++) {
179             char c1 = getChar(value, k);
180             char c2 = StringUTF16.getChar(other, k);
181             if (c1 != c2) {
182                 c1 = Character.toUpperCase(c1);
183                 c2 = Character.toUpperCase(c2);
184                 if (c1 != c2) {
185                     c1 = Character.toLowerCase(c1);
186                     c2 = Character.toLowerCase(c2);
187                     if (c1 != c2) {
188                         return c1 - c2;
189                     }
190                 }
191             }
192         }
193         return len1 - len2;
194     }
195 
hashCode(byte[] value)196     public static int hashCode(byte[] value) {
197         int h = 0;
198         for (byte v : value) {
199             h = 31 * h + (v & 0xff);
200         }
201         return h;
202     }
203 
indexOf(byte[] value, int ch, int fromIndex)204     public static int indexOf(byte[] value, int ch, int fromIndex) {
205         if (!canEncode(ch)) {
206             return -1;
207         }
208         int max = value.length;
209         if (fromIndex < 0) {
210             fromIndex = 0;
211         } else if (fromIndex >= max) {
212             // Note: fromIndex might be near -1>>>1.
213             return -1;
214         }
215         byte c = (byte)ch;
216         for (int i = fromIndex; i < max; i++) {
217             if (value[i] == c) {
218                return i;
219             }
220         }
221         return -1;
222     }
223 
224     // Android-removed: Remove unused code.
225     /*
226     @HotSpotIntrinsicCandidate
227     public static int indexOf(byte[] value, byte[] str) {
228         if (str.length == 0) {
229             return 0;
230         }
231         if (value.length == 0) {
232             return -1;
233         }
234         return indexOf(value, value.length, str, str.length, 0);
235     }
236     */
237 
238     @HotSpotIntrinsicCandidate
239     // Android-changed: libcore doesn't store String as Latin1 or UTF16 byte[] field.
240     // public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
indexOf(byte[] value, int valueCount, String str, int strCount, int fromIndex)241     public static int indexOf(byte[] value, int valueCount, String str, int strCount, int fromIndex) {
242         // byte first = str[0];
243         byte first = (byte) str.charAt(0);
244         int max = (valueCount - strCount);
245         for (int i = fromIndex; i <= max; i++) {
246             // Look for first character.
247             if (value[i] != first) {
248                 while (++i <= max && value[i] != first);
249             }
250             // Found first character, now look at the rest of value
251             if (i <= max) {
252                 int j = i + 1;
253                 int end = j + strCount - 1;
254                 // Android-changed: Use charAt() because libcore doesn't store byte[] in java level.
255                 // for (int k = 1; j < end && value[j] == str[k]; j++, k++);
256                 for (int k = 1; j < end && value[j] == ((byte) str.charAt(k)); j++, k++);
257                 if (j == end) {
258                     // Found whole string.
259                     return i;
260                 }
261             }
262         }
263         return -1;
264     }
265 
lastIndexOf(byte[] src, int srcCount, String tgt, int tgtCount, int fromIndex)266     public static int lastIndexOf(byte[] src, int srcCount,
267                                   // Android-changed: String has no byte[] field in libcore.
268                                   // byte[] tgt, int tgtCount, int fromIndex) {
269                                   String tgt, int tgtCount, int fromIndex) {
270         int min = tgtCount - 1;
271         int i = min + fromIndex;
272         int strLastIndex = tgtCount - 1;
273         // char strLastChar = (char)(tgt[strLastIndex] & 0xff);
274         char strLastChar = tgt.charAt(strLastIndex);
275 
276   startSearchForLastChar:
277         while (true) {
278             while (i >= min && (src[i] & 0xff) != strLastChar) {
279                 i--;
280             }
281             if (i < min) {
282                 return -1;
283             }
284             int j = i - 1;
285             int start = j - strLastIndex;
286             int k = strLastIndex - 1;
287             while (j > start) {
288                 // Android-changed: Use charAt() because libcore doesn't store byte[] in java level.
289                 // if ((src[j--] & 0xff) != (tgt[k--] & 0xff)) {
290                 if ((src[j--] & 0xff) != (tgt.charAt(k--) & 0xff)) {
291                     i--;
292                     continue startSearchForLastChar;
293                 }
294             }
295             return start + 1;
296         }
297     }
298 
lastIndexOf(final byte[] value, int ch, int fromIndex)299     public static int lastIndexOf(final byte[] value, int ch, int fromIndex) {
300         if (!canEncode(ch)) {
301             return -1;
302         }
303         int off  = Math.min(fromIndex, value.length - 1);
304         for (; off >= 0; off--) {
305             if (value[off] == (byte)ch) {
306                 return off;
307             }
308         }
309         return -1;
310     }
311 
312     // BEGIN Android-removed: Remove unused code.
313     /*
314     public static String replace(byte[] value, char oldChar, char newChar) {
315         if (canEncode(oldChar)) {
316             int len = value.length;
317             int i = -1;
318             while (++i < len) {
319                 if (value[i] == (byte)oldChar) {
320                     break;
321                 }
322             }
323             if (i < len) {
324                 if (canEncode(newChar)) {
325                     byte buf[] = new byte[len];
326                     for (int j = 0; j < i; j++) {    // TBD arraycopy?
327                         buf[j] = value[j];
328                     }
329                     while (i < len) {
330                         byte c = value[i];
331                         buf[i] = (c == (byte)oldChar) ? (byte)newChar : c;
332                         i++;
333                     }
334                     return new String(buf, LATIN1);
335                 } else {
336                     byte[] buf = StringUTF16.newBytesFor(len);
337                     // inflate from latin1 to UTF16
338                     inflate(value, 0, buf, 0, i);
339                     while (i < len) {
340                         char c = (char)(value[i] & 0xff);
341                         StringUTF16.putChar(buf, i, (c == oldChar) ? newChar : c);
342                         i++;
343                     }
344                     return new String(buf, UTF16);
345                 }
346             }
347         }
348         return null; // for string to return this;
349     }
350 
351     // case insensitive
352     public static boolean regionMatchesCI(byte[] value, int toffset,
353                                           byte[] other, int ooffset, int len) {
354         int last = toffset + len;
355         while (toffset < last) {
356             char c1 = (char)(value[toffset++] & 0xff);
357             char c2 = (char)(other[ooffset++] & 0xff);
358             if (c1 == c2) {
359                 continue;
360             }
361             char u1 = Character.toUpperCase(c1);
362             char u2 = Character.toUpperCase(c2);
363             if (u1 == u2) {
364                 continue;
365             }
366             if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
367                 continue;
368             }
369             return false;
370         }
371         return true;
372     }
373 
374     public static boolean regionMatchesCI_UTF16(byte[] value, int toffset,
375                                                 byte[] other, int ooffset, int len) {
376         int last = toffset + len;
377         while (toffset < last) {
378             char c1 = (char)(value[toffset++] & 0xff);
379             char c2 = StringUTF16.getChar(other, ooffset++);
380             if (c1 == c2) {
381                 continue;
382             }
383             char u1 = Character.toUpperCase(c1);
384             char u2 = Character.toUpperCase(c2);
385             if (u1 == u2) {
386                 continue;
387             }
388             if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
389                 continue;
390             }
391             return false;
392         }
393         return true;
394     }
395 
396     public static String toLowerCase(String str, byte[] value, Locale locale) {
397         if (locale == null) {
398             throw new NullPointerException();
399         }
400         int first;
401         final int len = value.length;
402         // Now check if there are any characters that need to be changed, or are surrogate
403         for (first = 0 ; first < len; first++) {
404             int cp = value[first] & 0xff;
405             if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
406                 break;
407             }
408         }
409         if (first == len)
410             return str;
411         String lang = locale.getLanguage();
412         if (lang == "tr" || lang == "az" || lang == "lt") {
413             return toLowerCaseEx(str, value, first, locale, true);
414         }
415         byte[] result = new byte[len];
416         System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
417                                                        // lowerCase characters.
418         for (int i = first; i < len; i++) {
419             int cp = value[i] & 0xff;
420             cp = Character.toLowerCase(cp);
421             if (!canEncode(cp)) {                      // not a latin1 character
422                 return toLowerCaseEx(str, value, first, locale, false);
423             }
424             result[i] = (byte)cp;
425         }
426         return new String(result, LATIN1);
427     }
428 
429     private static String toLowerCaseEx(String str, byte[] value,
430                                         int first, Locale locale, boolean localeDependent)
431     {
432         byte[] result = StringUTF16.newBytesFor(value.length);
433         int resultOffset = 0;
434         for (int i = 0; i < first; i++) {
435             StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
436         }
437         for (int i = first; i < value.length; i++) {
438             int srcChar = value[i] & 0xff;
439             int lowerChar;
440             char[] lowerCharArray;
441             if (localeDependent) {
442                 lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale);
443             } else {
444                 lowerChar = Character.toLowerCase(srcChar);
445             }
446             if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
447                 StringUTF16.putChar(result, resultOffset++, lowerChar);
448             } else {
449                 if (lowerChar == Character.ERROR) {
450                     lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale);
451                 } else {
452                     lowerCharArray = Character.toChars(lowerChar);
453                 }
454                 /* Grow result if needed *
455                 int mapLen = lowerCharArray.length;
456                 if (mapLen > 1) {
457                     byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
458                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
459                     result = result2;
460                 }
461                 for (int x = 0; x < mapLen; ++x) {
462                     StringUTF16.putChar(result, resultOffset++, lowerCharArray[x]);
463                 }
464             }
465         }
466         return StringUTF16.newString(result, 0, resultOffset);
467     }
468 
469     public static String toUpperCase(String str, byte[] value, Locale locale) {
470         if (locale == null) {
471             throw new NullPointerException();
472         }
473         int first;
474         final int len = value.length;
475 
476         // Now check if there are any characters that need to be changed, or are surrogate
477         for (first = 0 ; first < len; first++ ) {
478             int cp = value[first] & 0xff;
479             if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
480                 break;
481             }
482         }
483         if (first == len) {
484             return str;
485         }
486         String lang = locale.getLanguage();
487         if (lang == "tr" || lang == "az" || lang == "lt") {
488             return toUpperCaseEx(str, value, first, locale, true);
489         }
490         byte[] result = new byte[len];
491         System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
492                                                        // upperCase characters.
493         for (int i = first; i < len; i++) {
494             int cp = value[i] & 0xff;
495             cp = Character.toUpperCaseEx(cp);
496             if (!canEncode(cp)) {                      // not a latin1 character
497                 return toUpperCaseEx(str, value, first, locale, false);
498             }
499             result[i] = (byte)cp;
500         }
501         return new String(result, LATIN1);
502     }
503 
504     private static String toUpperCaseEx(String str, byte[] value,
505                                         int first, Locale locale, boolean localeDependent)
506     {
507         byte[] result = StringUTF16.newBytesFor(value.length);
508         int resultOffset = 0;
509         for (int i = 0; i < first; i++) {
510             StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
511         }
512         for (int i = first; i < value.length; i++) {
513             int srcChar = value[i] & 0xff;
514             int upperChar;
515             char[] upperCharArray;
516             if (localeDependent) {
517                 upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale);
518             } else {
519                 upperChar = Character.toUpperCaseEx(srcChar);
520             }
521             if (Character.isBmpCodePoint(upperChar)) {
522                 StringUTF16.putChar(result, resultOffset++, upperChar);
523             } else {
524                 if (upperChar == Character.ERROR) {
525                     if (localeDependent) {
526                         upperCharArray =
527                             ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale);
528                     } else {
529                         upperCharArray = Character.toUpperCaseCharArray(srcChar);
530                     }
531                 } else {
532                     upperCharArray = Character.toChars(upperChar);
533                 }
534                 /* Grow result if needed *
535                 int mapLen = upperCharArray.length;
536                 if (mapLen > 1) {
537                     byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
538                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
539                     result = result2;
540                 }
541                 for (int x = 0; x < mapLen; ++x) {
542                     StringUTF16.putChar(result, resultOffset++, upperCharArray[x]);
543                 }
544             }
545         }
546         return StringUTF16.newString(result, 0, resultOffset);
547     }
548     */
549     // END Android-removed: Remove unused code.
550 
trim(byte[] value)551     public static String trim(byte[] value) {
552         int len = value.length;
553         int st = 0;
554         while ((st < len) && ((value[st] & 0xff) <= ' ')) {
555             st++;
556         }
557         while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) {
558             len--;
559         }
560         return ((st > 0) || (len < value.length)) ?
561             newString(value, st, len - st) : null;
562     }
563 
indexOfNonWhitespace(byte[] value)564     public static int indexOfNonWhitespace(byte[] value) {
565         int length = value.length;
566         int left = 0;
567         while (left < length) {
568             char ch = (char)(value[left] & 0xff);
569             if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
570                 break;
571             }
572             left++;
573         }
574         return left;
575     }
576 
lastIndexOfNonWhitespace(byte[] value)577     public static int lastIndexOfNonWhitespace(byte[] value) {
578         int length = value.length;
579         int right = length;
580         while (0 < right) {
581             char ch = (char)(value[right - 1] & 0xff);
582             if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
583                 break;
584             }
585             right--;
586         }
587         return right;
588     }
589 
strip(byte[] value)590     public static String strip(byte[] value) {
591         int left = indexOfNonWhitespace(value);
592         if (left == value.length) {
593             return "";
594         }
595         int right = lastIndexOfNonWhitespace(value);
596         return ((left > 0) || (right < value.length)) ? newString(value, left, right - left) : null;
597     }
598 
stripLeading(byte[] value)599     public static String stripLeading(byte[] value) {
600         int left = indexOfNonWhitespace(value);
601         if (left == value.length) {
602             return "";
603         }
604         return (left != 0) ? newString(value, left, value.length - left) : null;
605     }
606 
stripTrailing(byte[] value)607     public static String stripTrailing(byte[] value) {
608         int right = lastIndexOfNonWhitespace(value);
609         if (right == 0) {
610             return "";
611         }
612         return (right != value.length) ? newString(value, 0, right) : null;
613     }
614 
615     private final static class LinesSpliterator implements Spliterator<String> {
616         private byte[] value;
617         private int index;        // current index, modified on advance/split
618         private final int fence;  // one past last index
619 
LinesSpliterator(byte[] value)620         LinesSpliterator(byte[] value) {
621             this(value, 0, value.length);
622         }
623 
LinesSpliterator(byte[] value, int start, int length)624         LinesSpliterator(byte[] value, int start, int length) {
625             this.value = value;
626             this.index = start;
627             this.fence = start + length;
628         }
629 
indexOfLineSeparator(int start)630         private int indexOfLineSeparator(int start) {
631             for (int current = start; current < fence; current++) {
632                 byte ch = value[current];
633                 if (ch == '\n' || ch == '\r') {
634                     return current;
635                 }
636             }
637             return fence;
638         }
639 
skipLineSeparator(int start)640         private int skipLineSeparator(int start) {
641             if (start < fence) {
642                 if (value[start] == '\r') {
643                     int next = start + 1;
644                     if (next < fence && value[next] == '\n') {
645                         return next + 1;
646                     }
647                 }
648                 return start + 1;
649             }
650             return fence;
651         }
652 
next()653         private String next() {
654             int start = index;
655             int end = indexOfLineSeparator(start);
656             index = skipLineSeparator(end);
657             return newString(value, start, end - start);
658         }
659 
660         @Override
tryAdvance(Consumer<? super String> action)661         public boolean tryAdvance(Consumer<? super String> action) {
662             if (action == null) {
663                 throw new NullPointerException("tryAdvance action missing");
664             }
665             if (index != fence) {
666                 action.accept(next());
667                 return true;
668             }
669             return false;
670         }
671 
672         @Override
forEachRemaining(Consumer<? super String> action)673         public void forEachRemaining(Consumer<? super String> action) {
674             if (action == null) {
675                 throw new NullPointerException("forEachRemaining action missing");
676             }
677             while (index != fence) {
678                 action.accept(next());
679             }
680         }
681 
682         @Override
trySplit()683         public Spliterator<String> trySplit() {
684             int half = (fence + index) >>> 1;
685             int mid = skipLineSeparator(indexOfLineSeparator(half));
686             if (mid < fence) {
687                 int start = index;
688                 index = mid;
689                 return new LinesSpliterator(value, start, mid - start);
690             }
691             return null;
692         }
693 
694         @Override
estimateSize()695         public long estimateSize() {
696             return fence - index + 1;
697         }
698 
699         @Override
characteristics()700         public int characteristics() {
701             return Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL;
702         }
703     }
704 
lines(byte[] value)705     static Stream<String> lines(byte[] value) {
706         return StreamSupport.stream(new LinesSpliterator(value), false);
707     }
708 
putChar(byte[] val, int index, int c)709     public static void putChar(byte[] val, int index, int c) {
710         //assert (canEncode(c));
711         val[index] = (byte)(c);
712     }
713 
getChar(byte[] val, int index)714     public static char getChar(byte[] val, int index) {
715         return (char)(val[index] & 0xff);
716     }
717 
toBytes(int[] val, int off, int len)718     public static byte[] toBytes(int[] val, int off, int len) {
719         byte[] ret = new byte[len];
720         for (int i = 0; i < len; i++) {
721             int cp = val[off++];
722             if (!canEncode(cp)) {
723                 return null;
724             }
725             ret[i] = (byte)cp;
726         }
727         return ret;
728     }
729 
toBytes(char c)730     public static byte[] toBytes(char c) {
731         return new byte[] { (byte)c };
732     }
733 
newString(byte[] val, int index, int len)734     public static String newString(byte[] val, int index, int len) {
735         // Android-changed: Avoid byte[] allocation.
736         // return new String(Arrays.copyOfRange(val, index, index + len),
737         //                   LATIN1);
738         return new String(val, /*high=*/ 0, index, len);
739     }
740 
fillNull(byte[] val, int index, int end)741     public static void fillNull(byte[] val, int index, int end) {
742         Arrays.fill(val, index, end, (byte)0);
743     }
744 
745     // inflatedCopy byte[] -> char[]
746     @HotSpotIntrinsicCandidate
inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len)747     public static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
748         for (int i = 0; i < len; i++) {
749             dst[dstOff++] = (char)(src[srcOff++] & 0xff);
750         }
751     }
752 
753     // inflatedCopy byte[] -> byte[]
754     @HotSpotIntrinsicCandidate
inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len)755     public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
756         StringUTF16.inflate(src, srcOff, dst, dstOff, len);
757     }
758 
759     static class CharsSpliterator implements Spliterator.OfInt {
760         private final byte[] array;
761         private int index;        // current index, modified on advance/split
762         private final int fence;  // one past last index
763         private final int cs;
764 
CharsSpliterator(byte[] array, int acs)765         CharsSpliterator(byte[] array, int acs) {
766             this(array, 0, array.length, acs);
767         }
768 
CharsSpliterator(byte[] array, int origin, int fence, int acs)769         CharsSpliterator(byte[] array, int origin, int fence, int acs) {
770             this.array = array;
771             this.index = origin;
772             this.fence = fence;
773             this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
774                       | Spliterator.SUBSIZED;
775         }
776 
777         @Override
trySplit()778         public OfInt trySplit() {
779             int lo = index, mid = (lo + fence) >>> 1;
780             return (lo >= mid)
781                    ? null
782                    : new CharsSpliterator(array, lo, index = mid, cs);
783         }
784 
785         @Override
forEachRemaining(IntConsumer action)786         public void forEachRemaining(IntConsumer action) {
787             byte[] a; int i, hi; // hoist accesses and checks from loop
788             if (action == null)
789                 throw new NullPointerException();
790             if ((a = array).length >= (hi = fence) &&
791                 (i = index) >= 0 && i < (index = hi)) {
792                 do { action.accept(a[i] & 0xff); } while (++i < hi);
793             }
794         }
795 
796         @Override
tryAdvance(IntConsumer action)797         public boolean tryAdvance(IntConsumer action) {
798             if (action == null)
799                 throw new NullPointerException();
800             if (index >= 0 && index < fence) {
801                 action.accept(array[index++] & 0xff);
802                 return true;
803             }
804             return false;
805         }
806 
807         @Override
estimateSize()808         public long estimateSize() { return (long)(fence - index); }
809 
810         @Override
characteristics()811         public int characteristics() {
812             return cs;
813         }
814     }
815 }
816