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