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
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
52     public static boolean canEncode(int cp) {
53         return cp >>> 8 == 0;
54     }
55
56     public static int length(byte[] value) {
57         return value.length;
58     }
59
60     public static int codePointAt(byte[] value, int index, int end) {
61         return value[index] & 0xff;
62     }
63
64     public static int codePointBefore(byte[] value, int index) {
65         return value[index - 1] & 0xff;
66     }
67
68     public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
69         return endIndex - beginIndex;
70     }
71
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
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
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
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
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
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
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
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      */

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
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
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                 char c1 = (char) CharacterDataLatin1.instance.toUpperCase(getChar(value, k));
158                 char c2 = (char) CharacterDataLatin1.instance.toUpperCase(getChar(other, k));
159                 if (c1 != c2) {
160                     c1 = Character.toLowerCase(c1);
161                     c2 = Character.toLowerCase(c2);
162                     if (c1 != c2) {
163                         return c1 - c2;
164                     }
165                 }
166             }
167         }
168         return len1 - len2;
169     }
170
171     public static int compareToCI_UTF16(byte[] value, byte[] other) {
172         int len1 = length(value);
173         int len2 = StringUTF16.length(other);
174         int lim = Math.min(len1, len2);
175         for (int k = 0; k < lim; k++) {
176             char c1 = getChar(value, k);
177             char c2 = StringUTF16.getChar(other, k);
178             if (c1 != c2) {
179                 c1 = Character.toUpperCase(c1);
180                 c2 = Character.toUpperCase(c2);
181                 if (c1 != c2) {
182                     c1 = Character.toLowerCase(c1);
183                     c2 = Character.toLowerCase(c2);
184                     if (c1 != c2) {
185                         return c1 - c2;
186                     }
187                 }
188             }
189         }
190         return len1 - len2;
191     }
192
193     public static int hashCode(byte[] value) {
194         int h = 0;
195         for (byte v : value) {
196             h = 31 * h + (v & 0xff);
197         }
198         return h;
199     }
200
201     public static int indexOf(byte[] value, int ch, int fromIndex) {
202         if (!canEncode(ch)) {
203             return -1;
204         }
205         int max = value.length;
206         if (fromIndex < 0) {
207             fromIndex = 0;
208         } else if (fromIndex >= max) {
209             // Note: fromIndex might be near -1>>>1.
210             return -1;
211         }
212         byte c = (byte)ch;
213         for (int i = fromIndex; i < max; i++) {
214             if (value[i] == c) {
215                return i;
216             }
217         }
218         return -1;
219     }
220
221     @HotSpotIntrinsicCandidate
222     public static int indexOf(byte[] value, byte[] str) {
223         if (str.length == 0) {
224             return 0;
225         }
226         if (value.length == 0) {
227             return -1;
228         }
229         return indexOf(value, value.length, str, str.length, 0);
230     }
231
232     @HotSpotIntrinsicCandidate
233     public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
234         byte first = str[0];
235         int max = (valueCount - strCount);
236         for (int i = fromIndex; i <= max; i++) {
237             // Look for first character.
238             if (value[i] != first) {
239                 while (++i <= max && value[i] != first);
240             }
241             // Found first character, now look at the rest of value
242             if (i <= max) {
243                 int j = i + 1;
244                 int end = j + strCount - 1;
245                 for (int k = 1; j < end && value[j] == str[k]; j++, k++);
246                 if (j == end) {
247                     // Found whole string.
248                     return i;
249                 }
250             }
251         }
252         return -1;
253     }
254
255     public static int lastIndexOf(byte[] src, int srcCount,
256                                   byte[] tgt, int tgtCount, int fromIndex) {
257         int min = tgtCount - 1;
258         int i = min + fromIndex;
259         int strLastIndex = tgtCount - 1;
260         char strLastChar = (char)(tgt[strLastIndex] & 0xff);
261
262   startSearchForLastChar:
263         while (true) {
264             while (i >= min && (src[i] & 0xff) != strLastChar) {
265                 i--;
266             }
267             if (i < min) {
268                 return -1;
269             }
270             int j = i - 1;
271             int start = j - strLastIndex;
272             int k = strLastIndex - 1;
273             while (j > start) {
274                 if ((src[j--] & 0xff) != (tgt[k--] & 0xff)) {
275                     i--;
276                     continue startSearchForLastChar;
277                 }
278             }
279             return start + 1;
280         }
281     }
282
283     public static int lastIndexOf(final byte[] value, int ch, int fromIndex) {
284         if (!canEncode(ch)) {
285             return -1;
286         }
287         int off  = Math.min(fromIndex, value.length - 1);
288         for (; off >= 0; off--) {
289             if (value[off] == (byte)ch) {
290                 return off;
291             }
292         }
293         return -1;
294     }
295
296     public static String replace(byte[] value, char oldChar, char newChar) {
297         if (canEncode(oldChar)) {
298             int len = value.length;
299             int i = -1;
300             while (++i < len) {
301                 if (value[i] == (byte)oldChar) {
302                     break;
303                 }
304             }
305             if (i < len) {
306                 if (canEncode(newChar)) {
307                     byte buf[] = new byte[len];
308                     for (int j = 0; j < i; j++) {    // TBD arraycopy?
309                         buf[j] = value[j];
310                     }
311                     while (i < len) {
312                         byte c = value[i];
313                         buf[i] = (c == (byte)oldChar) ? (byte)newChar : c;
314                         i++;
315                     }
316                     return new String(buf, LATIN1);
317                 } else {
318                     byte[] buf = StringUTF16.newBytesFor(len);
319                     // inflate from latin1 to UTF16
320                     inflate(value, 0, buf, 0, i);
321                     while (i < len) {
322                         char c = (char)(value[i] & 0xff);
323                         StringUTF16.putChar(buf, i, (c == oldChar) ? newChar : c);
324                         i++;
325                     }
326                     return new String(buf, UTF16);
327                 }
328             }
329         }
330         return null// for string to return this;
331     }
332
333     // case insensitive
334     public static boolean regionMatchesCI(byte[] value, int toffset,
335                                           byte[] other, int ooffset, int len) {
336         int last = toffset + len;
337         while (toffset < last) {
338             char c1 = (char)(value[toffset++] & 0xff);
339             char c2 = (char)(other[ooffset++] & 0xff);
340             if (c1 == c2) {
341                 continue;
342             }
343             char u1 = Character.toUpperCase(c1);
344             char u2 = Character.toUpperCase(c2);
345             if (u1 == u2) {
346                 continue;
347             }
348             if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
349                 continue;
350             }
351             return false;
352         }
353         return true;
354     }
355
356     public static boolean regionMatchesCI_UTF16(byte[] value, int toffset,
357                                                 byte[] other, int ooffset, int len) {
358         int last = toffset + len;
359         while (toffset < last) {
360             char c1 = (char)(value[toffset++] & 0xff);
361             char c2 = StringUTF16.getChar(other, ooffset++);
362             if (c1 == c2) {
363                 continue;
364             }
365             char u1 = Character.toUpperCase(c1);
366             char u2 = Character.toUpperCase(c2);
367             if (u1 == u2) {
368                 continue;
369             }
370             if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
371                 continue;
372             }
373             return false;
374         }
375         return true;
376     }
377
378     public static String toLowerCase(String str, byte[] value, Locale locale) {
379         if (locale == null) {
380             throw new NullPointerException();
381         }
382         int first;
383         final int len = value.length;
384         // Now check if there are any characters that need to be changed, or are surrogate
385         for (first = 0 ; first < len; first++) {
386             int cp = value[first] & 0xff;
387             if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
388                 break;
389             }
390         }
391         if (first == len)
392             return str;
393         String lang = locale.getLanguage();
394         if (lang == "tr" || lang == "az" || lang == "lt") {
395             return toLowerCaseEx(str, value, first, locale, true);
396         }
397         byte[] result = new byte[len];
398         System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
399                                                        // lowerCase characters.
400         for (int i = first; i < len; i++) {
401             int cp = value[i] & 0xff;
402             cp = Character.toLowerCase(cp);
403             if (!canEncode(cp)) {                      // not a latin1 character
404                 return toLowerCaseEx(str, value, first, locale, false);
405             }
406             result[i] = (byte)cp;
407         }
408         return new String(result, LATIN1);
409     }
410
411     private static String toLowerCaseEx(String str, byte[] value,
412                                         int first, Locale locale, boolean localeDependent)
413     {
414         byte[] result = StringUTF16.newBytesFor(value.length);
415         int resultOffset = 0;
416         for (int i = 0; i < first; i++) {
417             StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
418         }
419         for (int i = first; i < value.length; i++) {
420             int srcChar = value[i] & 0xff;
421             int lowerChar;
422             char[] lowerCharArray;
423             if (localeDependent) {
424                 lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale);
425             } else {
426                 lowerChar = Character.toLowerCase(srcChar);
427             }
428             if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
429                 StringUTF16.putChar(result, resultOffset++, lowerChar);
430             } else {
431                 if (lowerChar == Character.ERROR) {
432                     lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale);
433                 } else {
434                     lowerCharArray = Character.toChars(lowerChar);
435                 }
436                 /* Grow result if needed */
437                 int mapLen = lowerCharArray.length;
438                 if (mapLen > 1) {
439                     byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
440                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
441                     result = result2;
442                 }
443                 for (int x = 0; x < mapLen; ++x) {
444                     StringUTF16.putChar(result, resultOffset++, lowerCharArray[x]);
445                 }
446             }
447         }
448         return StringUTF16.newString(result, 0, resultOffset);
449     }
450
451     public static String toUpperCase(String str, byte[] value, Locale locale) {
452         if (locale == null) {
453             throw new NullPointerException();
454         }
455         int first;
456         final int len = value.length;
457
458         // Now check if there are any characters that need to be changed, or are surrogate
459         for (first = 0 ; first < len; first++ ) {
460             int cp = value[first] & 0xff;
461             if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
462                 break;
463             }
464         }
465         if (first == len) {
466             return str;
467         }
468         String lang = locale.getLanguage();
469         if (lang == "tr" || lang == "az" || lang == "lt") {
470             return toUpperCaseEx(str, value, first, locale, true);
471         }
472         byte[] result = new byte[len];
473         System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
474                                                        // upperCase characters.
475         for (int i = first; i < len; i++) {
476             int cp = value[i] & 0xff;
477             cp = Character.toUpperCaseEx(cp);
478             if (!canEncode(cp)) {                      // not a latin1 character
479                 return toUpperCaseEx(str, value, first, locale, false);
480             }
481             result[i] = (byte)cp;
482         }
483         return new String(result, LATIN1);
484     }
485
486     private static String toUpperCaseEx(String str, byte[] value,
487                                         int first, Locale locale, boolean localeDependent)
488     {
489         byte[] result = StringUTF16.newBytesFor(value.length);
490         int resultOffset = 0;
491         for (int i = 0; i < first; i++) {
492             StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
493         }
494         for (int i = first; i < value.length; i++) {
495             int srcChar = value[i] & 0xff;
496             int upperChar;
497             char[] upperCharArray;
498             if (localeDependent) {
499                 upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale);
500             } else {
501                 upperChar = Character.toUpperCaseEx(srcChar);
502             }
503             if (Character.isBmpCodePoint(upperChar)) {
504                 StringUTF16.putChar(result, resultOffset++, upperChar);
505             } else {
506                 if (upperChar == Character.ERROR) {
507                     if (localeDependent) {
508                         upperCharArray =
509                             ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale);
510                     } else {
511                         upperCharArray = Character.toUpperCaseCharArray(srcChar);
512                     }
513                 } else {
514                     upperCharArray = Character.toChars(upperChar);
515                 }
516                 /* Grow result if needed */
517                 int mapLen = upperCharArray.length;
518                 if (mapLen > 1) {
519                     byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
520                     System.arraycopy(result, 0, result2, 0, resultOffset << 1);
521                     result = result2;
522                 }
523                 for (int x = 0; x < mapLen; ++x) {
524                     StringUTF16.putChar(result, resultOffset++, upperCharArray[x]);
525                 }
526             }
527         }
528         return StringUTF16.newString(result, 0, resultOffset);
529     }
530
531     public static String trim(byte[] value) {
532         int len = value.length;
533         int st = 0;
534         while ((st < len) && ((value[st] & 0xff) <= ' ')) {
535             st++;
536         }
537         while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) {
538             len--;
539         }
540         return ((st > 0) || (len < value.length)) ?
541             newString(value, st, len - st) : null;
542     }
543
544     public static int indexOfNonWhitespace(byte[] value) {
545         int length = value.length;
546         int left = 0;
547         while (left < length) {
548             char ch = (char)(value[left] & 0xff);
549             if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
550                 break;
551             }
552             left++;
553         }
554         return left;
555     }
556
557     public static int lastIndexOfNonWhitespace(byte[] value) {
558         int length = value.length;
559         int right = length;
560         while (0 < right) {
561             char ch = (char)(value[right - 1] & 0xff);
562             if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) {
563                 break;
564             }
565             right--;
566         }
567         return right;
568     }
569
570     public static String strip(byte[] value) {
571         int left = indexOfNonWhitespace(value);
572         if (left == value.length) {
573             return "";
574         }
575         int right = lastIndexOfNonWhitespace(value);
576         return ((left > 0) || (right < value.length)) ? newString(value, left, right - left) : null;
577     }
578
579     public static String stripLeading(byte[] value) {
580         int left = indexOfNonWhitespace(value);
581         if (left == value.length) {
582             return "";
583         }
584         return (left != 0) ? newString(value, left, value.length - left) : null;
585     }
586
587     public static String stripTrailing(byte[] value) {
588         int right = lastIndexOfNonWhitespace(value);
589         if (right == 0) {
590             return "";
591         }
592         return (right != value.length) ? newString(value, 0, right) : null;
593     }
594
595     private final static class LinesSpliterator implements Spliterator<String> {
596         private byte[] value;
597         private int index;        // current index, modified on advance/split
598         private final int fence;  // one past last index
599
600         LinesSpliterator(byte[] value) {
601             this(value, 0, value.length);
602         }
603
604         LinesSpliterator(byte[] value, int start, int length) {
605             this.value = value;
606             this.index = start;
607             this.fence = start + length;
608         }
609
610         private int indexOfLineSeparator(int start) {
611             for (int current = start; current < fence; current++) {
612                 byte ch = value[current];
613                 if (ch == '\n' || ch == '\r') {
614                     return current;
615                 }
616             }
617             return fence;
618         }
619
620         private int skipLineSeparator(int start) {
621             if (start < fence) {
622                 if (value[start] == '\r') {
623                     int next = start + 1;
624                     if (next < fence && value[next] == '\n') {
625                         return next + 1;
626                     }
627                 }
628                 return start + 1;
629             }
630             return fence;
631         }
632
633         private String next() {
634             int start = index;
635             int end = indexOfLineSeparator(start);
636             index = skipLineSeparator(end);
637             return newString(value, start, end - start);
638         }
639
640         @Override
641         public boolean tryAdvance(Consumer<? super String> action) {
642             if (action == null) {
643                 throw new NullPointerException("tryAdvance action missing");
644             }
645             if (index != fence) {
646                 action.accept(next());
647                 return true;
648             }
649             return false;
650         }
651
652         @Override
653         public void forEachRemaining(Consumer<? super String> action) {
654             if (action == null) {
655                 throw new NullPointerException("forEachRemaining action missing");
656             }
657             while (index != fence) {
658                 action.accept(next());
659             }
660         }
661
662         @Override
663         public Spliterator<String> trySplit() {
664             int half = (fence + index) >>> 1;
665             int mid = skipLineSeparator(indexOfLineSeparator(half));
666             if (mid < fence) {
667                 int start = index;
668                 index = mid;
669                 return new LinesSpliterator(value, start, mid - start);
670             }
671             return null;
672         }
673
674         @Override
675         public long estimateSize() {
676             return fence - index + 1;
677         }
678
679         @Override
680         public int characteristics() {
681             return Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL;
682         }
683     }
684
685     static Stream<String> lines(byte[] value) {
686         return StreamSupport.stream(new LinesSpliterator(value), false);
687     }
688
689     public static void putChar(byte[] val, int index, int c) {
690         //assert (canEncode(c));
691         val[index] = (byte)(c);
692     }
693
694     public static char getChar(byte[] val, int index) {
695         return (char)(val[index] & 0xff);
696     }
697
698     public static byte[] toBytes(int[] val, int off, int len) {
699         byte[] ret = new byte[len];
700         for (int i = 0; i < len; i++) {
701             int cp = val[off++];
702             if (!canEncode(cp)) {
703                 return null;
704             }
705             ret[i] = (byte)cp;
706         }
707         return ret;
708     }
709
710     public static byte[] toBytes(char c) {
711         return new byte[] { (byte)c };
712     }
713
714     public static String newString(byte[] val, int index, int len) {
715         return new String(Arrays.copyOfRange(val, index, index + len),
716                           LATIN1);
717     }
718
719     public static void fillNull(byte[] val, int index, int end) {
720         Arrays.fill(val, index, end, (byte)0);
721     }
722
723     // inflatedCopy byte[] -> char[]
724     @HotSpotIntrinsicCandidate
725     public static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
726         for (int i = 0; i < len; i++) {
727             dst[dstOff++] = (char)(src[srcOff++] & 0xff);
728         }
729     }
730
731     // inflatedCopy byte[] -> byte[]
732     @HotSpotIntrinsicCandidate
733     public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
734         StringUTF16.inflate(src, srcOff, dst, dstOff, len);
735     }
736
737     static class CharsSpliterator implements Spliterator.OfInt {
738         private final byte[] array;
739         private int index;        // current index, modified on advance/split
740         private final int fence;  // one past last index
741         private final int cs;
742
743         CharsSpliterator(byte[] array, int acs) {
744             this(array, 0, array.length, acs);
745         }
746
747         CharsSpliterator(byte[] array, int origin, int fence, int acs) {
748             this.array = array;
749             this.index = origin;
750             this.fence = fence;
751             this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
752                       | Spliterator.SUBSIZED;
753         }
754
755         @Override
756         public OfInt trySplit() {
757             int lo = index, mid = (lo + fence) >>> 1;
758             return (lo >= mid)
759                    ? null
760                    : new CharsSpliterator(array, lo, index = mid, cs);
761         }
762
763         @Override
764         public void forEachRemaining(IntConsumer action) {
765             byte[] a; int i, hi; // hoist accesses and checks from loop
766             if (action == null)
767                 throw new NullPointerException();
768             if ((a = array).length >= (hi = fence) &&
769                 (i = index) >= 0 && i < (index = hi)) {
770                 do { action.accept(a[i] & 0xff); } while (++i < hi);
771             }
772         }
773
774         @Override
775         public boolean tryAdvance(IntConsumer action) {
776             if (action == null)
777                 throw new NullPointerException();
778             if (index >= 0 && index < fence) {
779                 action.accept(array[index++] & 0xff);
780                 return true;
781             }
782             return false;
783         }
784
785         @Override
786         public long estimateSize() { return (long)(fence - index); }
787
788         @Override
789         public int characteristics() {
790             return cs;
791         }
792     }
793 }
794