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
1395
1396
1397
1398 public static AsciiString cached(String string) {
1399 byte[] value = PlatformDependent.allocateUninitializedArray(string.length());
1400 boolean allLatin1 = true;
1401 for (int i = 0; i < value.length; i++) {
1402 char c = string.charAt(i);
1403 value[i] = c2b(c);
1404 if (c > MAX_CHAR_VALUE) {
1405 allLatin1 = false;
1406 }
1407 }
1408
1409 AsciiString asciiString = new AsciiString(value, false);
1410 asciiString.string = allLatin1 ? string : asciiString.toString(0);
1411 return asciiString;
1412 }
1413
1414
1415
1416
1417
1418
1419 public static int hashCode(CharSequence value) {
1420 if (value == null) {
1421 return 0;
1422 }
1423 if (value instanceof AsciiString) {
1424 return value.hashCode();
1425 }
1426
1427 return PlatformDependent.hashCodeAscii(value);
1428 }
1429
1430
1431
1432
1433 public static boolean contains(CharSequence a, CharSequence b) {
1434 return contains(a, b, DefaultCharEqualityComparator.INSTANCE);
1435 }
1436
1437
1438
1439
1440 public static boolean containsIgnoreCase(CharSequence a, CharSequence b) {
1441 return contains(a, b, AsciiCaseInsensitiveCharEqualityComparator.INSTANCE);
1442 }
1443
1444
1445
1446
1447
1448 public static boolean contentEqualsIgnoreCase(CharSequence a, CharSequence b) {
1449 if (a == null || b == null) {
1450 return a == b;
1451 }
1452
1453 if (a instanceof AsciiString) {
1454 return ((AsciiString) a).contentEqualsIgnoreCase(b);
1455 }
1456 if (b instanceof AsciiString) {
1457 return ((AsciiString) b).contentEqualsIgnoreCase(a);
1458 }
1459
1460 if (a.length() != b.length()) {
1461 return false;
1462 }
1463 for (int i = 0; i < a.length(); ++i) {
1464 if (!equalsIgnoreCase(a.charAt(i), b.charAt(i))) {
1465 return false;
1466 }
1467 }
1468 return true;
1469 }
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480 public static boolean containsContentEqualsIgnoreCase(Collection<CharSequence> collection, CharSequence value) {
1481 for (CharSequence v : collection) {
1482 if (contentEqualsIgnoreCase(value, v)) {
1483 return true;
1484 }
1485 }
1486 return false;
1487 }
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498 public static boolean containsAllContentEqualsIgnoreCase(Collection<CharSequence> a, Collection<CharSequence> b) {
1499 for (CharSequence v : b) {
1500 if (!containsContentEqualsIgnoreCase(a, v)) {
1501 return false;
1502 }
1503 }
1504 return true;
1505 }
1506
1507
1508
1509
1510 public static boolean contentEquals(CharSequence a, CharSequence b) {
1511 if (a == null || b == null) {
1512 return a == b;
1513 }
1514
1515 if (a instanceof AsciiString) {
1516 return ((AsciiString) a).contentEquals(b);
1517 }
1518
1519 if (b instanceof AsciiString) {
1520 return ((AsciiString) b).contentEquals(a);
1521 }
1522
1523 if (a.length() != b.length()) {
1524 return false;
1525 }
1526 for (int i = 0; i < a.length(); ++i) {
1527 if (a.charAt(i) != b.charAt(i)) {
1528 return false;
1529 }
1530 }
1531 return true;
1532 }
1533
1534 private static AsciiString[] toAsciiStringArray(String[] jdkResult) {
1535 AsciiString[] res = new AsciiString[jdkResult.length];
1536 for (int i = 0; i < jdkResult.length; i++) {
1537 res[i] = new AsciiString(jdkResult[i]);
1538 }
1539 return res;
1540 }
1541
1542 private interface CharEqualityComparator {
1543 boolean equals(char a, char b);
1544 }
1545
1546 private static final class DefaultCharEqualityComparator implements CharEqualityComparator {
1547 static final DefaultCharEqualityComparator INSTANCE = new DefaultCharEqualityComparator();
1548 private DefaultCharEqualityComparator() { }
1549
1550 @Override
1551 public boolean equals(char a, char b) {
1552 return a == b;
1553 }
1554 }
1555
1556 private static final class AsciiCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1557 static final AsciiCaseInsensitiveCharEqualityComparator
1558 INSTANCE = new AsciiCaseInsensitiveCharEqualityComparator();
1559 private AsciiCaseInsensitiveCharEqualityComparator() { }
1560
1561 @Override
1562 public boolean equals(char a, char b) {
1563 return equalsIgnoreCase(a, b);
1564 }
1565 }
1566
1567 private static final class GeneralCaseInsensitiveCharEqualityComparator implements CharEqualityComparator {
1568 static final GeneralCaseInsensitiveCharEqualityComparator
1569 INSTANCE = new GeneralCaseInsensitiveCharEqualityComparator();
1570 private GeneralCaseInsensitiveCharEqualityComparator() { }
1571
1572 @Override
1573 public boolean equals(char a, char b) {
1574
1575 return Character.toUpperCase(a) == Character.toUpperCase(b) ||
1576 Character.toLowerCase(a) == Character.toLowerCase(b);
1577 }
1578 }
1579
1580 private static boolean contains(CharSequence a, CharSequence b, CharEqualityComparator cmp) {
1581 if (a == null || b == null || a.length() < b.length()) {
1582 return false;
1583 }
1584 if (b.length() == 0) {
1585 return true;
1586 }
1587 int bStart = 0;
1588 for (int i = 0; i < a.length(); ++i) {
1589 if (cmp.equals(b.charAt(bStart), a.charAt(i))) {
1590
1591 if (++bStart == b.length()) {
1592 return true;
1593 }
1594 } else if (a.length() - i < b.length()) {
1595
1596 return false;
1597 } else {
1598 bStart = 0;
1599 }
1600 }
1601 return false;
1602 }
1603
1604 private static boolean regionMatchesCharSequences(final CharSequence cs, final int csStart,
1605 final CharSequence string, final int start, final int length,
1606 CharEqualityComparator charEqualityComparator) {
1607
1608 if (csStart < 0 || length > cs.length() - csStart) {
1609 return false;
1610 }
1611 if (start < 0 || length > string.length() - start) {
1612 return false;
1613 }
1614
1615 int csIndex = csStart;
1616 int csEnd = csIndex + length;
1617 int stringIndex = start;
1618
1619 while (csIndex < csEnd) {
1620 char c1 = cs.charAt(csIndex++);
1621 char c2 = string.charAt(stringIndex++);
1622
1623 if (!charEqualityComparator.equals(c1, c2)) {
1624 return false;
1625 }
1626 }
1627 return true;
1628 }
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640 public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int csStart,
1641 final CharSequence string, final int start, final int length) {
1642 if (cs == null || string == null) {
1643 return false;
1644 }
1645
1646 if (cs instanceof String && string instanceof String) {
1647 return ((String) cs).regionMatches(ignoreCase, csStart, (String) string, start, length);
1648 }
1649
1650 if (cs instanceof AsciiString) {
1651 return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1652 }
1653
1654 return regionMatchesCharSequences(cs, csStart, string, start, length,
1655 ignoreCase ? GeneralCaseInsensitiveCharEqualityComparator.INSTANCE :
1656 DefaultCharEqualityComparator.INSTANCE);
1657 }
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669 public static boolean regionMatchesAscii(final CharSequence cs, final boolean ignoreCase, final int csStart,
1670 final CharSequence string, final int start, final int length) {
1671 if (cs == null || string == null) {
1672 return false;
1673 }
1674
1675 if (!ignoreCase && cs instanceof String && string instanceof String) {
1676
1677
1678
1679 return ((String) cs).regionMatches(false, csStart, (String) string, start, length);
1680 }
1681
1682 if (cs instanceof AsciiString) {
1683 return ((AsciiString) cs).regionMatches(ignoreCase, csStart, string, start, length);
1684 }
1685
1686 return regionMatchesCharSequences(cs, csStart, string, start, length,
1687 ignoreCase ? AsciiCaseInsensitiveCharEqualityComparator.INSTANCE :
1688 DefaultCharEqualityComparator.INSTANCE);
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
1716
1717
1718
1719
1720
1721 public static int indexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) {
1722 if (str == null || searchStr == null) {
1723 return INDEX_NOT_FOUND;
1724 }
1725 if (startPos < 0) {
1726 startPos = 0;
1727 }
1728 int searchStrLen = searchStr.length();
1729 final int endLimit = str.length() - searchStrLen + 1;
1730 if (startPos > endLimit) {
1731 return INDEX_NOT_FOUND;
1732 }
1733 if (searchStrLen == 0) {
1734 return startPos;
1735 }
1736 for (int i = startPos; i < endLimit; i++) {
1737 if (regionMatches(str, true, i, searchStr, 0, searchStrLen)) {
1738 return i;
1739 }
1740 }
1741 return INDEX_NOT_FOUND;
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
1769
1770
1771
1772
1773
1774 public static int indexOfIgnoreCaseAscii(final CharSequence str, final CharSequence searchStr, int startPos) {
1775 if (str == null || searchStr == null) {
1776 return INDEX_NOT_FOUND;
1777 }
1778 if (startPos < 0) {
1779 startPos = 0;
1780 }
1781 int searchStrLen = searchStr.length();
1782 final int endLimit = str.length() - searchStrLen + 1;
1783 if (startPos > endLimit) {
1784 return INDEX_NOT_FOUND;
1785 }
1786 if (searchStrLen == 0) {
1787 return startPos;
1788 }
1789 for (int i = startPos; i < endLimit; i++) {
1790 if (regionMatchesAscii(str, true, i, searchStr, 0, searchStrLen)) {
1791 return i;
1792 }
1793 }
1794 return INDEX_NOT_FOUND;
1795 }
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808 public static int indexOf(final CharSequence cs, final char searchChar, int start) {
1809 if (cs instanceof String) {
1810 return ((String) cs).indexOf(searchChar, start);
1811 } else if (cs instanceof AsciiString) {
1812 return ((AsciiString) cs).indexOf(searchChar, start);
1813 }
1814 if (cs == null) {
1815 return INDEX_NOT_FOUND;
1816 }
1817 final int sz = cs.length();
1818 for (int i = start < 0 ? 0 : start; i < sz; i++) {
1819 if (cs.charAt(i) == searchChar) {
1820 return i;
1821 }
1822 }
1823 return INDEX_NOT_FOUND;
1824 }
1825
1826 private static boolean equalsIgnoreCase(byte a, byte b) {
1827 return a == b || AsciiStringUtil.toLowerCase(a) == AsciiStringUtil.toLowerCase(b);
1828 }
1829
1830 private static boolean equalsIgnoreCase(char a, char b) {
1831 return a == b || toLowerCase(a) == toLowerCase(b);
1832 }
1833
1834
1835
1836
1837
1838
1839
1840 public static char toLowerCase(char c) {
1841 return isUpperCase(c) ? (char) (c + 32) : c;
1842 }
1843
1844 private static byte toUpperCase(byte b) {
1845 return AsciiStringUtil.toUpperCase(b);
1846 }
1847
1848 public static boolean isUpperCase(byte value) {
1849 return AsciiStringUtil.isUpperCase(value);
1850 }
1851
1852 public static boolean isUpperCase(char value) {
1853 return value >= 'A' && value <= 'Z';
1854 }
1855
1856 public static byte c2b(char c) {
1857 return (byte) ((c > MAX_CHAR_VALUE) ? '?' : c);
1858 }
1859
1860 private static byte c2b0(char c) {
1861 return (byte) c;
1862 }
1863
1864 public static char b2c(byte b) {
1865 return (char) (b & 0xFF);
1866 }
1867 }