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 final int thatEnd = start + length;
837 for (int i = start, j = thisStart + arrayOffset(); i < thatEnd; i++, j++) {
838 if (b2c(value[j]) != string.charAt(i)) {
839 return false;
840 }
841 }
842 return true;
843 }
844
845
846
847
848
849
850
851
852
853
854
855
856
857 public boolean regionMatches(boolean ignoreCase, int thisStart, CharSequence string, int start, int length) {
858 if (!ignoreCase) {
859 return regionMatches(thisStart, string, start, length);
860 }
861
862 ObjectUtil.checkNotNull(string, "string");
863
864 final int thisLen = length();
865 if (thisStart < 0 || length > thisLen - thisStart) {
866 return false;
867 }
868 if (start < 0 || length > string.length() - start) {
869 return false;
870 }
871
872 thisStart += arrayOffset();
873 final int thisEnd = thisStart + length;
874 while (thisStart < thisEnd) {
875 if (!equalsIgnoreCase(b2c(value[thisStart++]), string.charAt(start++))) {
876 return false;
877 }
878 }
879 return true;
880 }
881
882
883
884
885
886
887
888
889 public AsciiString replace(char oldChar, char newChar) {
890 if (oldChar > MAX_CHAR_VALUE) {
891 return this;
892 }
893
894 final byte oldCharAsByte = c2b0(oldChar);
895 final byte newCharAsByte = c2b(newChar);
896 final int len = offset + length;
897 for (int i = offset; i < len; ++i) {
898 if (value[i] == oldCharAsByte) {
899 byte[] buffer = PlatformDependent.allocateUninitializedArray(length());
900 System.arraycopy(value, offset, buffer, 0, i - offset);
901 buffer[i - offset] = newCharAsByte;
902 ++i;
903 for (; i < len; ++i) {
904 byte oldValue = value[i];
905 buffer[i - offset] = oldValue != oldCharAsByte ? oldValue : newCharAsByte;
906 }
907 return new AsciiString(buffer, false);
908 }
909 }
910 return this;
911 }
912
913
914
915
916
917
918
919
920 public boolean startsWith(CharSequence prefix) {
921 return startsWith(prefix, 0);
922 }
923
924
925
926
927
928
929
930
931
932
933
934 public boolean startsWith(CharSequence prefix, int start) {
935 return regionMatches(start, prefix, 0, prefix.length());
936 }
937
938
939
940
941
942
943 public AsciiString toLowerCase() {
944 return AsciiStringUtil.toLowerCase(this);
945 }
946
947
948
949
950
951
952 public AsciiString toUpperCase() {
953 return AsciiStringUtil.toUpperCase(this);
954 }
955
956
957
958
959
960
961
962
963 public static CharSequence trim(CharSequence c) {
964 if (c instanceof AsciiString) {
965 return ((AsciiString) c).trim();
966 }
967 if (c instanceof String) {
968 return ((String) c).trim();
969 }
970 int start = 0, last = c.length() - 1;
971 int end = last;
972 while (start <= end && c.charAt(start) <= ' ') {
973 start++;
974 }
975 while (end >= start && c.charAt(end) <= ' ') {
976 end--;
977 }
978 if (start == 0 && end == last) {
979 return c;
980 }
981 return c.subSequence(start, end);
982 }
983
984
985
986
987
988
989
990 public AsciiString trim() {
991 int start = arrayOffset(), last = arrayOffset() + length() - 1;
992 int end = last;
993 while (start <= end && value[start] <= ' ') {
994 start++;
995 }
996 while (end >= start && value[end] <= ' ') {
997 end--;
998 }
999 if (start == 0 && end == last) {
1000 return this;
1001 }
1002 return new AsciiString(value, start, end - start + 1, false);
1003 }
1004
1005
1006
1007
1008
1009
1010
1011 public boolean contentEquals(CharSequence a) {
1012 if (this == a) {
1013 return true;
1014 }
1015
1016 if (a == null || a.length() != length()) {
1017 return false;
1018 }
1019 if (a instanceof AsciiString) {
1020 return equals(a);
1021 }
1022
1023 for (int i = arrayOffset(), j = 0; j < a.length(); ++i, ++j) {
1024 if (b2c(value[i]) != a.charAt(j)) {
1025 return false;
1026 }
1027 }
1028 return true;
1029 }
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039 public boolean matches(String expr) {
1040 return Pattern.matches(expr, this);
1041 }
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054 public AsciiString[] split(String expr, int max) {
1055 return toAsciiStringArray(Pattern.compile(expr).split(this, max));
1056 }
1057
1058
1059
1060
1061 public AsciiString[] split(char delim) {
1062 final List<AsciiString> res = InternalThreadLocalMap.get().arrayList();
1063
1064 int start = 0;
1065 final int length = length();
1066 for (int i = start; i < length; i++) {
1067 if (charAt(i) == delim) {
1068 if (start == i) {
1069 res.add(EMPTY_STRING);
1070 } else {
1071 res.add(new AsciiString(value, start + arrayOffset(), i - start, false));
1072 }
1073 start = i + 1;
1074 }
1075 }
1076
1077 if (start == 0) {
1078 res.add(this);
1079 } else {
1080 if (start != length) {
1081
1082 res.add(new AsciiString(value, start + arrayOffset(), length - start, false));
1083 } else {
1084
1085 for (int i = res.size() - 1; i >= 0; i--) {
1086 if (res.get(i).isEmpty()) {
1087 res.remove(i);
1088 } else {
1089 break;
1090 }
1091 }
1092 }
1093 }
1094
1095 return res.toArray(EmptyArrays.EMPTY_ASCII_STRINGS);
1096 }
1097
1098
1099
1100
1101
1102
1103 @Override
1104 public int hashCode() {
1105 int h = hash;
1106 if (h == 0) {
1107 h = PlatformDependent.hashCodeAscii(value, offset, length);
1108 hash = h;
1109 }
1110 return h;
1111 }
1112
1113 @Override
1114 public boolean equals(Object obj) {
1115 if (obj == null || obj.getClass() != AsciiString.class) {
1116 return false;
1117 }
1118 if (this == obj) {
1119 return true;
1120 }
1121
1122 AsciiString other = (AsciiString) obj;
1123 return length() == other.length() &&
1124 hashCode() == other.hashCode() &&
1125 PlatformDependent.equals(array(), arrayOffset(), other.array(), other.arrayOffset(), length());
1126 }
1127
1128
1129
1130
1131
1132 @Override
1133 public String toString() {
1134 String cache = string;
1135 if (cache == null) {
1136 cache = toString(0);
1137 string = cache;
1138 }
1139 return cache;
1140 }
1141
1142
1143
1144
1145
1146 public String toString(int start) {
1147 return toString(start, length());
1148 }
1149
1150
1151
1152
1153 public String toString(int start, int end) {
1154 int length = end - start;
1155 if (length == 0) {
1156 return "";
1157 }
1158
1159 if (isOutOfBounds(start, length, length())) {
1160 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
1161 + length + ") <= srcLen(" + length() + ')');
1162 }
1163
1164 @SuppressWarnings("deprecation")
1165 final String str = new String(value, 0, start + offset, length);
1166 return str;
1167 }
1168
1169 public boolean parseBoolean() {
1170 return length >= 1 && value[offset] != 0;
1171 }
1172
1173 public char parseChar() {
1174 return parseChar(0);
1175 }
1176
1177 public char parseChar(int start) {
1178 if (start + 1 >= length()) {
1179 throw new IndexOutOfBoundsException("2 bytes required to convert to character. index " +
1180 start + " would go out of bounds.");
1181 }
1182 final int startWithOffset = start + offset;
1183 return (char) ((b2c(value[startWithOffset]) << 8) | b2c(value[startWithOffset + 1]));
1184 }
1185
1186 public short parseShort() {
1187 return parseShort(0, length(), 10);
1188 }
1189
1190 public short parseShort(int radix) {
1191 return parseShort(0, length(), radix);
1192 }
1193
1194 public short parseShort(int start, int end) {
1195 return parseShort(start, end, 10);
1196 }
1197
1198 public short parseShort(int start, int end, int radix) {
1199 int intValue = parseInt(start, end, radix);
1200 short result = (short) intValue;
1201 if (result != intValue) {
1202 throw new NumberFormatException(subSequence(start, end, false).toString());
1203 }
1204 return result;
1205 }
1206
1207 public int parseInt() {
1208 return parseInt(0, length(), 10);
1209 }
1210
1211 public int parseInt(int radix) {
1212 return parseInt(0, length(), radix);
1213 }
1214
1215 public int parseInt(int start, int end) {
1216 return parseInt(start, end, 10);
1217 }
1218
1219 public int parseInt(int start, int end, int radix) {
1220 if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1221 throw new NumberFormatException();
1222 }
1223
1224 if (start == end) {
1225 throw new NumberFormatException();
1226 }
1227
1228 int i = start;
1229 boolean negative = byteAt(i) == '-';
1230 if (negative && ++i == end) {
1231 throw new NumberFormatException(subSequence(start, end, false).toString());
1232 }
1233
1234 return parseInt(i, end, radix, negative);
1235 }
1236
1237 private int parseInt(int start, int end, int radix, boolean negative) {
1238 int max = Integer.MIN_VALUE / radix;
1239 int result = 0;
1240 int currOffset = start;
1241 while (currOffset < end) {
1242 int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
1243 if (digit == -1) {
1244 throw new NumberFormatException(subSequence(start, end, false).toString());
1245 }
1246 if (max > result) {
1247 throw new NumberFormatException(subSequence(start, end, false).toString());
1248 }
1249 int next = result * radix - digit;
1250 if (next > result) {
1251 throw new NumberFormatException(subSequence(start, end, false).toString());
1252 }
1253 result = next;
1254 }
1255 if (!negative) {
1256 result = -result;
1257 if (result < 0) {
1258 throw new NumberFormatException(subSequence(start, end, false).toString());
1259 }
1260 }
1261 return result;
1262 }
1263
1264 public long parseLong() {
1265 return parseLong(0, length(), 10);
1266 }
1267
1268 public long parseLong(int radix) {
1269 return parseLong(0, length(), radix);
1270 }
1271
1272 public long parseLong(int start, int end) {
1273 return parseLong(start, end, 10);
1274 }
1275
1276 public long parseLong(int start, int end, int radix) {
1277 if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
1278 throw new NumberFormatException();
1279 }
1280
1281 if (start == end) {
1282 throw new NumberFormatException();
1283 }
1284
1285 int i = start;
1286 boolean negative = byteAt(i) == '-';
1287 if (negative && ++i == end) {
1288 throw new NumberFormatException(subSequence(start, end, false).toString());
1289 }
1290
1291 return parseLong(i, end, radix, negative);
1292 }
1293
1294 private long parseLong(int start, int end, int radix, boolean negative) {
1295 long max = Long.MIN_VALUE / radix;
1296 long result = 0;
1297 int currOffset = start;
1298 while (currOffset < end) {
1299 int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
1300 if (digit == -1) {
1301 throw new NumberFormatException(subSequence(start, end, false).toString());
1302 }
1303 if (max > result) {
1304 throw new NumberFormatException(subSequence(start, end, false).toString());
1305 }
1306 long next = result * radix - digit;
1307 if (next > result) {
1308 throw new NumberFormatException(subSequence(start, end, false).toString());
1309 }
1310 result = next;
1311 }
1312 if (!negative) {
1313 result = -result;
1314 if (result < 0) {
1315 throw new NumberFormatException(subSequence(start, end, false).toString());
1316 }
1317 }
1318 return result;
1319 }
1320
1321 public float parseFloat() {
1322 return parseFloat(0, length());
1323 }
1324
1325 public float parseFloat(int start, int end) {
1326 return Float.parseFloat(toString(start, end));
1327 }
1328
1329 public double parseDouble() {
1330 return parseDouble(0, length());
1331 }
1332
1333 public double parseDouble(int start, int end) {
1334 return Double.parseDouble(toString(start, end));
1335 }
1336
1337 public static final HashingStrategy<CharSequence> CASE_INSENSITIVE_HASHER =
1338 new HashingStrategy<CharSequence>() {
1339 @Override
1340 public int hashCode(CharSequence o) {
1341 return AsciiString.hashCode(o);
1342 }
1343
1344 @Override
1345 public boolean equals(CharSequence a, CharSequence b) {
1346 return AsciiString.contentEqualsIgnoreCase(a, b);
1347 }
1348 };
1349
1350 public static final HashingStrategy<CharSequence> CASE_SENSITIVE_HASHER =
1351 new HashingStrategy<CharSequence>() {
1352 @Override
1353 public int hashCode(CharSequence o) {
1354 return AsciiString.hashCode(o);
1355 }
1356
1357 @Override
1358 public boolean equals(CharSequence a, CharSequence b) {
1359 return AsciiString.contentEquals(a, b);
1360 }
1361 };
1362
1363
1364
1365
1366
1367 public static AsciiString of(CharSequence string) {
1368 return string instanceof AsciiString ? (AsciiString) string : new AsciiString(string);
1369 }
1370
1371
1372
1373
1374
1375
1376
1377 public static AsciiString cached(String string) {
1378 AsciiString asciiString = new AsciiString(string);
1379 asciiString.string = string;
1380 return asciiString;
1381 }
1382
1383
1384
1385
1386
1387
1388 public static int hashCode(CharSequence value) {
1389 if (value == null) {
1390 return 0;
1391 }
1392 if (value instanceof AsciiString) {
1393 return value.hashCode();
1394 }
1395
1396 return PlatformDependent.hashCodeAscii(value);
1397 }
1398
1399
1400
1401
1402 public static boolean contains(CharSequence a, CharSequence b) {
1403 return contains(a, b, DefaultCharEqualityComparator.INSTANCE);
1404 }
1405
1406
1407
1408
1409 public static boolean containsIgnoreCase(CharSequence a, CharSequence b) {
1410 return contains(a, b, AsciiCaseInsensitiveCharEqualityComparator.INSTANCE);
1411 }
1412
1413
1414
1415
1416
1417 public static boolean contentEqualsIgnoreCase(CharSequence a, CharSequence b) {
1418 if (a == null || b == null) {
1419 return a == b;
1420 }
1421
1422 if (a instanceof AsciiString) {
1423 return ((AsciiString) a).contentEqualsIgnoreCase(b);
1424 }
1425 if (b instanceof AsciiString) {
1426 return ((AsciiString) b).contentEqualsIgnoreCase(a);
1427 }
1428
1429 if (a.length() != b.length()) {
1430 return false;
1431 }
1432 for (int i = 0; i < a.length(); ++i) {
1433 if (!equalsIgnoreCase(a.charAt(i), b.charAt(i))) {
1434 return false;
1435 }
1436 }
1437 return true;
1438 }
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449 public static boolean containsContentEqualsIgnoreCase(Collection<CharSequence> collection, CharSequence value) {
1450 for (CharSequence v : collection) {
1451 if (contentEqualsIgnoreCase(value, v)) {
1452 return true;
1453 }
1454 }
1455 return false;
1456 }
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467 public static boolean containsAllContentEqualsIgnoreCase(Collection<CharSequence> a, Collection<CharSequence> b) {
1468 for (CharSequence v : b) {
1469 if (!containsContentEqualsIgnoreCase(a, v)) {
1470 return false;
1471 }
1472 }
1473 return true;
1474 }
1475
1476
1477
1478
1479 public static boolean contentEquals(CharSequence a, CharSequence b) {
1480 if (a == null || b == null) {
1481 return a == b;
1482 }
1483
1484 if (a instanceof AsciiString) {
1485 return ((AsciiString) a).contentEquals(b);
1486 }
1487
1488 if (b instanceof AsciiString) {
1489 return ((AsciiString) b).contentEquals(a);
1490 }
1491
1492 if (a.length() != b.length()) {
1493 return false;
1494 }
1495 for (int i = 0; i < a.length(); ++i) {
1496 if (a.charAt(i) != b.charAt(i)) {
1497 return false;
1498 }
1499 }
1500 return true;
1501 }
1502
1503 private static AsciiString[] toAsciiStringArray(String[] jdkResult) {
1504 AsciiString[] res = new AsciiString[jdkResult.length];
1505 for (int i = 0; i < jdkResult.length; i++) {
1506 res[i] = new AsciiString(jdkResult[i]);
1507 }
1508 return res;
1509 }
1510
1511 private interface CharEqualityComparator {
1512 boolean equals(char a, char b);
1513 }
1514
1515 private static final class DefaultCharEqualityComparator implements CharEqualityComparator {
1516 static final DefaultCharEqualityComparator INSTANCE = new DefaultCharEqualityComparator();
1517 private DefaultCharEqualityComparator() { }
1518
1519 @Override
1520 public boolean equals(char a, char b) {
1521 return a == b;
1522 }
1523 }
1524
1525 private static final class AsciiCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1526 static final AsciiCaseInsensitiveCharEqualityComparator
1527 INSTANCE = new AsciiCaseInsensitiveCharEqualityComparator();
1528 private AsciiCaseInsensitiveCharEqualityComparator() { }
1529
1530 @Override
1531 public boolean equals(char a, char b) {
1532 return equalsIgnoreCase(a, b);
1533 }
1534 }
1535
1536 private static final class GeneralCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1537 static final GeneralCaseInsensitiveCharEqualityComparator
1538 INSTANCE = new GeneralCaseInsensitiveCharEqualityComparator();
1539 private GeneralCaseInsensitiveCharEqualityComparator() { }
1540
1541 @Override
1542 public boolean equals(char a, char b) {
1543
1544 return Character.toUpperCase(a) == Character.toUpperCase(b) ||
1545 Character.toLowerCase(a) == Character.toLowerCase(b);
1546 }
1547 }
1548
1549 private static boolean contains(CharSequence a, CharSequence b, CharEqualityComparator cmp) {
1550 if (a == null || b == null || a.length() < b.length()) {
1551 return false;
1552 }
1553 if (b.length() == 0) {
1554 return true;
1555 }
1556 int bStart = 0;
1557 for (int i = 0; i < a.length(); ++i) {
1558 if (cmp.equals(b.charAt(bStart), a.charAt(i))) {
1559
1560 if (++bStart == b.length()) {
1561 return true;
1562 }
1563 } else if (a.length() - i < b.length()) {
1564
1565 return false;
1566 } else {
1567 bStart = 0;
1568 }
1569 }
1570 return false;
1571 }
1572
1573 private static boolean regionMatchesCharSequences(final CharSequence cs, final int csStart,
1574 final CharSequence string, final int start, final int length,
1575 CharEqualityComparator charEqualityComparator) {
1576
1577 if (csStart < 0 || length > cs.length() - csStart) {
1578 return false;
1579 }
1580 if (start < 0 || length > string.length() - start) {
1581 return false;
1582 }
1583
1584 int csIndex = csStart;
1585 int csEnd = csIndex + length;
1586 int stringIndex = start;
1587
1588 while (csIndex < csEnd) {
1589 char c1 = cs.charAt(csIndex++);
1590 char c2 = string.charAt(stringIndex++);
1591
1592 if (!charEqualityComparator.equals(c1, c2)) {
1593 return false;
1594 }
1595 }
1596 return true;
1597 }
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609 public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int csStart,
1610 final CharSequence string, final int start, final int length) {
1611 if (cs == null || string == null) {
1612 return false;
1613 }
1614
1615 if (cs instanceof String && string instanceof String) {
1616 return ((String) cs).regionMatches(ignoreCase, csStart, (String) string, start, length);
1617 }
1618
1619 if (cs instanceof AsciiString) {
1620 return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1621 }
1622
1623 return regionMatchesCharSequences(cs, csStart, string, start, length,
1624 ignoreCase ? GeneralCaseInsensitiveCharEqualityComparator.INSTANCE :
1625 DefaultCharEqualityComparator.INSTANCE);
1626 }
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638 public static boolean regionMatchesAscii(final CharSequence cs, final boolean ignoreCase, final int csStart,
1639 final CharSequence string, final int start, final int length) {
1640 if (cs == null || string == null) {
1641 return false;
1642 }
1643
1644 if (!ignoreCase && cs instanceof String && string instanceof String) {
1645
1646
1647
1648 return ((String) cs).regionMatches(false, csStart, (String) string, start, length);
1649 }
1650
1651 if (cs instanceof AsciiString) {
1652 return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1653 }
1654
1655 return regionMatchesCharSequences(cs, csStart, string, start, length,
1656 ignoreCase ? AsciiCaseInsensitiveCharEqualityComparator.INSTANCE :
1657 DefaultCharEqualityComparator.INSTANCE);
1658 }
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690 public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) {
1691 if (str == null || searchStr == null) {
1692 return INDEX_NOT_FOUND;
1693 }
1694 if (startPos < 0) {
1695 startPos = 0;
1696 }
1697 int searchStrLen = searchStr.length();
1698 final int endLimit = str.length() - searchStrLen + 1;
1699 if (startPos > endLimit) {
1700 return INDEX_NOT_FOUND;
1701 }
1702 if (searchStrLen == 0) {
1703 return startPos;
1704 }
1705 for (int i = startPos; i < endLimit; i++) {
1706 if (regionMatches(str, true, i, searchStr, 0, searchStrLen)) {
1707 return i;
1708 }
1709 }
1710 return INDEX_NOT_FOUND;
1711 }
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743 public static int indexOfIgnoreCaseAscii(final CharSequence str, final CharSequence searchStr, int startPos) {
1744 if (str == null || searchStr == null) {
1745 return INDEX_NOT_FOUND;
1746 }
1747 if (startPos < 0) {
1748 startPos = 0;
1749 }
1750 int searchStrLen = searchStr.length();
1751 final int endLimit = str.length() - searchStrLen + 1;
1752 if (startPos > endLimit) {
1753 return INDEX_NOT_FOUND;
1754 }
1755 if (searchStrLen == 0) {
1756 return startPos;
1757 }
1758 for (int i = startPos; i < endLimit; i++) {
1759 if (regionMatchesAscii(str, true, i, searchStr, 0, searchStrLen)) {
1760 return i;
1761 }
1762 }
1763 return INDEX_NOT_FOUND;
1764 }
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777 public static int indexOf(final CharSequence cs, final char searchChar, int start) {
1778 if (cs instanceof String) {
1779 return ((String) cs).indexOf(searchChar, start);
1780 } else if (cs instanceof AsciiString) {
1781 return ((AsciiString) cs).indexOf(searchChar, start);
1782 }
1783 if (cs == null) {
1784 return INDEX_NOT_FOUND;
1785 }
1786 final int sz = cs.length();
1787 for (int i = start < 0 ? 0 : start; i < sz; i++) {
1788 if (cs.charAt(i) == searchChar) {
1789 return i;
1790 }
1791 }
1792 return INDEX_NOT_FOUND;
1793 }
1794
1795 private static boolean equalsIgnoreCase(byte a, byte b) {
1796 return a == b || AsciiStringUtil.toLowerCase(a) == AsciiStringUtil.toLowerCase(b);
1797 }
1798
1799 private static boolean equalsIgnoreCase(char a, char b) {
1800 return a == b || toLowerCase(a) == toLowerCase(b);
1801 }
1802
1803
1804
1805
1806
1807
1808
1809 public static char toLowerCase(char c) {
1810 return isUpperCase(c) ? (char) (c + 32) : c;
1811 }
1812
1813 private static byte toUpperCase(byte b) {
1814 return AsciiStringUtil.toUpperCase(b);
1815 }
1816
1817 public static boolean isUpperCase(byte value) {
1818 return AsciiStringUtil.isUpperCase(value);
1819 }
1820
1821 public static boolean isUpperCase(char value) {
1822 return value >= 'A' && value <= 'Z';
1823 }
1824
1825 public static byte c2b(char c) {
1826 return (byte) ((c > MAX_CHAR_VALUE) ? '?' : c);
1827 }
1828
1829 private static byte c2b0(char c) {
1830 return (byte) c;
1831 }
1832
1833 public static char b2c(byte b) {
1834 return (char) (b & 0xFF);
1835 }
1836 }