1
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
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
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
238 if (value[i] != first) {
239 while (++i <= max && value[i] != first);
240 }
241
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
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++) {
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
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;
331 }
332
333
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
385 for (first = 0 ; first < len; first++) {
386 int cp = value[first] & 0xff;
387 if (cp != Character.toLowerCase(cp)) {
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);
399
400 for (int i = first; i < len; i++) {
401 int cp = value[i] & 0xff;
402 cp = Character.toLowerCase(cp);
403 if (!canEncode(cp)) {
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)) {
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
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
459 for (first = 0 ; first < len; first++ ) {
460 int cp = value[first] & 0xff;
461 if (cp != Character.toUpperCaseEx(cp)) {
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);
474
475 for (int i = first; i < len; i++) {
476 int cp = value[i] & 0xff;
477 cp = Character.toUpperCaseEx(cp);
478 if (!canEncode(cp)) {
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
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;
598 private final int fence;
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
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
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
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;
740 private final int fence;
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;
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