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 other = (AsciiString) string;
538 byte[] value = this.value;
539 if (offset == 0 && other.offset == 0 && length == value.length) {
540 byte[] otherValue = other.value;
541 for (int i = 0; i < value.length; ++i) {
542 if (!equalsIgnoreCase(value[i], otherValue[i])) {
543 return false;
544 }
545 }
546 return true;
547 }
548 return misalignedEqualsIgnoreCase(other);
549 }
550
551 byte[] value = this.value;
552 for (int i = offset, j = 0; j < string.length(); ++i, ++j) {
553 if (!equalsIgnoreCase(b2c(value[i]), string.charAt(j))) {
554 return false;
555 }
556 }
557 return true;
558 }
559
560 private boolean misalignedEqualsIgnoreCase(AsciiString other) {
561 byte[] value = this.value;
562 byte[] otherValue = other.value;
563 for (int i = offset, j = other.offset, end = offset + length; i < end; ++i, ++j) {
564 if (!equalsIgnoreCase(value[i], otherValue[j])) {
565 return false;
566 }
567 }
568 return true;
569 }
570
571
572
573
574
575
576 public char[] toCharArray() {
577 return toCharArray(0, length());
578 }
579
580
581
582
583
584
585 public char[] toCharArray(int start, int end) {
586 int length = end - start;
587 if (length == 0) {
588 return EmptyArrays.EMPTY_CHARS;
589 }
590
591 if (isOutOfBounds(start, length, length())) {
592 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
593 + length + ") <= srcLen(" + length() + ')');
594 }
595
596 final char[] buffer = new char[length];
597 for (int i = 0, j = start + arrayOffset(); i < length; i++, j++) {
598 buffer[i] = b2c(value[j]);
599 }
600 return buffer;
601 }
602
603
604
605
606
607
608
609
610
611 public void copy(int srcIdx, char[] dst, int dstIdx, int length) {
612 ObjectUtil.checkNotNull(dst, "dst");
613
614 if (isOutOfBounds(srcIdx, length, length())) {
615 throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
616 + length + ") <= srcLen(" + length() + ')');
617 }
618
619 final int dstEnd = dstIdx + length;
620 for (int i = dstIdx, j = srcIdx + arrayOffset(); i < dstEnd; i++, j++) {
621 dst[i] = b2c(value[j]);
622 }
623 }
624
625
626
627
628
629
630
631 public AsciiString subSequence(int start) {
632 return subSequence(start, length());
633 }
634
635
636
637
638
639
640
641
642 @Override
643 public AsciiString subSequence(int start, int end) {
644 return subSequence(start, end, true);
645 }
646
647
648
649
650
651
652
653
654
655
656 public AsciiString subSequence(int start, int end, boolean copy) {
657 if (isOutOfBounds(start, end - start, length())) {
658 throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length("
659 + length() + ')');
660 }
661
662 if (start == 0 && end == length()) {
663 return this;
664 }
665
666 if (end == start) {
667 return EMPTY_STRING;
668 }
669
670 return new AsciiString(value, start + offset, end - start, copy);
671 }
672
673
674
675
676
677
678
679
680
681
682 public int indexOf(CharSequence string) {
683 return indexOf(string, 0);
684 }
685
686
687
688
689
690
691
692
693
694
695
696 public int indexOf(CharSequence subString, int start) {
697 final int subCount = subString.length();
698 if (start < 0) {
699 start = 0;
700 }
701 if (subCount <= 0) {
702 return start < length ? start : length;
703 }
704 if (subCount > length - start) {
705 return INDEX_NOT_FOUND;
706 }
707
708 final char firstChar = subString.charAt(0);
709 if (firstChar > MAX_CHAR_VALUE) {
710 return INDEX_NOT_FOUND;
711 }
712 final byte firstCharAsByte = c2b0(firstChar);
713 final int len = offset + length - subCount;
714 for (int i = start + offset; i <= len; ++i) {
715 if (value[i] == firstCharAsByte) {
716 int o1 = i, o2 = 0;
717 while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
718
719 }
720 if (o2 == subCount) {
721 return i - offset;
722 }
723 }
724 }
725 return INDEX_NOT_FOUND;
726 }
727
728
729
730
731
732
733
734
735
736
737 public int indexOf(char ch, int start) {
738 if (ch > MAX_CHAR_VALUE) {
739 return INDEX_NOT_FOUND;
740 }
741
742 if (start < 0) {
743 start = 0;
744 }
745
746 final byte chAsByte = c2b0(ch);
747 final int len = offset + length;
748 for (int i = start + offset; i < len; ++i) {
749 if (value[i] == chAsByte) {
750 return i - offset;
751 }
752 }
753 return INDEX_NOT_FOUND;
754 }
755
756
757
758
759
760
761
762
763
764
765 public int lastIndexOf(CharSequence string) {
766
767 return lastIndexOf(string, length);
768 }
769
770
771
772
773
774
775
776
777
778
779
780 public int lastIndexOf(CharSequence subString, int start) {
781 final int subCount = subString.length();
782 start = Math.min(start, length - subCount);
783 if (start < 0) {
784 return INDEX_NOT_FOUND;
785 }
786 if (subCount == 0) {
787 return start;
788 }
789
790 final char firstChar = subString.charAt(0);
791 if (firstChar > MAX_CHAR_VALUE) {
792 return INDEX_NOT_FOUND;
793 }
794 final byte firstCharAsByte = c2b0(firstChar);
795 for (int i = offset + start; i >= offset; --i) {
796 if (value[i] == firstCharAsByte) {
797 int o1 = i, o2 = 0;
798 while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
799
800 }
801 if (o2 == subCount) {
802 return i - offset;
803 }
804 }
805 }
806 return INDEX_NOT_FOUND;
807 }
808
809
810
811
812
813
814
815
816
817
818
819
820 public boolean regionMatches(int thisStart, CharSequence string, int start, int length) {
821 ObjectUtil.checkNotNull(string, "string");
822
823 if (start < 0 || string.length() - start < length) {
824 return false;
825 }
826
827 final int thisLen = length();
828 if (thisStart < 0 || thisLen - thisStart < length) {
829 return false;
830 }
831
832 if (length <= 0) {
833 return true;
834 }
835
836 if (string instanceof AsciiString) {
837 final AsciiString asciiString = (AsciiString) string;
838 return PlatformDependent.equals(value, thisStart + offset, asciiString.value,
839 start + asciiString.offset, length);
840 }
841 final int thatEnd = start + length;
842 for (int i = start, j = thisStart + arrayOffset(); i < thatEnd; i++, j++) {
843 if (b2c(value[j]) != string.charAt(i)) {
844 return false;
845 }
846 }
847 return true;
848 }
849
850
851
852
853
854
855
856
857
858
859
860
861
862 public boolean regionMatches(boolean ignoreCase, int thisStart, CharSequence string, int start, int length) {
863 if (!ignoreCase) {
864 return regionMatches(thisStart, string, start, length);
865 }
866
867 ObjectUtil.checkNotNull(string, "string");
868
869 final int thisLen = length();
870 if (thisStart < 0 || length > thisLen - thisStart) {
871 return false;
872 }
873 if (start < 0 || length > string.length() - start) {
874 return false;
875 }
876
877 thisStart += arrayOffset();
878 final int thisEnd = thisStart + length;
879 if (string instanceof AsciiString) {
880 final AsciiString asciiString = (AsciiString) string;
881 final byte[] value = this.value;
882 final byte[] otherValue = asciiString.value;
883 start += asciiString.offset;
884 while (thisStart < thisEnd) {
885 if (!equalsIgnoreCase(value[thisStart++], otherValue[start++])) {
886 return false;
887 }
888 }
889 return true;
890 }
891 while (thisStart < thisEnd) {
892 if (!equalsIgnoreCase(b2c(value[thisStart++]), string.charAt(start++))) {
893 return false;
894 }
895 }
896 return true;
897 }
898
899
900
901
902
903
904
905
906 public AsciiString replace(char oldChar, char newChar) {
907 if (oldChar > MAX_CHAR_VALUE) {
908 return this;
909 }
910
911 final byte oldCharAsByte = c2b0(oldChar);
912 final byte newCharAsByte = c2b(newChar);
913 final int len = offset + length;
914 for (int i = offset; i < len; ++i) {
915 if (value[i] == oldCharAsByte) {
916 byte[] buffer = PlatformDependent.allocateUninitializedArray(length());
917 System.arraycopy(value, offset, buffer, 0, i - offset);
918 buffer[i - offset] = newCharAsByte;
919 ++i;
920 for (; i < len; ++i) {
921 byte oldValue = value[i];
922 buffer[i - offset] = oldValue != oldCharAsByte ? oldValue : newCharAsByte;
923 }
924 return new AsciiString(buffer, false);
925 }
926 }
927 return this;
928 }
929
930
931
932
933
934
935
936
937 public boolean startsWith(CharSequence prefix) {
938 return startsWith(prefix, 0);
939 }
940
941
942
943
944
945
946
947
948
949
950
951 public boolean startsWith(CharSequence prefix, int start) {
952 return regionMatches(start, prefix, 0, prefix.length());
953 }
954
955
956
957
958
959
960 public AsciiString toLowerCase() {
961 return AsciiStringUtil.toLowerCase(this);
962 }
963
964
965
966
967
968
969 public AsciiString toUpperCase() {
970 return AsciiStringUtil.toUpperCase(this);
971 }
972
973
974
975
976
977
978
979
980 public static CharSequence trim(CharSequence c) {
981 if (c instanceof AsciiString) {
982 return ((AsciiString) c).trim();
983 }
984 if (c instanceof String) {
985 return ((String) c).trim();
986 }
987 int start = 0, last = c.length() - 1;
988 int end = last;
989 while (start <= end && c.charAt(start) <= ' ') {
990 start++;
991 }
992 while (end >= start && c.charAt(end) <= ' ') {
993 end--;
994 }
995 if (start == 0 && end == last) {
996 return c;
997 }
998 return c.subSequence(start, end);
999 }
1000
1001
1002
1003
1004
1005
1006
1007 public AsciiString trim() {
1008 int start = arrayOffset(), last = arrayOffset() + length() - 1;
1009 int end = last;
1010 while (start <= end && value[start] <= ' ') {
1011 start++;
1012 }
1013 while (end >= start && value[end] <= ' ') {
1014 end--;
1015 }
1016 if (start == 0 && end == last) {
1017 return this;
1018 }
1019 return new AsciiString(value, start, end - start + 1, false);
1020 }
1021
1022
1023
1024
1025
1026
1027
1028 public boolean contentEquals(CharSequence a) {
1029 if (this == a) {
1030 return true;
1031 }
1032
1033 if (a == null || a.length() != length()) {
1034 return false;
1035 }
1036 if (a instanceof AsciiString) {
1037 return equals(a);
1038 }
1039
1040 for (int i = arrayOffset(), j = 0; j < a.length(); ++i, ++j) {
1041 if (b2c(value[i]) != a.charAt(j)) {
1042 return false;
1043 }
1044 }
1045 return true;
1046 }
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056 public boolean matches(String expr) {
1057 return Pattern.matches(expr, this);
1058 }
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071 public AsciiString[] split(String expr, int max) {
1072 return toAsciiStringArray(Pattern.compile(expr).split(this, max));
1073 }
1074
1075
1076
1077
1078 public AsciiString[] split(char delim) {
1079 final List<AsciiString> res = InternalThreadLocalMap.get().arrayList();
1080
1081 int start = 0;
1082 final int length = length();
1083 for (int i = start; i < length; i++) {
1084 if (charAt(i) == delim) {
1085 if (start == i) {
1086 res.add(EMPTY_STRING);
1087 } else {
1088 res.add(new AsciiString(value, start + arrayOffset(), i - start, false));
1089 }
1090 start = i + 1;
1091 }
1092 }
1093
1094 if (start == 0) {
1095 res.add(this);
1096 } else {
1097 if (start != length) {
1098
1099 res.add(new AsciiString(value, start + arrayOffset(), length - start, false));
1100 } else {
1101
1102 for (int i = res.size() - 1; i >= 0; i--) {
1103 if (res.get(i).isEmpty()) {
1104 res.remove(i);
1105 } else {
1106 break;
1107 }
1108 }
1109 }
1110 }
1111
1112 return res.toArray(EmptyArrays.EMPTY_ASCII_STRINGS);
1113 }
1114
1115
1116
1117
1118
1119
1120 @Override
1121 public int hashCode() {
1122 int h = hash;
1123 if (h == 0) {
1124 h = PlatformDependent.hashCodeAscii(value, offset, length);
1125 hash = h;
1126 }
1127 return h;
1128 }
1129
1130 @Override
1131 public boolean equals(Object obj) {
1132 if (obj == null || obj.getClass() != AsciiString.class) {
1133 return false;
1134 }
1135 if (this == obj) {
1136 return true;
1137 }
1138
1139 AsciiString other = (AsciiString) obj;
1140 return length() == other.length() &&
1141 hashCode() == other.hashCode() &&
1142 PlatformDependent.equals(array(), arrayOffset(), other.array(), other.arrayOffset(), length());
1143 }
1144
1145
1146
1147
1148
1149 @Override
1150 public String toString() {
1151 String cache = string;
1152 if (cache == null) {
1153 cache = toString(0);
1154 string = cache;
1155 }
1156 return cache;
1157 }
1158
1159
1160
1161
1162
1163 public String toString(int start) {
1164 return toString(start, length());
1165 }
1166
1167
1168
1169
1170 public String toString(int start, int end) {
1171 int length = end - start;
1172 if (length == 0) {
1173 return "";
1174 }
1175
1176 if (isOutOfBounds(start, length, length())) {
1177 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
1178 + length + ") <= srcLen(" + length() + ')');
1179 }
1180
1181 @SuppressWarnings("deprecation")
1182 final String str = new String(value, 0, start + offset, length);
1183 return str;
1184 }
1185
1186 public boolean parseBoolean() {
1187 return length >= 1 && value[offset] != 0;
1188 }
1189
1190 public char parseChar() {
1191 return parseChar(0);
1192 }
1193
1194 public char parseChar(int start) {
1195 if (start + 1 >= length()) {
1196 throw new IndexOutOfBoundsException("2 bytes required to convert to character. index " +
1197 start + " would go out of bounds.");
1198 }
1199 final int startWithOffset = start + offset;
1200 return (char) ((b2c(value[startWithOffset]) << 8) | b2c(value[startWithOffset + 1]));
1201 }
1202
1203 public short parseShort() {
1204 return parseShort(0, length(), 10);
1205 }
1206
1207 public short parseShort(int radix) {
1208 return parseShort(0, length(), radix);
1209 }
1210
1211 public short parseShort(int start, int end) {
1212 return parseShort(start, end, 10);
1213 }
1214
1215 public short parseShort(int start, int end, int radix) {
1216 int intValue = parseInt(start, end, radix);
1217 short result = (short) intValue;
1218 if (result != intValue) {
1219 throw new NumberFormatException(subSequence(start, end, false).toString());
1220 }
1221 return result;
1222 }
1223
1224 public int parseInt() {
1225 return parseInt(0, length(), 10);
1226 }
1227
1228 public int parseInt(int radix) {
1229 return parseInt(0, length(), radix);
1230 }
1231
1232 public int parseInt(int start, int end) {
1233 return parseInt(start, end, 10);
1234 }
1235
1236 public int parseInt(int start, int end, int radix) {
1237 if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1238 throw new NumberFormatException();
1239 }
1240
1241 if (start == end) {
1242 throw new NumberFormatException();
1243 }
1244
1245 int i = start;
1246 boolean negative = byteAt(i) == '-';
1247 if (negative && ++i == end) {
1248 throw new NumberFormatException(subSequence(start, end, false).toString());
1249 }
1250
1251 return parseInt(i, end, radix, negative);
1252 }
1253
1254 private int parseInt(int start, int end, int radix, boolean negative) {
1255 int max = Integer.MIN_VALUE / radix;
1256 int result = 0;
1257 int currOffset = start;
1258 while (currOffset < end) {
1259 int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
1260 if (digit == -1) {
1261 throw new NumberFormatException(subSequence(start, end, false).toString());
1262 }
1263 if (max > result) {
1264 throw new NumberFormatException(subSequence(start, end, false).toString());
1265 }
1266 int next = result * radix - digit;
1267 if (next > result) {
1268 throw new NumberFormatException(subSequence(start, end, false).toString());
1269 }
1270 result = next;
1271 }
1272 if (!negative) {
1273 result = -result;
1274 if (result < 0) {
1275 throw new NumberFormatException(subSequence(start, end, false).toString());
1276 }
1277 }
1278 return result;
1279 }
1280
1281 public long parseLong() {
1282 return parseLong(0, length(), 10);
1283 }
1284
1285 public long parseLong(int radix) {
1286 return parseLong(0, length(), radix);
1287 }
1288
1289 public long parseLong(int start, int end) {
1290 return parseLong(start, end, 10);
1291 }
1292
1293 public long parseLong(int start, int end, int radix) {
1294 if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1295 throw new NumberFormatException();
1296 }
1297
1298 if (start == end) {
1299 throw new NumberFormatException();
1300 }
1301
1302 int i = start;
1303 boolean negative = byteAt(i) == '-';
1304 if (negative && ++i == end) {
1305 throw new NumberFormatException(subSequence(start, end, false).toString());
1306 }
1307
1308 return parseLong(i, end, radix, negative);
1309 }
1310
1311 private long parseLong(int start, int end, int radix, boolean negative) {
1312 long max = Long.MIN_VALUE / radix;
1313 long result = 0;
1314 int currOffset = start;
1315 while (currOffset < end) {
1316 int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
1317 if (digit == -1) {
1318 throw new NumberFormatException(subSequence(start, end, false).toString());
1319 }
1320 if (max > result) {
1321 throw new NumberFormatException(subSequence(start, end, false).toString());
1322 }
1323 long next = result * radix - digit;
1324 if (next > result) {
1325 throw new NumberFormatException(subSequence(start, end, false).toString());
1326 }
1327 result = next;
1328 }
1329 if (!negative) {
1330 result = -result;
1331 if (result < 0) {
1332 throw new NumberFormatException(subSequence(start, end, false).toString());
1333 }
1334 }
1335 return result;
1336 }
1337
1338 public float parseFloat() {
1339 return parseFloat(0, length());
1340 }
1341
1342 public float parseFloat(int start, int end) {
1343 return Float.parseFloat(toString(start, end));
1344 }
1345
1346 public double parseDouble() {
1347 return parseDouble(0, length());
1348 }
1349
1350 public double parseDouble(int start, int end) {
1351 return Double.parseDouble(toString(start, end));
1352 }
1353
1354 public static final HashingStrategy<CharSequence> CASE_INSENSITIVE_HASHER =
1355 new HashingStrategy<CharSequence>() {
1356 @Override
1357 public int hashCode(CharSequence o) {
1358 return AsciiString.hashCode(o);
1359 }
1360
1361 @Override
1362 public boolean equals(CharSequence a, CharSequence b) {
1363 return AsciiString.contentEqualsIgnoreCase(a, b);
1364 }
1365 };
1366
1367 public static final HashingStrategy<CharSequence> CASE_SENSITIVE_HASHER =
1368 new HashingStrategy<CharSequence>() {
1369 @Override
1370 public int hashCode(CharSequence o) {
1371 return AsciiString.hashCode(o);
1372 }
1373
1374 @Override
1375 public boolean equals(CharSequence a, CharSequence b) {
1376 return AsciiString.contentEquals(a, b);
1377 }
1378 };
1379
1380
1381
1382
1383
1384 public static AsciiString of(CharSequence string) {
1385 return string instanceof AsciiString ? (AsciiString) string : new AsciiString(string);
1386 }
1387
1388
1389
1390
1391
1392
1393
1394 public static AsciiString cached(String string) {
1395 AsciiString asciiString = new AsciiString(string);
1396 asciiString.string = string;
1397 return asciiString;
1398 }
1399
1400
1401
1402
1403
1404
1405 public static int hashCode(CharSequence value) {
1406 if (value == null) {
1407 return 0;
1408 }
1409 if (value instanceof AsciiString) {
1410 return value.hashCode();
1411 }
1412
1413 return PlatformDependent.hashCodeAscii(value);
1414 }
1415
1416
1417
1418
1419 public static boolean contains(CharSequence a, CharSequence b) {
1420 return contains(a, b, DefaultCharEqualityComparator.INSTANCE);
1421 }
1422
1423
1424
1425
1426 public static boolean containsIgnoreCase(CharSequence a, CharSequence b) {
1427 return contains(a, b, AsciiCaseInsensitiveCharEqualityComparator.INSTANCE);
1428 }
1429
1430
1431
1432
1433
1434 public static boolean contentEqualsIgnoreCase(CharSequence a, CharSequence b) {
1435 if (a == null || b == null) {
1436 return a == b;
1437 }
1438
1439 if (a instanceof AsciiString) {
1440 return ((AsciiString) a).contentEqualsIgnoreCase(b);
1441 }
1442 if (b instanceof AsciiString) {
1443 return ((AsciiString) b).contentEqualsIgnoreCase(a);
1444 }
1445
1446 if (a.length() != b.length()) {
1447 return false;
1448 }
1449 for (int i = 0; i < a.length(); ++i) {
1450 if (!equalsIgnoreCase(a.charAt(i), b.charAt(i))) {
1451 return false;
1452 }
1453 }
1454 return true;
1455 }
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466 public static boolean containsContentEqualsIgnoreCase(Collection<CharSequence> collection, CharSequence value) {
1467 for (CharSequence v : collection) {
1468 if (contentEqualsIgnoreCase(value, v)) {
1469 return true;
1470 }
1471 }
1472 return false;
1473 }
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484 public static boolean containsAllContentEqualsIgnoreCase(Collection<CharSequence> a, Collection<CharSequence> b) {
1485 for (CharSequence v : b) {
1486 if (!containsContentEqualsIgnoreCase(a, v)) {
1487 return false;
1488 }
1489 }
1490 return true;
1491 }
1492
1493
1494
1495
1496 public static boolean contentEquals(CharSequence a, CharSequence b) {
1497 if (a == null || b == null) {
1498 return a == b;
1499 }
1500
1501 if (a instanceof AsciiString) {
1502 return ((AsciiString) a).contentEquals(b);
1503 }
1504
1505 if (b instanceof AsciiString) {
1506 return ((AsciiString) b).contentEquals(a);
1507 }
1508
1509 if (a.length() != b.length()) {
1510 return false;
1511 }
1512 for (int i = 0; i < a.length(); ++i) {
1513 if (a.charAt(i) != b.charAt(i)) {
1514 return false;
1515 }
1516 }
1517 return true;
1518 }
1519
1520 private static AsciiString[] toAsciiStringArray(String[] jdkResult) {
1521 AsciiString[] res = new AsciiString[jdkResult.length];
1522 for (int i = 0; i < jdkResult.length; i++) {
1523 res[i] = new AsciiString(jdkResult[i]);
1524 }
1525 return res;
1526 }
1527
1528 private interface CharEqualityComparator {
1529 boolean equals(char a, char b);
1530 }
1531
1532 private static final class DefaultCharEqualityComparator implements CharEqualityComparator {
1533 static final DefaultCharEqualityComparator INSTANCE = new DefaultCharEqualityComparator();
1534 private DefaultCharEqualityComparator() { }
1535
1536 @Override
1537 public boolean equals(char a, char b) {
1538 return a == b;
1539 }
1540 }
1541
1542 private static final class AsciiCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1543 static final AsciiCaseInsensitiveCharEqualityComparator
1544 INSTANCE = new AsciiCaseInsensitiveCharEqualityComparator();
1545 private AsciiCaseInsensitiveCharEqualityComparator() { }
1546
1547 @Override
1548 public boolean equals(char a, char b) {
1549 return equalsIgnoreCase(a, b);
1550 }
1551 }
1552
1553 private static final class GeneralCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1554 static final GeneralCaseInsensitiveCharEqualityComparator
1555 INSTANCE = new GeneralCaseInsensitiveCharEqualityComparator();
1556 private GeneralCaseInsensitiveCharEqualityComparator() { }
1557
1558 @Override
1559 public boolean equals(char a, char b) {
1560
1561 return Character.toUpperCase(a) == Character.toUpperCase(b) ||
1562 Character.toLowerCase(a) == Character.toLowerCase(b);
1563 }
1564 }
1565
1566 private static boolean contains(CharSequence a, CharSequence b, CharEqualityComparator cmp) {
1567 if (a == null || b == null || a.length() < b.length()) {
1568 return false;
1569 }
1570 if (b.length() == 0) {
1571 return true;
1572 }
1573 int bStart = 0;
1574 for (int i = 0; i < a.length(); ++i) {
1575 if (cmp.equals(b.charAt(bStart), a.charAt(i))) {
1576
1577 if (++bStart == b.length()) {
1578 return true;
1579 }
1580 } else if (a.length() - i < b.length()) {
1581
1582 return false;
1583 } else {
1584 bStart = 0;
1585 }
1586 }
1587 return false;
1588 }
1589
1590 private static boolean regionMatchesCharSequences(final CharSequence cs, final int csStart,
1591 final CharSequence string, final int start, final int length,
1592 CharEqualityComparator charEqualityComparator) {
1593
1594 if (csStart < 0 || length > cs.length() - csStart) {
1595 return false;
1596 }
1597 if (start < 0 || length > string.length() - start) {
1598 return false;
1599 }
1600
1601 int csIndex = csStart;
1602 int csEnd = csIndex + length;
1603 int stringIndex = start;
1604
1605 while (csIndex < csEnd) {
1606 char c1 = cs.charAt(csIndex++);
1607 char c2 = string.charAt(stringIndex++);
1608
1609 if (!charEqualityComparator.equals(c1, c2)) {
1610 return false;
1611 }
1612 }
1613 return true;
1614 }
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626 public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int csStart,
1627 final CharSequence string, final int start, final int length) {
1628 if (cs == null || string == null) {
1629 return false;
1630 }
1631
1632 if (cs instanceof String && string instanceof String) {
1633 return ((String) cs).regionMatches(ignoreCase, csStart, (String) string, start, length);
1634 }
1635
1636 if (cs instanceof AsciiString) {
1637 return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1638 }
1639
1640 return regionMatchesCharSequences(cs, csStart, string, start, length,
1641 ignoreCase ? GeneralCaseInsensitiveCharEqualityComparator.INSTANCE :
1642 DefaultCharEqualityComparator.INSTANCE);
1643 }
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655 public static boolean regionMatchesAscii(final CharSequence cs, final boolean ignoreCase, final int csStart,
1656 final CharSequence string, final int start, final int length) {
1657 if (cs == null || string == null) {
1658 return false;
1659 }
1660
1661 if (!ignoreCase && cs instanceof String && string instanceof String) {
1662
1663
1664
1665 return ((String) cs).regionMatches(false, csStart, (String) string, start, length);
1666 }
1667
1668 if (cs instanceof AsciiString) {
1669 return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1670 }
1671
1672 return regionMatchesCharSequences(cs, csStart, string, start, length,
1673 ignoreCase ? AsciiCaseInsensitiveCharEqualityComparator.INSTANCE :
1674 DefaultCharEqualityComparator.INSTANCE);
1675 }
1676
1677
1678
1679
1680
1681
1682
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 public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) {
1708 if (str == null || searchStr == null) {
1709 return INDEX_NOT_FOUND;
1710 }
1711 if (startPos < 0) {
1712 startPos = 0;
1713 }
1714 int searchStrLen = searchStr.length();
1715 final int endLimit = str.length() - searchStrLen + 1;
1716 if (startPos > endLimit) {
1717 return INDEX_NOT_FOUND;
1718 }
1719 if (searchStrLen == 0) {
1720 return startPos;
1721 }
1722 for (int i = startPos; i < endLimit; i++) {
1723 if (regionMatches(str, true, i, searchStr, 0, searchStrLen)) {
1724 return i;
1725 }
1726 }
1727 return INDEX_NOT_FOUND;
1728 }
1729
1730
1731
1732
1733
1734
1735
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 public static int indexOfIgnoreCaseAscii(final CharSequence str, final CharSequence searchStr, int startPos) {
1761 if (str == null || searchStr == null) {
1762 return INDEX_NOT_FOUND;
1763 }
1764 if (startPos < 0) {
1765 startPos = 0;
1766 }
1767 int searchStrLen = searchStr.length();
1768 final int endLimit = str.length() - searchStrLen + 1;
1769 if (startPos > endLimit) {
1770 return INDEX_NOT_FOUND;
1771 }
1772 if (searchStrLen == 0) {
1773 return startPos;
1774 }
1775 for (int i = startPos; i < endLimit; i++) {
1776 if (regionMatchesAscii(str, true, i, searchStr, 0, searchStrLen)) {
1777 return i;
1778 }
1779 }
1780 return INDEX_NOT_FOUND;
1781 }
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794 public static int indexOf(final CharSequence cs, final char searchChar, int start) {
1795 if (cs instanceof String) {
1796 return ((String) cs).indexOf(searchChar, start);
1797 } else if (cs instanceof AsciiString) {
1798 return ((AsciiString) cs).indexOf(searchChar, start);
1799 }
1800 if (cs == null) {
1801 return INDEX_NOT_FOUND;
1802 }
1803 final int sz = cs.length();
1804 for (int i = start < 0 ? 0 : start; i < sz; i++) {
1805 if (cs.charAt(i) == searchChar) {
1806 return i;
1807 }
1808 }
1809 return INDEX_NOT_FOUND;
1810 }
1811
1812 private static boolean equalsIgnoreCase(byte a, byte b) {
1813 return a == b || AsciiStringUtil.toLowerCase(a) == AsciiStringUtil.toLowerCase(b);
1814 }
1815
1816 private static boolean equalsIgnoreCase(char a, char b) {
1817 return a == b || toLowerCase(a) == toLowerCase(b);
1818 }
1819
1820
1821
1822
1823
1824
1825
1826 public static char toLowerCase(char c) {
1827 return isUpperCase(c) ? (char) (c + 32) : c;
1828 }
1829
1830 private static byte toUpperCase(byte b) {
1831 return AsciiStringUtil.toUpperCase(b);
1832 }
1833
1834 public static boolean isUpperCase(byte value) {
1835 return AsciiStringUtil.isUpperCase(value);
1836 }
1837
1838 public static boolean isUpperCase(char value) {
1839 return value >= 'A' && value <= 'Z';
1840 }
1841
1842 public static byte c2b(char c) {
1843 return (byte) ((c > MAX_CHAR_VALUE) ? '?' : c);
1844 }
1845
1846 private static byte c2b0(char c) {
1847 return (byte) c;
1848 }
1849
1850 public static char b2c(byte b) {
1851 return (char) (b & 0xFF);
1852 }
1853 }