1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.util;
17
18 import io.netty5.util.concurrent.FastThreadLocal;
19 import io.netty5.util.internal.EmptyArrays;
20 import io.netty5.util.internal.PlatformDependent;
21
22 import java.nio.ByteBuffer;
23 import java.nio.CharBuffer;
24 import java.nio.charset.Charset;
25 import java.nio.charset.CharsetEncoder;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.List;
30 import java.util.regex.Pattern;
31 import java.util.regex.PatternSyntaxException;
32
33 import static io.netty5.util.internal.MathUtil.isOutOfBounds;
34 import static java.util.Objects.requireNonNull;
35
36
37
38
39
40
41
42
43
44
45
46
47 public final class AsciiString implements CharSequence, Comparable<CharSequence> {
48 public static final AsciiString EMPTY_STRING = cached("");
49 private static final char MAX_CHAR_VALUE = 255;
50
51 public static final int INDEX_NOT_FOUND = -1;
52
53 private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8;
54
55 private static final FastThreadLocal<List<AsciiString>> ARRAYLISTS = new FastThreadLocal<>() {
56 @Override
57 protected List<AsciiString> initialValue() {
58 return new ArrayList<>(DEFAULT_ARRAY_LIST_INITIAL_CAPACITY);
59 }
60 };
61
62
63
64
65 private final byte[] value;
66
67
68
69 private final int offset;
70
71
72
73
74 private final int length;
75
76
77
78 private int hash;
79
80
81
82 private String string;
83
84
85
86
87 public AsciiString(byte[] value) {
88 this(value, true);
89 }
90
91
92
93
94
95 public AsciiString(byte[] value, boolean copy) {
96 this(value, 0, value.length, copy);
97 }
98
99
100
101
102
103
104 public AsciiString(byte[] value, int start, int length, boolean copy) {
105 if (copy) {
106 this.value = Arrays.copyOfRange(value, start, start + length);
107 this.offset = 0;
108 } else {
109 if (isOutOfBounds(start, length, value.length)) {
110 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" +
111 length + ") <= " + "value.length(" + value.length + ')');
112 }
113 this.value = value;
114 this.offset = start;
115 }
116 this.length = length;
117 }
118
119
120
121
122
123 public AsciiString(ByteBuffer value) {
124 this(value, true);
125 }
126
127
128
129
130
131
132
133 public AsciiString(ByteBuffer value, boolean copy) {
134 this(value, value.position(), value.remaining(), copy);
135 }
136
137
138
139
140
141
142
143 public AsciiString(ByteBuffer value, int start, int length, boolean copy) {
144 if (isOutOfBounds(start, length, value.capacity())) {
145 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
146 + ") <= " + "value.capacity(" + value.capacity() + ')');
147 }
148
149 if (value.hasArray()) {
150 if (copy) {
151 final int bufferOffset = value.arrayOffset() + start;
152 this.value = Arrays.copyOfRange(value.array(), bufferOffset, bufferOffset + length);
153 offset = 0;
154 } else {
155 this.value = value.array();
156 this.offset = start;
157 }
158 } else {
159 this.value = PlatformDependent.allocateUninitializedArray(length);
160 int oldPos = value.position();
161 value.get(this.value, 0, length);
162 value.position(oldPos);
163 this.offset = 0;
164 }
165 this.length = length;
166 }
167
168
169
170
171 public AsciiString(char[] value) {
172 this(value, 0, value.length);
173 }
174
175
176
177
178
179 public AsciiString(char[] value, int start, int length) {
180 if (isOutOfBounds(start, length, value.length)) {
181 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
182 + ") <= " + "value.length(" + value.length + ')');
183 }
184
185 this.value = PlatformDependent.allocateUninitializedArray(length);
186 for (int i = 0, j = start; i < length; i++, j++) {
187 this.value[i] = c2b(value[j]);
188 }
189 this.offset = 0;
190 this.length = length;
191 }
192
193
194
195
196 public AsciiString(char[] value, Charset charset) {
197 this(value, charset, 0, value.length);
198 }
199
200
201
202
203
204 public AsciiString(char[] value, Charset charset, int start, int length) {
205 CharBuffer cbuf = CharBuffer.wrap(value, start, length);
206 CharsetEncoder encoder = CharsetUtil.encoder(charset);
207 ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
208 encoder.encode(cbuf, nativeBuffer, true);
209 final int bufferOffset = nativeBuffer.arrayOffset();
210 this.value = Arrays.copyOfRange(nativeBuffer.array(), bufferOffset, bufferOffset + nativeBuffer.position());
211 this.offset = 0;
212 this.length = this.value.length;
213 }
214
215
216
217
218 public AsciiString(CharSequence value) {
219 this(value, 0, value.length());
220 }
221
222
223
224
225
226 public AsciiString(CharSequence value, int start, int length) {
227 if (isOutOfBounds(start, length, value.length())) {
228 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
229 + ") <= " + "value.length(" + value.length() + ')');
230 }
231
232 this.value = PlatformDependent.allocateUninitializedArray(length);
233 for (int i = 0, j = start; i < length; i++, j++) {
234 this.value[i] = c2b(value.charAt(j));
235 }
236 this.offset = 0;
237 this.length = length;
238 }
239
240
241
242
243 public AsciiString(CharSequence value, Charset charset) {
244 this(value, charset, 0, value.length());
245 }
246
247
248
249
250
251 public AsciiString(CharSequence value, Charset charset, int start, int length) {
252 CharBuffer cbuf = CharBuffer.wrap(value, start, start + length);
253 CharsetEncoder encoder = CharsetUtil.encoder(charset);
254 ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
255 encoder.encode(cbuf, nativeBuffer, true);
256 final int offset = nativeBuffer.arrayOffset();
257 this.value = Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position());
258 this.offset = 0;
259 this.length = this.value.length;
260 }
261
262
263
264
265
266
267
268 public int forEachByte(ByteProcessor visitor) {
269 return forEachByte0(0, length(), visitor);
270 }
271
272
273
274
275
276
277
278
279 public int forEachByte(int index, int length, ByteProcessor visitor) {
280 if (isOutOfBounds(index, length, length())) {
281 throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length
282 + ") <= " + "length(" + length() + ')');
283 }
284 return forEachByte0(index, length, visitor);
285 }
286
287 private int forEachByte0(int index, int length, ByteProcessor visitor) {
288 final int len = offset + index + length;
289 for (int i = offset + index; i < len; ++i) {
290 if (!visitor.process(value[i])) {
291 return i - offset;
292 }
293 }
294 return -1;
295 }
296
297
298
299
300
301
302
303 public int forEachByteDesc(ByteProcessor visitor) {
304 return forEachByteDesc0(0, length(), visitor);
305 }
306
307
308
309
310
311
312
313
314 public int forEachByteDesc(int index, int length, ByteProcessor visitor) {
315 if (isOutOfBounds(index, length, length())) {
316 throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length
317 + ") <= " + "length(" + length() + ')');
318 }
319 return forEachByteDesc0(index, length, visitor);
320 }
321
322 private int forEachByteDesc0(int index, int length, ByteProcessor visitor) {
323 final int end = offset + index;
324 for (int i = offset + index + length - 1; i >= end; --i) {
325 if (!visitor.process(value[i])) {
326 return i - offset;
327 }
328 }
329 return -1;
330 }
331
332 public byte byteAt(int index) {
333
334
335 if (index < 0 || index >= length) {
336 throw new IndexOutOfBoundsException("index: " + index + " must be in the range [0," + length + ")");
337 }
338
339 if (PlatformDependent.hasUnsafe()) {
340 return PlatformDependent.getByte(value, index + offset);
341 }
342 return value[index + offset];
343 }
344
345
346
347
348 public boolean isEmpty() {
349 return length == 0;
350 }
351
352
353
354
355 @Override
356 public int length() {
357 return length;
358 }
359
360
361
362
363
364 public void arrayChanged() {
365 string = null;
366 hash = 0;
367 }
368
369
370
371
372
373
374
375
376 public byte[] array() {
377 return value;
378 }
379
380
381
382
383
384
385 public int arrayOffset() {
386 return offset;
387 }
388
389
390
391
392
393 public boolean isEntireArrayUsed() {
394 return offset == 0 && length == value.length;
395 }
396
397
398
399
400 public byte[] toByteArray() {
401 return toByteArray(0, length());
402 }
403
404
405
406
407
408 public byte[] toByteArray(int start, int end) {
409 return Arrays.copyOfRange(value, start + offset, end + offset);
410 }
411
412
413
414
415
416
417
418
419
420 public void copy(int srcIdx, byte[] dst, int dstIdx, int length) {
421 if (isOutOfBounds(srcIdx, length, length())) {
422 throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
423 + length + ") <= srcLen(" + length() + ')');
424 }
425
426 System.arraycopy(value, srcIdx + offset, requireNonNull(dst, "dst"), dstIdx, length);
427 }
428
429 @Override
430 public char charAt(int index) {
431 return b2c(byteAt(index));
432 }
433
434
435
436
437
438
439
440 public boolean contains(CharSequence cs) {
441 return indexOf(cs) >= 0;
442 }
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457 @Override
458 public int compareTo(CharSequence string) {
459 if (this == string) {
460 return 0;
461 }
462
463 int result;
464 int length1 = length();
465 int length2 = string.length();
466 int minLength = Math.min(length1, length2);
467 for (int i = 0, j = arrayOffset(); i < minLength; i++, j++) {
468 result = b2c(value[j]) - string.charAt(i);
469 if (result != 0) {
470 return result;
471 }
472 }
473
474 return length1 - length2;
475 }
476
477
478
479
480
481
482
483 public AsciiString concat(CharSequence string) {
484 int thisLen = length();
485 int thatLen = string.length();
486 if (thatLen == 0) {
487 return this;
488 }
489
490 if (string instanceof AsciiString) {
491 AsciiString that = (AsciiString) string;
492 if (isEmpty()) {
493 return that;
494 }
495
496 byte[] newValue = PlatformDependent.allocateUninitializedArray(thisLen + thatLen);
497 System.arraycopy(value, arrayOffset(), newValue, 0, thisLen);
498 System.arraycopy(that.value, that.arrayOffset(), newValue, thisLen, thatLen);
499 return new AsciiString(newValue, false);
500 }
501
502 if (isEmpty()) {
503 return new AsciiString(string);
504 }
505
506 byte[] newValue = PlatformDependent.allocateUninitializedArray(thisLen + thatLen);
507 System.arraycopy(value, arrayOffset(), newValue, 0, thisLen);
508 for (int i = thisLen, j = 0; i < newValue.length; i++, j++) {
509 newValue[i] = c2b(string.charAt(j));
510 }
511
512 return new AsciiString(newValue, false);
513 }
514
515
516
517
518
519
520
521
522 public boolean endsWith(CharSequence suffix) {
523 int suffixLen = suffix.length();
524 return regionMatches(length() - suffixLen, suffix, 0, suffixLen);
525 }
526
527
528
529
530
531
532
533
534 public boolean contentEqualsIgnoreCase(CharSequence string) {
535 if (this == string) {
536 return true;
537 }
538
539 if (string == null || string.length() != length()) {
540 return false;
541 }
542
543 if (string instanceof AsciiString) {
544 AsciiString rhs = (AsciiString) string;
545 for (int i = arrayOffset(), j = rhs.arrayOffset(), end = i + length(); i < end; ++i, ++j) {
546 if (!equalsIgnoreCase(value[i], rhs.value[j])) {
547 return false;
548 }
549 }
550 return true;
551 }
552
553 for (int i = arrayOffset(), j = 0, end = length(); j < end; ++i, ++j) {
554 if (!equalsIgnoreCase(b2c(value[i]), string.charAt(j))) {
555 return false;
556 }
557 }
558 return true;
559 }
560
561
562
563
564
565
566 public char[] toCharArray() {
567 return toCharArray(0, length());
568 }
569
570
571
572
573
574
575 public char[] toCharArray(int start, int end) {
576 int length = end - start;
577 if (length == 0) {
578 return EmptyArrays.EMPTY_CHARS;
579 }
580
581 if (isOutOfBounds(start, length, length())) {
582 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
583 + length + ") <= srcLen(" + length() + ')');
584 }
585
586 final char[] buffer = new char[length];
587 for (int i = 0, j = start + arrayOffset(); i < length; i++, j++) {
588 buffer[i] = b2c(value[j]);
589 }
590 return buffer;
591 }
592
593
594
595
596
597
598
599
600
601 public void copy(int srcIdx, char[] dst, int dstIdx, int length) {
602 requireNonNull(dst, "dst");
603
604 if (isOutOfBounds(srcIdx, length, length())) {
605 throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
606 + length + ") <= srcLen(" + length() + ')');
607 }
608
609 final int dstEnd = dstIdx + length;
610 for (int i = dstIdx, j = srcIdx + arrayOffset(); i < dstEnd; i++, j++) {
611 dst[i] = b2c(value[j]);
612 }
613 }
614
615
616
617
618
619
620
621 public AsciiString subSequence(int start) {
622 return subSequence(start, length());
623 }
624
625
626
627
628
629
630
631
632 @Override
633 public AsciiString subSequence(int start, int end) {
634 return subSequence(start, end, true);
635 }
636
637
638
639
640
641
642
643
644
645
646 public AsciiString subSequence(int start, int end, boolean copy) {
647 if (isOutOfBounds(start, end - start, length())) {
648 throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length("
649 + length() + ')');
650 }
651
652 if (start == 0 && end == length()) {
653 return this;
654 }
655
656 if (end == start) {
657 return EMPTY_STRING;
658 }
659
660 return new AsciiString(value, start + offset, end - start, copy);
661 }
662
663
664
665
666
667
668
669
670
671
672 public int indexOf(CharSequence string) {
673 return indexOf(string, 0);
674 }
675
676
677
678
679
680
681
682
683
684
685
686 public int indexOf(CharSequence subString, int start) {
687 final int subCount = subString.length();
688 if (start < 0) {
689 start = 0;
690 }
691 if (subCount <= 0) {
692 return start < length ? start : length;
693 }
694 if (subCount > length - start) {
695 return INDEX_NOT_FOUND;
696 }
697
698 final char firstChar = subString.charAt(0);
699 if (firstChar > MAX_CHAR_VALUE) {
700 return INDEX_NOT_FOUND;
701 }
702 final byte firstCharAsByte = c2b0(firstChar);
703 final int len = offset + length - subCount;
704 for (int i = start + offset; i <= len; ++i) {
705 if (value[i] == firstCharAsByte) {
706 int o1 = i, o2 = 0;
707 while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
708
709 }
710 if (o2 == subCount) {
711 return i - offset;
712 }
713 }
714 }
715 return INDEX_NOT_FOUND;
716 }
717
718
719
720
721
722
723
724
725
726
727 public int indexOf(char ch, int start) {
728 if (ch > MAX_CHAR_VALUE) {
729 return INDEX_NOT_FOUND;
730 }
731
732 if (start < 0) {
733 start = 0;
734 }
735
736 final byte chAsByte = c2b0(ch);
737 final int len = offset + length;
738 for (int i = start + offset; i < len; ++i) {
739 if (value[i] == chAsByte) {
740 return i - offset;
741 }
742 }
743 return INDEX_NOT_FOUND;
744 }
745
746
747
748
749
750
751
752
753
754
755 public int lastIndexOf(CharSequence string) {
756
757 return lastIndexOf(string, length);
758 }
759
760
761
762
763
764
765
766
767
768
769
770 public int lastIndexOf(CharSequence subString, int start) {
771 final int subCount = subString.length();
772 start = Math.min(start, length - subCount);
773 if (start < 0) {
774 return INDEX_NOT_FOUND;
775 }
776 if (subCount == 0) {
777 return start;
778 }
779
780 final char firstChar = subString.charAt(0);
781 if (firstChar > MAX_CHAR_VALUE) {
782 return INDEX_NOT_FOUND;
783 }
784 final byte firstCharAsByte = c2b0(firstChar);
785 for (int i = offset + start; i >= 0; --i) {
786 if (value[i] == firstCharAsByte) {
787 int o1 = i, o2 = 0;
788 while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
789
790 }
791 if (o2 == subCount) {
792 return i - offset;
793 }
794 }
795 }
796 return INDEX_NOT_FOUND;
797 }
798
799
800
801
802
803
804
805
806
807
808
809
810 public boolean regionMatches(int thisStart, CharSequence string, int start, int length) {
811 requireNonNull(string, "string");
812
813 if (start < 0 || string.length() - start < length) {
814 return false;
815 }
816
817 final int thisLen = length();
818 if (thisStart < 0 || thisLen - thisStart < length) {
819 return false;
820 }
821
822 if (length <= 0) {
823 return true;
824 }
825
826 final int thatEnd = start + length;
827 for (int i = start, j = thisStart + arrayOffset(); i < thatEnd; i++, j++) {
828 if (b2c(value[j]) != string.charAt(i)) {
829 return false;
830 }
831 }
832 return true;
833 }
834
835
836
837
838
839
840
841
842
843
844
845
846
847 public boolean regionMatches(boolean ignoreCase, int thisStart, CharSequence string, int start, int length) {
848 if (!ignoreCase) {
849 return regionMatches(thisStart, string, start, length);
850 }
851
852 requireNonNull(string, "string");
853
854 final int thisLen = length();
855 if (thisStart < 0 || length > thisLen - thisStart) {
856 return false;
857 }
858 if (start < 0 || length > string.length() - start) {
859 return false;
860 }
861
862 thisStart += arrayOffset();
863 final int thisEnd = thisStart + length;
864 while (thisStart < thisEnd) {
865 if (!equalsIgnoreCase(b2c(value[thisStart++]), string.charAt(start++))) {
866 return false;
867 }
868 }
869 return true;
870 }
871
872
873
874
875
876
877
878
879 public AsciiString replace(char oldChar, char newChar) {
880 if (oldChar > MAX_CHAR_VALUE) {
881 return this;
882 }
883
884 final byte oldCharAsByte = c2b0(oldChar);
885 final byte newCharAsByte = c2b(newChar);
886 final int len = offset + length;
887 for (int i = offset; i < len; ++i) {
888 if (value[i] == oldCharAsByte) {
889 byte[] buffer = PlatformDependent.allocateUninitializedArray(length());
890 System.arraycopy(value, offset, buffer, 0, i - offset);
891 buffer[i - offset] = newCharAsByte;
892 ++i;
893 for (; i < len; ++i) {
894 byte oldValue = value[i];
895 buffer[i - offset] = oldValue != oldCharAsByte ? oldValue : newCharAsByte;
896 }
897 return new AsciiString(buffer, false);
898 }
899 }
900 return this;
901 }
902
903
904
905
906
907
908
909
910 public boolean startsWith(CharSequence prefix) {
911 return startsWith(prefix, 0);
912 }
913
914
915
916
917
918
919
920
921
922
923
924 public boolean startsWith(CharSequence prefix, int start) {
925 return regionMatches(start, prefix, 0, prefix.length());
926 }
927
928
929
930
931
932
933 public AsciiString toLowerCase() {
934 boolean lowercased = true;
935 int i, j;
936 final int len = length() + arrayOffset();
937 for (i = arrayOffset(); i < len; ++i) {
938 byte b = value[i];
939 if (b >= 'A' && b <= 'Z') {
940 lowercased = false;
941 break;
942 }
943 }
944
945
946 if (lowercased) {
947 return this;
948 }
949
950 final byte[] newValue = PlatformDependent.allocateUninitializedArray(length());
951 for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) {
952 newValue[i] = toLowerCase(value[j]);
953 }
954
955 return new AsciiString(newValue, false);
956 }
957
958
959
960
961
962
963 public AsciiString toUpperCase() {
964 boolean uppercased = true;
965 int i, j;
966 final int len = length() + arrayOffset();
967 for (i = arrayOffset(); i < len; ++i) {
968 byte b = value[i];
969 if (b >= 'a' && b <= 'z') {
970 uppercased = false;
971 break;
972 }
973 }
974
975
976 if (uppercased) {
977 return this;
978 }
979
980 final byte[] newValue = PlatformDependent.allocateUninitializedArray(length());
981 for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) {
982 newValue[i] = toUpperCase(value[j]);
983 }
984
985 return new AsciiString(newValue, false);
986 }
987
988
989
990
991
992
993
994
995 public static CharSequence trim(CharSequence c) {
996 if (c instanceof AsciiString) {
997 return ((AsciiString) c).trim();
998 }
999 if (c instanceof String) {
1000 return ((String) c).trim();
1001 }
1002 int start = 0, last = c.length() - 1;
1003 int end = last;
1004 while (start <= end && c.charAt(start) <= ' ') {
1005 start++;
1006 }
1007 while (end >= start && c.charAt(end) <= ' ') {
1008 end--;
1009 }
1010 if (start == 0 && end == last) {
1011 return c;
1012 }
1013 return c.subSequence(start, end);
1014 }
1015
1016
1017
1018
1019
1020
1021
1022 public AsciiString trim() {
1023 int start = arrayOffset(), last = arrayOffset() + length() - 1;
1024 int end = last;
1025 while (start <= end && value[start] <= ' ') {
1026 start++;
1027 }
1028 while (end >= start && value[end] <= ' ') {
1029 end--;
1030 }
1031 if (start == 0 && end == last) {
1032 return this;
1033 }
1034 return new AsciiString(value, start, end - start + 1, false);
1035 }
1036
1037
1038
1039
1040
1041
1042
1043 public boolean contentEquals(CharSequence a) {
1044 if (this == a) {
1045 return true;
1046 }
1047
1048 if (a == null || a.length() != length()) {
1049 return false;
1050 }
1051 if (a instanceof AsciiString) {
1052 return equals(a);
1053 }
1054
1055 for (int i = arrayOffset(), j = 0; j < a.length(); ++i, ++j) {
1056 if (b2c(value[i]) != a.charAt(j)) {
1057 return false;
1058 }
1059 }
1060 return true;
1061 }
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071 public boolean matches(String expr) {
1072 return Pattern.matches(expr, this);
1073 }
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086 public AsciiString[] split(String expr, int max) {
1087 return toAsciiStringArray(Pattern.compile(expr).split(this, max));
1088 }
1089
1090
1091
1092
1093 public AsciiString[] split(char delim) {
1094 final List<AsciiString> res = ARRAYLISTS.get();
1095 res.clear();
1096
1097 int start = 0;
1098 final int length = length();
1099 for (int i = start; i < length; i++) {
1100 if (charAt(i) == delim) {
1101 if (start == i) {
1102 res.add(EMPTY_STRING);
1103 } else {
1104 res.add(new AsciiString(value, start + arrayOffset(), i - start, false));
1105 }
1106 start = i + 1;
1107 }
1108 }
1109
1110 if (start == 0) {
1111 res.add(this);
1112 } else {
1113 if (start != length) {
1114
1115 res.add(new AsciiString(value, start + arrayOffset(), length - start, false));
1116 } else {
1117
1118 for (int i = res.size() - 1; i >= 0; i--) {
1119 if (res.get(i).isEmpty()) {
1120 res.remove(i);
1121 } else {
1122 break;
1123 }
1124 }
1125 }
1126 }
1127
1128 return res.toArray(new AsciiString[0]);
1129 }
1130
1131
1132
1133
1134
1135
1136 @Override
1137 public int hashCode() {
1138 int h = hash;
1139 if (h == 0) {
1140 h = PlatformDependent.hashCodeAscii(value, offset, length);
1141 hash = h;
1142 }
1143 return h;
1144 }
1145
1146 @Override
1147 public boolean equals(Object obj) {
1148 if (obj == null || obj.getClass() != AsciiString.class) {
1149 return false;
1150 }
1151 if (this == obj) {
1152 return true;
1153 }
1154
1155 AsciiString other = (AsciiString) obj;
1156 return length() == other.length() &&
1157 hashCode() == other.hashCode() &&
1158 PlatformDependent.equals(array(), arrayOffset(), other.array(), other.arrayOffset(), length());
1159 }
1160
1161
1162
1163
1164
1165 @Override
1166 public String toString() {
1167 String cache = string;
1168 if (cache == null) {
1169 cache = toString(0);
1170 string = cache;
1171 }
1172 return cache;
1173 }
1174
1175
1176
1177
1178
1179 public String toString(int start) {
1180 return toString(start, length());
1181 }
1182
1183
1184
1185
1186 public String toString(int start, int end) {
1187 int length = end - start;
1188 if (length == 0) {
1189 return "";
1190 }
1191
1192 if (isOutOfBounds(start, length, length())) {
1193 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
1194 + length + ") <= srcLen(" + length() + ')');
1195 }
1196
1197 @SuppressWarnings("deprecation")
1198 final String str = new String(value, 0, start + offset, length);
1199 return str;
1200 }
1201
1202 public boolean parseBoolean() {
1203 return length >= 1 && value[offset] != 0;
1204 }
1205
1206 public char parseChar() {
1207 return parseChar(0);
1208 }
1209
1210 public char parseChar(int start) {
1211 if (start + 1 >= length()) {
1212 throw new IndexOutOfBoundsException("2 bytes required to convert to character. index " +
1213 start + " would go out of bounds.");
1214 }
1215 final int startWithOffset = start + offset;
1216 return (char) ((b2c(value[startWithOffset]) << 8) | b2c(value[startWithOffset + 1]));
1217 }
1218
1219 public short parseShort() {
1220 return parseShort(0, length(), 10);
1221 }
1222
1223 public short parseShort(int radix) {
1224 return parseShort(0, length(), radix);
1225 }
1226
1227 public short parseShort(int start, int end) {
1228 return parseShort(start, end, 10);
1229 }
1230
1231 public short parseShort(int start, int end, int radix) {
1232 int intValue = parseInt(start, end, radix);
1233 short result = (short) intValue;
1234 if (result != intValue) {
1235 throw new NumberFormatException(subSequence(start, end, false).toString());
1236 }
1237 return result;
1238 }
1239
1240 public int parseInt() {
1241 return parseInt(0, length(), 10);
1242 }
1243
1244 public int parseInt(int radix) {
1245 return parseInt(0, length(), radix);
1246 }
1247
1248 public int parseInt(int start, int end) {
1249 return parseInt(start, end, 10);
1250 }
1251
1252 public int parseInt(int start, int end, int radix) {
1253 if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1254 throw new NumberFormatException();
1255 }
1256
1257 if (start == end) {
1258 throw new NumberFormatException();
1259 }
1260
1261 int i = start;
1262 boolean negative = byteAt(i) == '-';
1263 if (negative && ++i == end) {
1264 throw new NumberFormatException(subSequence(start, end, false).toString());
1265 }
1266
1267 return parseInt(i, end, radix, negative);
1268 }
1269
1270 private int parseInt(int start, int end, int radix, boolean negative) {
1271 int max = Integer.MIN_VALUE / radix;
1272 int result = 0;
1273 int currOffset = start;
1274 while (currOffset < end) {
1275 int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
1276 if (digit == -1) {
1277 throw new NumberFormatException(subSequence(start, end, false).toString());
1278 }
1279 if (max > result) {
1280 throw new NumberFormatException(subSequence(start, end, false).toString());
1281 }
1282 int next = result * radix - digit;
1283 if (next > result) {
1284 throw new NumberFormatException(subSequence(start, end, false).toString());
1285 }
1286 result = next;
1287 }
1288 if (!negative) {
1289 result = -result;
1290 if (result < 0) {
1291 throw new NumberFormatException(subSequence(start, end, false).toString());
1292 }
1293 }
1294 return result;
1295 }
1296
1297 public long parseLong() {
1298 return parseLong(0, length(), 10);
1299 }
1300
1301 public long parseLong(int radix) {
1302 return parseLong(0, length(), radix);
1303 }
1304
1305 public long parseLong(int start, int end) {
1306 return parseLong(start, end, 10);
1307 }
1308
1309 public long parseLong(int start, int end, int radix) {
1310 if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1311 throw new NumberFormatException();
1312 }
1313
1314 if (start == end) {
1315 throw new NumberFormatException();
1316 }
1317
1318 int i = start;
1319 boolean negative = byteAt(i) == '-';
1320 if (negative && ++i == end) {
1321 throw new NumberFormatException(subSequence(start, end, false).toString());
1322 }
1323
1324 return parseLong(i, end, radix, negative);
1325 }
1326
1327 private long parseLong(int start, int end, int radix, boolean negative) {
1328 long max = Long.MIN_VALUE / radix;
1329 long result = 0;
1330 int currOffset = start;
1331 while (currOffset < end) {
1332 int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
1333 if (digit == -1) {
1334 throw new NumberFormatException(subSequence(start, end, false).toString());
1335 }
1336 if (max > result) {
1337 throw new NumberFormatException(subSequence(start, end, false).toString());
1338 }
1339 long next = result * radix - digit;
1340 if (next > result) {
1341 throw new NumberFormatException(subSequence(start, end, false).toString());
1342 }
1343 result = next;
1344 }
1345 if (!negative) {
1346 result = -result;
1347 if (result < 0) {
1348 throw new NumberFormatException(subSequence(start, end, false).toString());
1349 }
1350 }
1351 return result;
1352 }
1353
1354 public float parseFloat() {
1355 return parseFloat(0, length());
1356 }
1357
1358 public float parseFloat(int start, int end) {
1359 return Float.parseFloat(toString(start, end));
1360 }
1361
1362 public double parseDouble() {
1363 return parseDouble(0, length());
1364 }
1365
1366 public double parseDouble(int start, int end) {
1367 return Double.parseDouble(toString(start, end));
1368 }
1369
1370 public static final HashingStrategy<CharSequence> CASE_INSENSITIVE_HASHER =
1371 new HashingStrategy<>() {
1372 @Override
1373 public int hashCode(CharSequence o) {
1374 return AsciiString.hashCode(o);
1375 }
1376
1377 @Override
1378 public boolean equals(CharSequence a, CharSequence b) {
1379 return AsciiString.contentEqualsIgnoreCase(a, b);
1380 }
1381 };
1382
1383 public static final HashingStrategy<CharSequence> CASE_SENSITIVE_HASHER =
1384 new HashingStrategy<>() {
1385 @Override
1386 public int hashCode(CharSequence o) {
1387 return AsciiString.hashCode(o);
1388 }
1389
1390 @Override
1391 public boolean equals(CharSequence a, CharSequence b) {
1392 return AsciiString.contentEquals(a, b);
1393 }
1394 };
1395
1396
1397
1398
1399
1400 public static AsciiString of(CharSequence string) {
1401 return string instanceof AsciiString ? (AsciiString) string : new AsciiString(string);
1402 }
1403
1404
1405
1406
1407
1408
1409
1410 public static AsciiString cached(String string) {
1411 AsciiString asciiString = new AsciiString(string);
1412 asciiString.string = string;
1413 return asciiString;
1414 }
1415
1416
1417
1418
1419
1420
1421 public static int hashCode(CharSequence value) {
1422 if (value == null) {
1423 return 0;
1424 }
1425 if (value instanceof AsciiString) {
1426 return value.hashCode();
1427 }
1428
1429 return PlatformDependent.hashCodeAscii(value);
1430 }
1431
1432
1433
1434
1435 public static boolean contains(CharSequence a, CharSequence b) {
1436 return contains(a, b, DefaultCharEqualityComparator.INSTANCE);
1437 }
1438
1439
1440
1441
1442 public static boolean containsIgnoreCase(CharSequence a, CharSequence b) {
1443 return contains(a, b, AsciiCaseInsensitiveCharEqualityComparator.INSTANCE);
1444 }
1445
1446
1447
1448
1449
1450 public static boolean contentEqualsIgnoreCase(CharSequence a, CharSequence b) {
1451 if (a == null || b == null) {
1452 return a == b;
1453 }
1454
1455 if (a instanceof AsciiString) {
1456 return ((AsciiString) a).contentEqualsIgnoreCase(b);
1457 }
1458 if (b instanceof AsciiString) {
1459 return ((AsciiString) b).contentEqualsIgnoreCase(a);
1460 }
1461
1462 if (a.length() != b.length()) {
1463 return false;
1464 }
1465 for (int i = 0; i < a.length(); ++i) {
1466 if (!equalsIgnoreCase(a.charAt(i), b.charAt(i))) {
1467 return false;
1468 }
1469 }
1470 return true;
1471 }
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482 public static boolean containsContentEqualsIgnoreCase(Collection<CharSequence> collection, CharSequence value) {
1483 for (CharSequence v : collection) {
1484 if (contentEqualsIgnoreCase(value, v)) {
1485 return true;
1486 }
1487 }
1488 return false;
1489 }
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500 public static boolean containsAllContentEqualsIgnoreCase(Collection<CharSequence> a, Collection<CharSequence> b) {
1501 for (CharSequence v : b) {
1502 if (!containsContentEqualsIgnoreCase(a, v)) {
1503 return false;
1504 }
1505 }
1506 return true;
1507 }
1508
1509
1510
1511
1512 public static boolean contentEquals(CharSequence a, CharSequence b) {
1513 if (a == null || b == null) {
1514 return a == b;
1515 }
1516
1517 if (a instanceof AsciiString) {
1518 return ((AsciiString) a).contentEquals(b);
1519 }
1520
1521 if (b instanceof AsciiString) {
1522 return ((AsciiString) b).contentEquals(a);
1523 }
1524
1525 if (a.length() != b.length()) {
1526 return false;
1527 }
1528 for (int i = 0; i < a.length(); ++i) {
1529 if (a.charAt(i) != b.charAt(i)) {
1530 return false;
1531 }
1532 }
1533 return true;
1534 }
1535
1536 private static AsciiString[] toAsciiStringArray(String[] jdkResult) {
1537 AsciiString[] res = new AsciiString[jdkResult.length];
1538 for (int i = 0; i < jdkResult.length; i++) {
1539 res[i] = new AsciiString(jdkResult[i]);
1540 }
1541 return res;
1542 }
1543
1544 private interface CharEqualityComparator {
1545 boolean equals(char a, char b);
1546 }
1547
1548 private static final class DefaultCharEqualityComparator implements CharEqualityComparator {
1549 static final DefaultCharEqualityComparator INSTANCE = new DefaultCharEqualityComparator();
1550 private DefaultCharEqualityComparator() { }
1551
1552 @Override
1553 public boolean equals(char a, char b) {
1554 return a == b;
1555 }
1556 }
1557
1558 private static final class AsciiCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1559 static final AsciiCaseInsensitiveCharEqualityComparator
1560 INSTANCE = new AsciiCaseInsensitiveCharEqualityComparator();
1561 private AsciiCaseInsensitiveCharEqualityComparator() { }
1562
1563 @Override
1564 public boolean equals(char a, char b) {
1565 return equalsIgnoreCase(a, b);
1566 }
1567 }
1568
1569 private static final class GeneralCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1570 static final GeneralCaseInsensitiveCharEqualityComparator
1571 INSTANCE = new GeneralCaseInsensitiveCharEqualityComparator();
1572 private GeneralCaseInsensitiveCharEqualityComparator() { }
1573
1574 @Override
1575 public boolean equals(char a, char b) {
1576
1577 return Character.toUpperCase(a) == Character.toUpperCase(b) ||
1578 Character.toLowerCase(a) == Character.toLowerCase(b);
1579 }
1580 }
1581
1582 private static boolean contains(CharSequence a, CharSequence b, CharEqualityComparator cmp) {
1583 if (a == null || b == null || a.length() < b.length()) {
1584 return false;
1585 }
1586 if (b.length() == 0) {
1587 return true;
1588 }
1589 int bStart = 0;
1590 for (int i = 0; i < a.length(); ++i) {
1591 if (cmp.equals(b.charAt(bStart), a.charAt(i))) {
1592
1593 if (++bStart == b.length()) {
1594 return true;
1595 }
1596 } else if (a.length() - i < b.length()) {
1597
1598 return false;
1599 } else {
1600 bStart = 0;
1601 }
1602 }
1603 return false;
1604 }
1605
1606 private static boolean regionMatchesCharSequences(final CharSequence cs, final int csStart,
1607 final CharSequence string, final int start, final int length,
1608 CharEqualityComparator charEqualityComparator) {
1609
1610 if (csStart < 0 || length > cs.length() - csStart) {
1611 return false;
1612 }
1613 if (start < 0 || length > string.length() - start) {
1614 return false;
1615 }
1616
1617 int csIndex = csStart;
1618 int csEnd = csIndex + length;
1619 int stringIndex = start;
1620
1621 while (csIndex < csEnd) {
1622 char c1 = cs.charAt(csIndex++);
1623 char c2 = string.charAt(stringIndex++);
1624
1625 if (!charEqualityComparator.equals(c1, c2)) {
1626 return false;
1627 }
1628 }
1629 return true;
1630 }
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642 public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int csStart,
1643 final CharSequence string, final int start, final int length) {
1644 if (cs == null || string == null) {
1645 return false;
1646 }
1647
1648 if (cs instanceof String && string instanceof String) {
1649 return ((String) cs).regionMatches(ignoreCase, csStart, (String) string, start, length);
1650 }
1651
1652 if (cs instanceof AsciiString) {
1653 return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1654 }
1655
1656 return regionMatchesCharSequences(cs, csStart, string, start, length,
1657 ignoreCase ? GeneralCaseInsensitiveCharEqualityComparator.INSTANCE :
1658 DefaultCharEqualityComparator.INSTANCE);
1659 }
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671 public static boolean regionMatchesAscii(final CharSequence cs, final boolean ignoreCase, final int csStart,
1672 final CharSequence string, final int start, final int length) {
1673 if (cs == null || string == null) {
1674 return false;
1675 }
1676
1677 if (!ignoreCase && cs instanceof String && string instanceof String) {
1678
1679
1680
1681 return ((String) cs).regionMatches(false, csStart, (String) string, start, length);
1682 }
1683
1684 if (cs instanceof AsciiString) {
1685 return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1686 }
1687
1688 return regionMatchesCharSequences(cs, csStart, string, start, length,
1689 ignoreCase ? AsciiCaseInsensitiveCharEqualityComparator.INSTANCE :
1690 DefaultCharEqualityComparator.INSTANCE);
1691 }
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723 public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) {
1724 if (str == null || searchStr == null) {
1725 return INDEX_NOT_FOUND;
1726 }
1727 if (startPos < 0) {
1728 startPos = 0;
1729 }
1730 int searchStrLen = searchStr.length();
1731 final int endLimit = str.length() - searchStrLen + 1;
1732 if (startPos > endLimit) {
1733 return INDEX_NOT_FOUND;
1734 }
1735 if (searchStrLen == 0) {
1736 return startPos;
1737 }
1738 for (int i = startPos; i < endLimit; i++) {
1739 if (regionMatches(str, true, i, searchStr, 0, searchStrLen)) {
1740 return i;
1741 }
1742 }
1743 return INDEX_NOT_FOUND;
1744 }
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776 public static int indexOfIgnoreCaseAscii(final CharSequence str, final CharSequence searchStr, int startPos) {
1777 if (str == null || searchStr == null) {
1778 return INDEX_NOT_FOUND;
1779 }
1780 if (startPos < 0) {
1781 startPos = 0;
1782 }
1783 int searchStrLen = searchStr.length();
1784 final int endLimit = str.length() - searchStrLen + 1;
1785 if (startPos > endLimit) {
1786 return INDEX_NOT_FOUND;
1787 }
1788 if (searchStrLen == 0) {
1789 return startPos;
1790 }
1791 for (int i = startPos; i < endLimit; i++) {
1792 if (regionMatchesAscii(str, true, i, searchStr, 0, searchStrLen)) {
1793 return i;
1794 }
1795 }
1796 return INDEX_NOT_FOUND;
1797 }
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810 public static int indexOf(final CharSequence cs, final char searchChar, int start) {
1811 if (cs instanceof String) {
1812 return ((String) cs).indexOf(searchChar, start);
1813 } else if (cs instanceof AsciiString) {
1814 return ((AsciiString) cs).indexOf(searchChar, start);
1815 }
1816 if (cs == null) {
1817 return INDEX_NOT_FOUND;
1818 }
1819 final int sz = cs.length();
1820 for (int i = start < 0 ? 0 : start; i < sz; i++) {
1821 if (cs.charAt(i) == searchChar) {
1822 return i;
1823 }
1824 }
1825 return INDEX_NOT_FOUND;
1826 }
1827
1828 private static boolean equalsIgnoreCase(byte a, byte b) {
1829 return a == b || toLowerCase(a) == toLowerCase(b);
1830 }
1831
1832 private static boolean equalsIgnoreCase(char a, char b) {
1833 return a == b || toLowerCase(a) == toLowerCase(b);
1834 }
1835
1836 private static byte toLowerCase(byte b) {
1837 return isUpperCase(b) ? (byte) (b + 32) : b;
1838 }
1839
1840
1841
1842
1843
1844
1845
1846 public static char toLowerCase(char c) {
1847 return isUpperCase(c) ? (char) (c + 32) : c;
1848 }
1849
1850 private static byte toUpperCase(byte b) {
1851 return isLowerCase(b) ? (byte) (b - 32) : b;
1852 }
1853
1854 private static boolean isLowerCase(byte value) {
1855 return value >= 'a' && value <= 'z';
1856 }
1857
1858 public static boolean isUpperCase(byte value) {
1859 return value >= 'A' && value <= 'Z';
1860 }
1861
1862 public static boolean isUpperCase(char value) {
1863 return value >= 'A' && value <= 'Z';
1864 }
1865
1866 public static byte c2b(char c) {
1867 return (byte) ((c > MAX_CHAR_VALUE) ? '?' : c);
1868 }
1869
1870 private static byte c2b0(char c) {
1871 return (byte) c;
1872 }
1873
1874 public static char b2c(byte b) {
1875 return (char) (b & 0xFF);
1876 }
1877 }