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