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