• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Spliterator;
29 import java.util.function.Consumer;
30 import java.util.function.IntConsumer;
31 import java.util.stream.Stream;
32 import java.util.stream.StreamSupport;
33 import jdk.internal.HotSpotIntrinsicCandidate;
34 
35 // BEGIN Android-added: Clarification for usage of StringUTF16 and not StringLatin1.
36 /**
37  * In upstream, this class (StringUTF16) contains implementations of various APIs and their helper
38  * methods for uncompressed strings. Libcore makes use of it to implement APIs for both compressed
39  * and uncompressed strings.
40  *
41  * Upstream implementation also has StringLatin1 class which contains same implementations for
42  * compressed strings (and this class is for uncompressed). In our case we use only StringUTF16
43  * (and StringLatin1 is intentionally not imported) as string characters are managed by ART and
44  * everything is implemented in terms of {@link String#charAt(int)} an other primitives which do not
45  * have direct access to underlying character array. From those two, if we choose to implement
46  * everything in those primitives, StringUTF16 has less difference with upstream, so it is chosen
47  * here.
48  *
49  * @hide
50  */
51 // END Android-added: Clarification for usage of StringUTF16 and not StringLatin1.
52 final class StringUTF16 {
53 
54     // BEGIN Android-changed: Pass String instead of byte[]; implement in terms of charAt().
55     // @HotSpotIntrinsicCandidate
56     // intrinsic performs no bounds checks
57     /*
58     static char getChar(byte[] val, int index) {
59         assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
60         index <<= 1;
61         return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
62                       ((val[index]   & 0xff) << LO_BYTE_SHIFT));
63      */
64     @HotSpotIntrinsicCandidate
getChar(String val, int index)65     static char getChar(String val, int index) {
66         return val.charAt(index);
67     }
68     // END Android-changed: Pass String instead of byte[]; implement in terms of charAt().
69 
70     // BEGIN Android-changed: Pass String instead of byte[].
71     /*
72     public static int length(byte[] value) {
73         return value.length >> 1;
74      */
length(String value)75     public static int length(String value) {
76         return value.length();
77     // END Android-changed: Pass String instead of byte[].
78     }
79 
80     // BEGIN Android-changed: Pass String instead of byte[].
81     /*
82     public static int indexOfNonWhitespace(byte[] value) {
83         int length = value.length >> 1;
84      */
indexOfNonWhitespace(String value)85     public static int indexOfNonWhitespace(String value) {
86         int length = value.length();
87         int left = 0;
88         while (left < length) {
89             /*
90             int codepoint = codePointAt(value, left, length);
91              */
92             int codepoint = value.codePointAt(left);
93     // END Android-changed: Pass String instead of byte[].
94             if (codepoint != ' ' && codepoint != '\t' && !Character.isWhitespace(codepoint)) {
95                 break;
96             }
97             left += Character.charCount(codepoint);
98         }
99         return left;
100     }
101 
102     // BEGIN Android-changed: Pass String instead of byte[].
103     /*
104     public static int lastIndexOfNonWhitespace(byte[] value) {
105         int length = value.length >> 1;
106         int right = length;
107      */
lastIndexOfNonWhitespace(String value)108     public static int lastIndexOfNonWhitespace(String value) {
109         int right = value.length();
110         while (0 < right) {
111             /*
112             int codepoint = codePointBefore(value, right);
113              */
114             int codepoint = value.codePointBefore(right);
115     // END Android-changed: Pass String instead of byte[].
116             if (codepoint != ' ' && codepoint != '\t' && !Character.isWhitespace(codepoint)) {
117                 break;
118             }
119             right -= Character.charCount(codepoint);
120         }
121         return right;
122     }
123 
124     // BEGIN Android-changed: Pass String instead of byte[].
125     /*
126     public static String strip(byte[] value) {
127         int length = value.length >> 1;
128      */
strip(String value)129     public static String strip(String value) {
130         int length = value.length();
131     // END Android-changed: Pass String instead of byte[].
132         int left = indexOfNonWhitespace(value);
133         if (left == length) {
134             return "";
135         }
136         int right = lastIndexOfNonWhitespace(value);
137         return ((left > 0) || (right < length)) ? newString(value, left, right - left) : null;
138     }
139 
140     // BEGIN Android-changed: Pass String instead of byte[].
141     /*
142     public static String stripLeading(byte[] value) {
143         int length = value.length >> 1;
144      */
stripLeading(String value)145     public static String stripLeading(String value) {
146         int length = value.length();
147     // END Android-changed: Pass String instead of byte[].
148         int left = indexOfNonWhitespace(value);
149         if (left == length) {
150             return "";
151         }
152         return (left != 0) ? newString(value, left, length - left) : null;
153     }
154 
155     // BEGIN Android-changed: Pass String instead of byte[].
156     /*
157     public static String stripTrailing(byte[] value) {
158         int length = value.length >> 1;
159      */
stripTrailing(String value)160     public static String stripTrailing(String value) {
161         int length = value.length();
162     // END Android-changed: Pass String instead of byte[].
163         int right = lastIndexOfNonWhitespace(value);
164         if (right == 0) {
165             return "";
166         }
167         return (right != length) ? newString(value, 0, right) : null;
168     }
169 
170     private final static class LinesSpliterator implements Spliterator<String> {
171         // BEGIN Android-changed: Pass String instead of byte[].
172         /*
173         private byte[] value;
174          */
175         private String value;
176         // END Android-changed: Pass String instead of byte[].
177         private int index;        // current index, modified on advance/split
178         private final int fence;  // one past last index
179 
180         // BEGIN Android-changed: Pass String instead of byte[].
181         /*
182         LinesSpliterator(byte[] value) {
183             this(value, 0, value.length >>> 1);
184         */
LinesSpliterator(String value)185         LinesSpliterator(String value) {
186             this(value, 0, value.length());
187         // END Android-changed: Pass String instead of byte[].
188         }
189 
190         // BEGIN Android-changed: Pass String instead of byte[].
191         /*
192         LinesSpliterator(byte[] value, int start, int length) {
193          */
LinesSpliterator(String value, int start, int length)194         LinesSpliterator(String value, int start, int length) {
195         // END Android-changed: Pass String instead of byte[].
196             this.value = value;
197             this.index = start;
198             this.fence = start + length;
199         }
200 
indexOfLineSeparator(int start)201         private int indexOfLineSeparator(int start) {
202             for (int current = start; current < fence; current++) {
203                 char ch = getChar(value, current);
204                 if (ch == '\n' || ch == '\r') {
205                     return current;
206                 }
207             }
208             return fence;
209         }
210 
skipLineSeparator(int start)211         private int skipLineSeparator(int start) {
212             if (start < fence) {
213                 if (getChar(value, start) == '\r') {
214                     int next = start + 1;
215                     if (next < fence && getChar(value, next) == '\n') {
216                         return next + 1;
217                     }
218                 }
219                 return start + 1;
220             }
221             return fence;
222         }
223 
next()224         private String next() {
225             int start = index;
226             int end = indexOfLineSeparator(start);
227             index = skipLineSeparator(end);
228             return newString(value, start, end - start);
229         }
230 
231         @Override
tryAdvance(Consumer<? super String> action)232         public boolean tryAdvance(Consumer<? super String> action) {
233             if (action == null) {
234                 throw new NullPointerException("tryAdvance action missing");
235             }
236             if (index != fence) {
237                 action.accept(next());
238                 return true;
239             }
240             return false;
241         }
242 
243         @Override
forEachRemaining(Consumer<? super String> action)244         public void forEachRemaining(Consumer<? super String> action) {
245             if (action == null) {
246                 throw new NullPointerException("forEachRemaining action missing");
247             }
248             while (index != fence) {
249                 action.accept(next());
250             }
251         }
252 
253         @Override
trySplit()254         public Spliterator<String> trySplit() {
255             int half = (fence + index) >>> 1;
256             int mid = skipLineSeparator(indexOfLineSeparator(half));
257             if (mid < fence) {
258                 int start = index;
259                 index = mid;
260                 return new LinesSpliterator(value, start, mid - start);
261             }
262             return null;
263         }
264 
265         @Override
estimateSize()266         public long estimateSize() {
267             return fence - index + 1;
268         }
269 
270         @Override
characteristics()271         public int characteristics() {
272             return Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL;
273         }
274     }
275 
276     // BEGIN Android-changed: Pass String instead of byte[].
277     /*
278     static Stream<String> lines(byte[] value) {
279      */
lines(String value)280     static Stream<String> lines(String value) {
281         return StreamSupport.stream(new LinesSpliterator(value), false);
282     // END Android-changed: Pass String instead of byte[].
283     }
284 
285     // BEGIN Android-changed: Pass String instead of byte[]; implement in terms of substring().
286     /*
287     public static String newString(byte[] val, int index, int len) {
288         if (String.COMPACT_STRINGS) {
289             byte[] buf = compress(val, index, len);
290             if (buf != null) {
291                 return new String(buf, LATIN1);
292             }
293         }
294         int last = index + len;
295         return new String(Arrays.copyOfRange(val, index << 1, last << 1), UTF16);
296     }
297      */
newString(String val, int index, int len)298     public static String newString(String val, int index, int len) {
299         return val.substring(index, index + len);
300     }
301     // END Android-changed: Pass String instead of byte[]; implement in terms of substring().
302 
303     static class CharsSpliterator implements Spliterator.OfInt {
304         // BEGIN Android-changed: Pass String instead of byte[].
305         /*
306         private final byte[] array;
307          */
308         private final String array;
309         // END Android-changed: Pass String instead of byte[].
310         private int index;        // current index, modified on advance/split
311         private final int fence;  // one past last index
312         private final int cs;
313 
314         // BEGIN Android-changed: Pass String instead of byte[].
315         /*
316         CharsSpliterator(byte[] array, int acs) {
317             this(array, 0, array.length >> 1, acs);
318          */
CharsSpliterator(String array, int acs)319         CharsSpliterator(String array, int acs) {
320             this(array, 0, array.length(), acs);
321         // END Android-changed: Pass String instead of byte[].
322         }
323 
324         // BEGIN Android-changed: Pass String instead of byte[].
325         /*
326         CharsSpliterator(byte[] array, int origin, int fence, int acs) {
327          */
CharsSpliterator(String array, int origin, int fence, int acs)328         CharsSpliterator(String array, int origin, int fence, int acs) {
329         // END Android-changed: Pass String instead of byte[].
330             this.array = array;
331             this.index = origin;
332             this.fence = fence;
333             this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
334                       | Spliterator.SUBSIZED;
335         }
336 
337         @Override
trySplit()338         public OfInt trySplit() {
339             int lo = index, mid = (lo + fence) >>> 1;
340             return (lo >= mid)
341                    ? null
342                    : new CharsSpliterator(array, lo, index = mid, cs);
343         }
344 
345         @Override
forEachRemaining(IntConsumer action)346         public void forEachRemaining(IntConsumer action) {
347             // BEGIN Android-changed: Pass String instead of byte[].
348             /*
349             byte[] a; int i, hi; // hoist accesses and checks from loop
350              */
351             String a; int i, hi; // hoist accesses and checks from loop
352             // END Android-changed: Pass String instead of byte[].
353             if (action == null)
354                 throw new NullPointerException();
355             // BEGIN Android-changed: Pass String instead of byte[].
356             /*
357             if (((a = array).length >> 1) >= (hi = fence) &&
358              */
359             if (((a = array).length()) >= (hi = fence) &&
360             // END Android-changed: Pass String instead of byte[].
361                 (i = index) >= 0 && i < (index = hi)) {
362                 do {
363                     action.accept(charAt(a, i));
364                 } while (++i < hi);
365             }
366         }
367 
368         @Override
tryAdvance(IntConsumer action)369         public boolean tryAdvance(IntConsumer action) {
370             if (action == null)
371                 throw new NullPointerException();
372             int i = index;
373             if (i >= 0 && i < fence) {
374                 action.accept(charAt(array, i));
375                 index++;
376                 return true;
377             }
378             return false;
379         }
380 
381         @Override
estimateSize()382         public long estimateSize() { return (long)(fence - index); }
383 
384         @Override
characteristics()385         public int characteristics() {
386             return cs;
387         }
388     }
389 
390     static class CodePointsSpliterator implements Spliterator.OfInt {
391         // BEGIN Android-changed: Pass String instead of byte[].
392         /*
393         private final byte[] array;
394          */
395         private final String array;
396         // END Android-changed: Pass String instead of byte[].
397         private int index;        // current index, modified on advance/split
398         private final int fence;  // one past last index
399         private final int cs;
400 
401         // BEGIN Android-changed: Pass String instead of byte[].
402         /*
403         CodePointsSpliterator(byte[] array, int acs) {
404             this(array, 0, array.length >> 1, acs);
405          */
CodePointsSpliterator(String array, int acs)406         CodePointsSpliterator(String array, int acs) {
407             this(array, 0, array.length(), acs);
408         // END Android-changed: Pass String instead of byte[].
409         }
410 
411         // BEGIN Android-changed: Pass String instead of byte[].
412         /*
413         CodePointsSpliterator(byte[] array, int origin, int fence, int acs) {
414          */
CodePointsSpliterator(String array, int origin, int fence, int acs)415         CodePointsSpliterator(String array, int origin, int fence, int acs) {
416         // END Android-changed: Pass String instead of byte[].
417             this.array = array;
418             this.index = origin;
419             this.fence = fence;
420             this.cs = acs | Spliterator.ORDERED;
421         }
422 
423         @Override
trySplit()424         public OfInt trySplit() {
425             int lo = index, mid = (lo + fence) >>> 1;
426             if (lo >= mid)
427                 return null;
428 
429             int midOneLess;
430             // If the mid-point intersects a surrogate pair
431             if (Character.isLowSurrogate(charAt(array, mid)) &&
432                 Character.isHighSurrogate(charAt(array, midOneLess = (mid -1)))) {
433                 // If there is only one pair it cannot be split
434                 if (lo >= midOneLess)
435                     return null;
436                 // Shift the mid-point to align with the surrogate pair
437                 return new CodePointsSpliterator(array, lo, index = midOneLess, cs);
438             }
439             return new CodePointsSpliterator(array, lo, index = mid, cs);
440         }
441 
442         @Override
forEachRemaining(IntConsumer action)443         public void forEachRemaining(IntConsumer action) {
444             // BEGIN Android-changed: Pass String instead of byte[].
445             /*
446             byte[] a; int i, hi; // hoist accesses and checks from loop
447              */
448             String a; int i, hi; // hoist accesses and checks from loop
449             // END Android-changed: Pass String instead of byte[].
450             if (action == null)
451                 throw new NullPointerException();
452             // BEGIN Android-changed: Pass String instead of byte[].
453             /*
454             if (((a = array).length >> 1) >= (hi = fence) &&
455              */
456             if (((a = array).length()) >= (hi = fence) &&
457             // END Android-changed: Pass String instead of byte[].
458                 (i = index) >= 0 && i < (index = hi)) {
459                 do {
460                     i = advance(a, i, hi, action);
461                 } while (i < hi);
462             }
463         }
464 
465         @Override
tryAdvance(IntConsumer action)466         public boolean tryAdvance(IntConsumer action) {
467             if (action == null)
468                 throw new NullPointerException();
469             if (index >= 0 && index < fence) {
470                 index = advance(array, index, fence, action);
471                 return true;
472             }
473             return false;
474         }
475 
476         // Advance one code point from the index, i, and return the next
477         // index to advance from
478         // BEGIN Android-changed: Pass String instead of byte[].
479         /*
480         private static int advance(byte[] a, int i, int hi, IntConsumer action) {
481          */
advance(String a, int i, int hi, IntConsumer action)482         private static int advance(String a, int i, int hi, IntConsumer action) {
483         // END Android-changed: Pass String instead of byte[].
484             char c1 = charAt(a, i++);
485             int cp = c1;
486             if (Character.isHighSurrogate(c1) && i < hi) {
487                 char c2 = charAt(a, i);
488                 if (Character.isLowSurrogate(c2)) {
489                     i++;
490                     cp = Character.toCodePoint(c1, c2);
491                 }
492             }
493             action.accept(cp);
494             return i;
495         }
496 
497         @Override
estimateSize()498         public long estimateSize() { return (long)(fence - index); }
499 
500         @Override
characteristics()501         public int characteristics() {
502             return cs;
503         }
504     }
505 
506     // BEGIN Android-changed: Pass String instead of byte[].
507     /*
508     public static char charAt(byte[] value, int index) {
509      */
charAt(String value, int index)510     public static char charAt(String value, int index) {
511     // END Android-changed: Pass String instead of byte[].
512         checkIndex(index, value);
513         return getChar(value, index);
514     }
515 
516     // BEGIN Android-changed: Pass String instead of byte[].
517     /*
518     public static void checkIndex(int off, byte[] val) {
519      */
checkIndex(int off, String val)520     public static void checkIndex(int off, String val) {
521     // END Android-changed: Pass String instead of byte[].
522         String.checkIndex(off, length(val));
523     }
524 
525 }
526